Reputation: 9191
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
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