前言
上一篇学习了security的Filter初始化顺序,现在来看一下登录验证的流程
正文
首先从上一部分了解到Filter中有一个
UsernamePasswordAuthenticationFilter
这个过滤器主要是进行用户验证,流程大致如下:
登录验证流程.png
UsernamePasswordAuthenticationFilter继承了AbstractAuthenticationProcessingFilter类,所以请求会先进入父类的doFilter方法
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if (!requiresAuthentication(request, response)) {
chain.doFilter(request, response);
return;
}
Authentication authResult;
try {
//调用子类的方法进行登录验证
authResult = attemptAuthentication(request, response);
if (authResult == null) {
// return immediately as subclass has indicated that it hasn't completed
// authentication
return;
}
sessionStrategy.onAuthentication(authResult, request, response);
}
....
// Authentication success
if (continueChainBeforeSuccessfulAuthentication) {
chain.doFilter(request, response);
}
successfulAuthentication(request, response, chain, authResult);
}
而attemptAuthentication方法中主要是调用调用AuthenticationManager的authenticate去验证,于是进入其子类ProviderManager中,
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
Class<? extends Authentication> toTest = authentication.getClass();
AuthenticationException lastException = null;
Authentication result = null;
boolean debug = logger.isDebugEnabled();
for (AuthenticationProvider provider : getProviders()) {
if (!provider.supports(toTest)) {
continue;
}
try {
result = provider.authenticate(authentication);
if (result != null) {
copyDetails(authentication, result);
break;
}
}
.....
}
if (result == null && parent != null) {
// Allow the parent to try.
try {
result = parent.authenticate(authentication);
}
catch (ProviderNotFoundException e) {
}
catch (AuthenticationException e) {
lastException = e;
}
}
.....
}
其中循环调用AuthenticationProvider去进行权限验证,下面看一下抽象类AbstractUserDetailsAuthenticationProvider的authenticate方法
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
// Determine username
String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED"
: authentication.getName();
boolean cacheWasUsed = true;
UserDetails user = this.userCache.getUserFromCache(username);
if (user == null) {
cacheWasUsed = false;
try {
//获取用户相关信息
user = retrieveUser(username,
(UsernamePasswordAuthenticationToken) authentication);
}
.....
}
try {
preAuthenticationChecks.check(user);
//进行验证
additionalAuthenticationChecks(user,
(UsernamePasswordAuthenticationToken) authentication);
}
.....
return createSuccessAuthentication(principalToReturn, authentication, user);
}
protected final UserDetails retrieveUser(String username,
UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
prepareTimingAttackProtection();
try {
//根据配置的不同验证方式找到相应UserDetailService的loadUserByUsername方法获取用户
UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
if (loadedUser == null) {
throw new InternalAuthenticationServiceException(
"UserDetailsService returned null, which is an interface contract violation");
}
return loadedUser;
}
......
}
所以在第一部分中说了实现登录验证自定义配置时一定要实现UserDetailService的loadUserByUsername方法
事实上内存和JDBC验证方式也是实现了loadUserByUsername方法
总结
本次梳理下登录验证的流程,其实也挺简单的,下面开始梳理下权限验证流程
网友评论