• 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。

  • 相关阅读:
    操作系统识别
    Float/Double内存结构分析,取值范围、有效位数与内存结构的关系详解(有效位数和取值范围如何得出?)
    数据结构HW1
    leetcode_1726 同积元组
    使用Go+Lua解决Redis秒杀中库存与超卖问题
    加解密随笔
    算法通关村——不简单的字符串转换问题
    冰冰学习笔记:Linux下的权限理解
    ARM PWN
    中远麒麟堡垒机tokensSQL注入漏洞
  • 原文地址:https://blog.csdn.net/qq_53318060/article/details/126801693