spring security验证流程:
- spring security的登录验证是由UsernamePasswordAuthenticationFilter这个过滤器完成,在该类的父类AbstractAuthenticationProcessingFilter(585行)中有一个AuthenticationManager接口属性,验证工作主要是通过这个AuthenticationManager接口的实例来完成的。默认情况下,spring security会把ProviderManager类的实例注入到该属性。而自定义AbstractAuthenticationProcessingFilter的情况下,一般会在配置文件:,起别名,然后注入该自定义类的某个属性中,看上面那个配置文件就知道了
- UsernamePasswordAuthenticationFilter的验证过程如下:
- 首先过滤器会调用自身的attemptAuthentication方法(646行),该方法规定请求必须为post,并从request中取出authentication,authentication是在SecurityContextPersistenceFilter过滤器中通过捕获用户提交的登录表单中的内容生成的一个Authentication接口实例。
- attemptAuthentication方法拿到authentication对象后,在方法中又调用ProviderManager类的authenticate方法并传入authentication对象(由于只能验证用户名、密码的简单信息,所以经常重写以实现复杂的用户验证,并返回包含当前用户所有信息的authentication),验证用户是否能登录,并处理定义登录成败重定向的页面
- 也就是说attemptAuthentication方法会调用类中的List providers集合中各个AuthenticationProvider接口实现类中的authenticate(Authentication authentication)进行验证
- provider的实现类在验证用户时,会调用UserDetailsService的实现类的loadUserByUsername方法获取用户信息。
- 由此可见,真正的验证逻辑是由各个AuthenticationProvider接口实现类来完成的。DaoAuthenticationProvider类是默认情况下会被注入AuthenticationProvider的接口实现类。
源码分析
AbstractAuthenticationProcessingFilter
public abstract class AbstractAuthenticationProcessingFilter extends GenericFilterBean implements ApplicationEventPublisherAware, MessageSourceAware { // 过滤器doFilter方法 public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; /* * 1、判断当前filter是否可以处理当前请求,若不行,则交给下一个filter去处理。 */ if (!requiresAuthentication(request, response)) { chain.doFilter(request, response); return; } if (logger.isDebugEnabled()) { logger.debug("Request is to process authentication"); } Authentication authResult; /* * 2、调用子类(UsernamePasswordAuthenticationFilter)attemptAuthentication方法 */ try { authResult = attemptAuthentication(request, response); if (authResult == null) { // return immediately as subclass has indicated that it hasn't completed // authentication return; } /* * 3、最终认证成功后,会处理一些与session相关的方法 * (比如将认证信息存到session等操作)。 */ sessionStrategy.onAuthentication(authResult, request, response); } /* * 3、认证失败后的一些处理。 */ catch (InternalAuthenticationServiceException failed) { logger.error( "An internal error occurred while trying to authenticate the user.", failed); unsuccessfulAuthentication(request, response, failed); return; } catch (AuthenticationException failed) { unsuccessfulAuthentication(request, response, failed); return; } /* * 4、认证成功,继续下个请求 */ if (continueChainBeforeSuccessfulAuthentication) { chain.doFilter(request, response); } /* * 5、最终认证成功后的相关回调方法,主要将当前的认证信息放到SecurityContextHolder中 * 并调用认证成功处理器AuthenticationSuccessHandler接口实现类的 * onAuthenticationSuccess()。 */ successfulAuthentication(request, response, chain, authResult); } }
UsernamePasswordAuthenticaionFilter,以上AbstractAuthenticationProcessingFilter的子类,主要是重写attemptAuthentication(),规定请求必须为post。
public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter { //实现父类的方法 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(); /* * 封装进Authentication实现类UsernamePasswordAuthenticationToken * public UsernamePasswordAuthenticationToken(Object principal, Object credentials) { super((Collection)null); //设置权限为null this.principal = principal; this.credentials = credentials; this.setAuthenticated(false); } */ UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken( username, password); //一般来说principle是User对象较常见 setDetails(request, authRequest); //Allow subclasses to set the "details" property //调用AuthenticationManager管理下的AuthenticationProvider接口实现类的authenticate() return this.getAuthenticationManager().authenticate(authRequest); } }
- AuthenticationManager:AuthenticationManager仅仅是一个接口,默认实现类是ProviderManager,ProviderManager不是自己直接对请求进行验证,而是将其委派给一个AuthenticationProvider列表。例如我们要同时使用jdbc认证和ldap认证,那么就可以用这个列表。列表中的每一个AuthenticationProvider将会被一次查询是否需要通过器进行验证。每个provider的验证结果只有两种情况:抛出ProviderNotFoundException异常或者完全填充一个Authentication对象,并存储在SecurityContext中。
ProviderManager
public Authentication authenticate(A