CodeMed
CodeMed

Reputation: 9191

authentication failing in spring security, why?

A Spring MVC app with spring security has three views including the main public site at /, the secure site only for logged-in users at /secure-home, and the login page at /login. Spring security is successfully redirecting all users to the /login page when they try to load the /secure-home url. But the problem is that users who enter valid credentials into the custom login page are not being allowed to view the /secure-home url. How can I change the code so that users who login with valid credentials get redirected to the /secure-home url?

Note that the login page does not show an error message when a user submits bad credentials, even though the jsp code below should print an error on failed authentication.

Here are relevant aspects of the code:

SecurityConfig.java is:

@Configuration
@EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .formLogin()
                .loginPage("/login")
                .defaultSuccessUrl("/secure-home")
                .usernameParameter("j_username")
                .passwordParameter("j_password")
                .loginProcessingUrl("/j_spring_security_check")
                .failureUrl("/login")
                .permitAll()
                .and()
            .logout()
                .logoutUrl("/logout")
                .logoutSuccessUrl("/login")
                .and()
            .authorizeRequests()
                .antMatchers("/secure-home").hasAuthority("registered")
                .antMatchers("/j_spring_security_check").permitAll()
                .and()
            .userDetailsService(userDetailsService());
    }
}  

MessageWebApplicationInitializer.java is:

@Order(2)
public class MessageSecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
}

business-config.xml contains:

<!-- lots of other stuff -->
<bean class="my.app.config.SecurityConfig"></bean>

mvc-core-config.java contains:

<!--  lots of other stuff -->
<mvc:view-controller path="/" view-name="welcome" />
<mvc:view-controller path="/login" view-name="login" />

User.java is:

@Entity
@Table(name="users")
public class User implements UserDetails{

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue
    private Integer id;

    @Column(name= "email", unique=true, nullable=false)
    private String login;//must be a valid email address

    @Column(name = "password")
    private String password;

    @Column(name = "phone")
    private String phone;

    @Column(name = "pin")
    private String pin;

    @Column(name = "sessionid")
    private String sessionId;

    @ManyToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
    @JoinTable(name="user_roles",
        joinColumns = {@JoinColumn(name="user_id", referencedColumnName="id")},
        inverseJoinColumns = {@JoinColumn(name="role_id", referencedColumnName="id")}
    )
    private Set<Role> roles;

    public Integer getId() {return id;}
    public void setId(Integer id) { this.id = id;}

    public String getPhone(){return phone;}
    public void setPhone(String pn){phone = pn;}

    public String getPin(){return pin;}
    public void setPin(String pi){pin = pi;}

    public String getSessionId(){return sessionId;}
    public void setSessionId(String sd){sessionId = sd;}

    public String getLogin() {
        return login;
    }

    public void setLogin(String login) {
        this.login = login;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    //roles methods
    public void addRole(Role alg) {roles.add(alg);}
    public Set<Role> getRoles(){
        if(this.roles==null){this.roles = new HashSet<Role>();}
        return this.roles;
    }
    public void setRoles(Set<Role> alg){this.roles = alg;}
    public boolean isInRoles(int aid){
        ArrayList<Role> mylgs = new ArrayList<Role>();
        mylgs.addAll(this.roles);
        for(int a=0;a<mylgs.size();a++){if(mylgs.get(a).getId()==aid){return true;}}
        return false;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public String getUsername() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public boolean isAccountNonExpired() {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean isAccountNonLocked() {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean isEnabled() {
        // TODO Auto-generated method stub
        return false;
    }

}

Role.java is:

@Entity
@Table(name="roles")
public class Role implements GrantedAuthority{

    @Id
    @GeneratedValue
    private Integer id;

    private String role;

    @ManyToMany(cascade = CascadeType.ALL, mappedBy = "roles")
    private Set<User> userRoles;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getRole() {
        return role;
    }

    public void setRole(String role) {
        this.role = role;
    }

    public Set<User> getUserRoles() {
        return userRoles;
    }

    public void setUserRoles(Set<User> userRoles) {
        this.userRoles = userRoles;
    }

    @Override
    public String getAuthority() {
        // TODO Auto-generated method stub
        return null;
    }

}

The DML to populate the database is:

SET FOREIGN_KEY_CHECKS=0;
INSERT INTO `roles` VALUES (100,'registered');
INSERT INTO `user_roles` VALUES (100,100);
INSERT INTO `users` VALUES (100,'[email protected]','password','phonenumber','pin','sessionid');
SET FOREIGN_KEY_CHECKS=1;

And login.jsp is:

<?xml version="1.0" encoding="ISO-8859-1" ?>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
<title>Custom Login page</title>
<style>.error {color: red;}
</style>

</head>
<body>
<div class="container">

<h1>Custom Login page</h1>
<p>
<c:if test="${error == true}">
    <b class="error">Invalid login or password.</b>
</c:if>
</p>

<form method="post" action="<c:url value='j_spring_security_check'/>" >
<table>
<tbody>
    <tr>
        <td>Login:</td>
        <td><input type="text" name="j_username" id="j_username"size="30" maxlength="40"  /></td>
    </tr>
    <tr>
        <td>Password:</td>
        <td><input type="password" name="j_password" id="j_password" size="30" maxlength="32" /></td>
    </tr>
    <tr>
    <td colspan=2>
          <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
    </td>
    </tr>
    <tr>
        <td></td>
        <td><input type="submit" value="Login" /></td>
    </tr>
</tbody>
</table>
</form> 
</div>
</body>
</html>

There is also code for a service layer, a repository layer, and a controller to manage the /secure-home url pattern.

Upvotes: 0

Views: 3236

Answers (1)

KSTN
KSTN

Reputation: 2022

This /secure-home is only for user who has registered authority.

.antMatchers("/secure-home").hasAuthority("registered")

while your user details User.java always returns null whenever spring security check the authorities of authenticated user.

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
    // TODO Auto-generated method stub
    return null;
}

try this

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
    List<GrantedAuthority> authorities = new ArrayList<>();
    for(Role role : roles){
        authorities.add(new SimpleGrantedAuthority(role.getRole()))
    }
    return authorities;
}

Upvotes: 1

Related Questions