I have an application that uses Spring Security to control access to pages, to manage user roles (GrantedAuthority) and for ACL. The application uses the standard UsernamePasswordAuthenticationFilter that intercepts requests to /j_spring_security_check (with j_username and j_password request parameters), and using a ProviderManager it authenticates the user and on success stores it in the SecurityContextHolder.
The above is configured in the security context, using a customized UserDetailsService:
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref='myUserDetailsService'/>
</authentication-manager>
The above approach in my case is not optimal, for the following reasons:
- Adding a captcha requires extra filters
- In order to customize the login logic, I need to replace the
AuthenticationProvideras well - showing errors in the login form is complex, since I cannot use Spring MVC’s forms
My idea is to remove the interceptor based login and put all the logic inside a Spring 3 MVC controller. The pseudo-code is as following:
RequestMapping(value="/login/", method = RequestMethod.POST)
public String attemptLogin(HttpServletRequest request, HttpServletResponse response,
@ModelAttribute("login") LoginCmd login, Model model) {
// validate command (username, password, captcha)
// ...
// load user from DB
User user = userService.loadUserByUsername(login.getUsername());
// extra logic (check number of failed logins + other stuff)
// ...
// In case everything is fine, create a spring security User
/* Instead of creating the user, read it from DB */
org.springframework.security.core.userdetails.User authUser =
new org.springframework.security.core.userdetails.User(
login.getUsername() /*username*/,
login.getPassword() /*password*/,
true /*enabled*/,
true /*accountNonExpired */,
true /*credentialsNonExpired */,
true /*accountNonLocked*/,
new ArrayList<GrantedAuthority>() /*authorities*/
);
// build the AuthenticationToken
UsernamePasswordAuthenticationToken authResult =
new UsernamePasswordAuthenticationToken(authUser, login.getPassword(),
authUser.getAuthorities());
// use WebAuthenticationDetailsSource do build details
authResult.setDetails(detailsSource.buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authResult);
return SUCCESS_VIEW;
}
Do you see any problem with the solution here above? Is setting the authentication inside the SecurityContextHolder enough? Am I missing something?
Comments and suggestions are welcome 😉
Thanks a lot to everyone
Andrea
I went through the Spring Security code, and on successful authentication also the original code just stores the
Authenticationobject in the SecurityContextHolder, nothing else is done.For example, in class
AbstractAuthenticationProcessingFilter(which is used by the standard login intercepting requests to/j_spring_security_check) does that:I implemented this on my application and everything works fine.