Spring-security源码-Filter之UsernamePasswordAuthenticationFilter(十四)
1.Spring-Security系列导航2.spring-security使用-登录(一)3.spring-security使用-自定义数据源(二)4.spring-security使用-更友好的方式扩展登录AuthenticationProvider(三)5.spring-security使用-获得当前用户信息(四)6.spring-security使用-同一个账号只允许登录一次(五)7.spring-security使用-session共享(六)8.spring-security使用-安全防护HttpFirewall(七)9.spring-security使用-权限控制(八)10.spring-security源码-初始化(九)11.spring-security源码-如何初始化SecurityFilterChain到Servlet12.spring-security源码-FilterChainProxy13.spring-security源码-Filter之WebAsyncManagerIntegrationFilter(十)14.Spring-security源码-Filter之SecurityContextPersistenceFilter(十一)15.Spring-security源码-Filter之HeaderWriterFilter(十二)16.Spring-security源码-Filter之LogoutFilter(十三)
17.Spring-security源码-Filter之UsernamePasswordAuthenticationFilter(十四)
18.Spring-security源码-Filter之ConcurrentSessionFilter(十五)19.Spring-security源码-Filter之SessionManagementFilter(十六)20.Spring-security源码-Filter之RememberMeAuthenticationFilter(十七)21.Spring-security源码-Filter之ExceptionTranslationFilter(十八)22.Spring-security源码-Filter之FilterSecurityInterceptor(十九)23.Spring-security源码-注解权限原理(二十)24.Spring-security源码-注解权限原理之MethodSecurityInterceptor(二十一)25.Spring-Security基于源码扩展-一套系统多套登录逻辑(二十二)26.Spring-Security基于源码扩展-自定义登录(二十三)27.Spring-Security基于源码扩展-自定义认证失败返回(二十四)28.Spring-Security基于源码扩展-自定义授权注解(二十五)最常用的一中过滤器,用于登录认证
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; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
2020-11-10 spring源码阅读(二)-IOC之ClassPathXmlApplicationContext