最常用的一中过滤器,用于登录认证
http.formLogin() 初始化
类图
AbstractAuthenticationProcessingFilter负责 认证成功和认证失败的调度 提供抽象方法attemptAuthentication 具体的认证逻辑由子类实现
<1>
org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter#doFilter
private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { //判断是否是需要处理的请求 默认是/login if (!requiresAuthentication(request, response)) { chain.doFilter(request, response); return; } try { //<2>子类实现 认证 同时返回认证成功用户信息 Authentication authenticationResult = attemptAuthentication(request, response); if (authenticationResult == null) { // return immediately as subclass has indicated that it hasn't completed return; } //登录成功的一些操作 比如sesion管理 超过最大登录限制 剔除其他session this.sessionStrategy.onAuthentication(authenticationResult, request, response); // 登录成功执行后续调用链 if (this.continueChainBeforeSuccessfulAuthentication) { chain.doFilter(request, response); } //登录成功处理逻辑 比如是否包含记住我的标识 如果有则设置cookie successfulAuthentication(request, response, chain, authenticationResult); } catch (InternalAuthenticationServiceException failed) { this.logger.error("An internal error occurred while trying to authenticate the user.", failed); unsuccessfulAuthentication(request, response, failed); } catch (AuthenticationException ex) { // 处理登录失败 unsuccessfulAuthentication(request, response, ex); } }
<2>
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#attemptAuthentication
@Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { //是否只支持post请求 if (this.postOnly && !request.getMethod().equals("POST")) { throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod()); } //获取用户输入登录名 String username = obtainUsername(request); username = (username != null) ? username : ""; username = username.trim(); //获取用户输入登录密码 String password = obtainPassword(request); password = (password != null) ? password : ""; //用名字和密码封装成UsernamePasswordAuthenticationToken UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password); // 将request也封装到UsernamePasswordAuthenticationToken setDetails(request, authRequest); //交给AuthenticationManager处理,初始化处:<3> 本质是ProviceManger authenticae则看<4> return this.getAuthenticationManager().authenticate(authRequest); }
@Nullable protected String obtainUsername(HttpServletRequest request) { //从request 获取用户输入登录名 可以定制默认是username return request.getParameter(this.usernameParameter); } @Nullable protected String obtainPassword(HttpServletRequest request) { //从request 获取用户输入用户输入的密码 可以定制默认是password return request.getParameter(this.passwordParameter); }
<3>
可参考:https://www.cnblogs.com/LQBlog/p/15508248.html#autoid-9-0-0
<4>
我们可以自定义provider实现定制化登录逻辑如:
@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { /** * inMemoryAuthentication 开启在内存中定义用户 * 多个用户通过and隔开 */ auth.authenticationProvider({}).inMemoryAuthentication() .withUser("liqiang").password("liqiang").roles("admin") .and() .withUser("admin").password("admin").roles("admin"); }
org.springframework.security.authentication.ProviderManager#authenticate
@Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { Class<? extends Authentication> toTest = authentication.getClass(); AuthenticationException lastException = null; AuthenticationException parentException = null; Authentication result = null; Authentication parentResult = null; int currentPosition = 0; int size = this.providers.size(); //获取Providers for (AuthenticationProvider provider : getProviders()) { //根据Authentication 判断是否能被处理 if (!provider.supports(toTest)) { continue; } if (logger.isTraceEnabled()) { logger.trace(LogMessage.format("Authenticating request with %s (%d/%d)", provider.getClass().getSimpleName(), ++currentPosition, size)); } try { //进行认真 result = provider.authenticate(authentication); if (result != null) { copyDetails(authentication, result); break; } } catch (AccountStatusException | InternalAuthenticationServiceException ex) { //发布spring异常事件 prepareException(ex, authentication); throw ex; } catch (AuthenticationException ex) { lastException = ex; } } if (result == null && this.parent != null) { try { //根据parent再校验一次 parentResult = this.parent.authenticate(authentication); result = parentResult; } catch (ProviderNotFoundException ex) { // 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 ex) { parentException = ex; lastException = ex; } } if (result != null) { if (this.eraseCredentialsAfterAuthentication && (result instanceof CredentialsContainer)) { // Authentication is complete. Remove credentials and other secret data // from authentication ((CredentialsContainer) result).eraseCredentials(); } if (parentResult == null) { this.eventPublisher.publishAuthenticationSuccess(result); } return result; } if (lastException == null) { lastException = new ProviderNotFoundException(this.messages.getMessage("ProviderManager.providerNotFound", new Object[] { toTest.getName() }, "No AuthenticationProvider found for {0}")); } if (parentException == null) { prepareException(lastException, authentication); } throw lastException; }