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_ch开发者_如何学JAVAeck
(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
AuthenticationProvider
as 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 Authentication
object 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:
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
Authentication authResult) throws IOException, ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Authentication success. Updating SecurityContextHolder to contain: " + authResult);
}
SecurityContextHolder.getContext().setAuthentication(authResult);
rememberMeServices.loginSuccess(request, response, authResult);
// Fire event
if (this.eventPublisher != null) {
eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
}
successHandler.onAuthenticationSuccess(request, response, authResult);
}
I implemented this on my application and everything works fine.
精彩评论