原文链接:http://dead-knight.iteye.com/blog/1513149 好好
参考链接2:http://blog.csdn.net/yecong111/article/details/17015199
如果用户没有自定义登录的url使用默认的url,路径是url:j_spring_security_check
过滤器UsernamePasswordAuthenticationFilter过滤到这个url。(实际上这个Filter类的doFilter是父类AbstractAuthenticationProcessingFilter的)。
AbstractAuthenticationProcessingFilter调用UsernamePasswordAuthenticationFilter中的attemptAuthentication方法,将表单请求的信息(用户、密码等信息)赋值给UsernamePasswordAuthenticationToken(authRequest),然后调用AbstractAuthenticationProcessingFilter中的AuthenticationManager类中的(默认是ProviderManager类)方法——getAuthenticationManager().authenticate(authRequest)对用户密码的正确性进行验证,认证失败就抛出异常,成功就返回Authentication对象。
UsernamePasswordAuthenticationFilter代码
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
//只处理post提交的请求
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
//获取用户名、密码数据
String username = obtainUsername(request);
String password = obtainPassword(request);
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
username = username.trim();
//构造未认证的UsernamePasswordAuthenticationToken
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
// Place the last username attempted into HttpSession for views
HttpSession session = request.getSession(false);
//如果session不为空,添加username到session中
if (session != null || getAllowSessionCreation()) {
request.getSession().setAttribute(SPRING_SECURITY_LAST_USERNAME_KEY, TextEscapeUtils.escapeEntities(username));
}
// Allow subclasses to set the "details" property
//设置details,这里就是设置org.springframework.security.web.
//authentication.WebAuthenticationDetails实例到details中
setDetails(request, authRequest);
//通过AuthenticationManager:ProviderManager完成认证任务
return this.getAuthenticationManager().authenticate(authRequest);
}
this.getAuthenticationManager().authenticate(authRequest)是指ProviderManager类中authenticate方法,ProviderManager类中authenticate方法里校验用户名密码的核心是providers。
ProviderManager代码
public Authentication doAuthentication(Authentication authentication) throws AuthenticationException {
Class<? extends Authentication> toTest = authentication.getClass();
AuthenticationException lastException = null;
Authentication result = null;
//循环ProviderManager中的providers,由具体的provider执行认证操作
for (AuthenticationProvider provider : getProviders()) {
System.out.println("AuthenticationProvider: " + provider.getClass().getName());
if (!provider.supports(toTest)) {
continue;
}
logger.debug("Authentication attempt using " + provider.getClass().getName());
try {
result = provider.authenticate(authentication);
if (result != null) {
//复制details
copyDetails(authentication, result);
break;
}
} catch (AccountStatusException e) {
// SEC-546: Avoid polling additional providers if auth failure is due to invalid account status
eventPublisher.publishAuthenticationFailure(e, authentication);
throw e;
} catch (AuthenticationException e) {
lastException = e;
}
}
if (result == null && parent != null) {
// Allow the parent to try.
try {
result = parent.authenticate(authentication);
} catch (ProviderNotFoundException e) {
// ignore as we will throw below if no other exception occurred prior to calling parent and the parent
// may throw ProviderNotFound even though a provider in the child already handled the request
} catch (AuthenticationException e) {
lastException = e;
}
}
if (result != null) {
eventPublisher.publishAuthenticationSuccess(result);
return result;
}
// Parent was null, or didn't authenticate (or throw an exception).
if (lastException == null) {
lastException = new ProviderNotFoundException(messages.getMessage("ProviderManager.providerNotFound",
new Object[] {toTest.getName()}, "No AuthenticationProvider found for {0}"));
}
//由注入进来的org.springframework.security.authentication.DefaultAuthenticationEventPublisher完成事件发布任务
eventPublisher.publishAuthenticationFailure(lastException, authentication);
throw lastException;
}
ProviderManager仅仅是管理provider的,具体的authenticate认证任务由各自provider来完成。 providers中的provider分别为:
org.springframework.security.authentication.dao.DaoAuthenticationProvider
org.springframework.security.authentication.AnonymousAuthenticationProvider
其他的provider根据特殊情况,再添加到providers中的,如remember me功能的provider
org.springframework.security.authentication.RememberMeAuthenticationProvider 。
AuthenticationProvider接口里有authenticate方法,实现类有多个:其中有AbstractUserDetailsAuthenticationProvider,AbstractUserDetailsAuthenticationProvider的子类DaoAuthenticationProvider(上文提到的providers中的provider) 中有retrieveUser方法,retrieveUser方法中有最重要的一句代码:
loadedUser = this.getUserDetailsService().loadUserByUsername(username);
这行代码中调用了UserDetailsService,我们通过实现UserDetailsService重写loadUserByUsername方法,就可以用查询数据库的方式从数据库表中获取用户名密码,而不是在配置文件中写死用户名密码。