• UsernamePasswordAuthenticationFilter执行流程


    SpringSecurity是在拦截器Interceptor前面的。
    SpringSecurity走完,如果项目中设置了拦截器,还会走拦截器的。

            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-securityartifactId>
            dependency>
    
    • 1
    • 2
    • 3
    • 4

    添加依赖后,启动项目,访问任意接口都会跳转到登录页
    在这里插入图片描述
    输入账号,密码,程序会先执行到UsernamePasswordAuthenticationFilter的attemptAuthentication方法

    // UsernamePasswordAuthenticationFilter.class
        public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response){
            if (this.postOnly && !request.getMethod().equals("POST")) {
                throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
            } else {
                String username = this.obtainUsername(request);		// 从HttpServletRequest中拿到传来的的用户名参数
                username = username != null ? username : "";
                username = username.trim();
                String password = this.obtainPassword(request);		// 从HttpServletRequest中拿到传来的的密码参数
                password = password != null ? pasword : "";
                // 封装 Authentication
                UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
                this.setDetails(request, authRequest);
                return this.getAuthenticationManager().authenticate(authRequest);	// 试图认证
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    进入到ProviderManager这个类当中的authenticate方法。
    之后调用DaoAuthenticationProvider继承的其父类public abstract class AbstractUserDetailsAuthenticationProvider的authenticate方法。
    之后进入到DaoAuthenticationProvider这个类中retrieveUser方法。

        protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) {
            this.prepareTimingAttackProtection();
    
            try {	// 根据用户名在系统中查询用户信息
                UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
                if (loadedUser == null) {
                    throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation");
                } else {
                    return loadedUser;
                }
            } catch (Exception var6) {
                throw new InternalAuthenticationServiceException(var6.getMessage(), var6);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    之后又进入到public class InMemoryUserDetailsManager implements UserDetailsManager, UserDetailsPasswordService这个类中的loadUserByUsername方法

        public UserDetails loadUserByUsername(String username){
        // 根据用户名在系统中查询用户信息
        // InMemory是默认内存中存着相应的用户信息
            UserDetails user = (UserDetails)this.users.get(username.toLowerCase());
            if (user == null) {
                throw new UsernameNotFoundException(username);
            } else {
    			// 封装返回
                return new User(user.getUsername(), user.getPassword(), user.isEnabled(), 
                user.isAccountNonExpired(), user.isCredentialsNonExpired(), 
                user.isAccountNonLocked(), user.getAuthorities());
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    查询到的话,封装为UserDetails,又层层返回至AbstractUserDetailsAuthenticationProvider类中的authenticate方法。
    之后又进入到DaoAuthenticationProvider类中的additionalAuthenticationChecks方法。

    // userDetails是在系统中查到的用户信息, authentication是登录时填写的信息进行封装
        protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication){
            if (authentication.getCredentials() == null) {
                this.logger.debug("Failed to authenticate since no credentials provided");
                throw new BadCredentialsException(this.messages
                .getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
            } else {
            // 比对密码
                String presentedPassword = authentication.getCredentials().toString();
                if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
                    this.logger.debug("Failed to authenticate since password does not match stored value");
                    throw new BadCredentialsException(this.messages
                    .getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
                }
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    比对成功之后,又返回到AbstractUserDetailsAuthenticationProvider类中的authenticate方法,调用DaoAuthenticationProvider类中的createSuccessAuthentication方法
    将从系统中查到的UserDetails信息(改名为principal),以及authentication(用户登陆时填的信息),以及一个MutableUserDetails(将从系统中查到的UserDetails信息,将其中的密码进行Bcrpt加密,封装为MutableUserDetails对象)这三者进行整合,最后封装信息,返回一个UsernamePasswordAuthenticationToken。

  • 相关阅读:
    10分钟读懂数据响应式和双向绑定原理
    laravel框架 - 事件与监听器
    scrapy框架-Middleware(爬虫中间件)
    spring cloud 官方文档如何阅读
    C# 委托学习1
    Python--练习:报数字(数7)
    代码随想录算法训练营 day59|503.下一个更大元素II、42. 接雨水
    1 评价类算法:层次分析法笔记(附Python代码)
    [数据挖掘、数据分析] clickhouse在go语言里的实践
    ES6 --》JS运算符及数组扩展方法
  • 原文地址:https://blog.csdn.net/qq_53318060/article/details/126801693