springsecurity认证源码跟踪

springsecurity认证源码跟踪

时序图(出自大神之手,借用一下,备忘一下)

在这里插入图片描述

源码跟踪

0.发送登陆请求
1.org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter过滤器拦截,进入该类的doFilter方法
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)req;
        HttpServletResponse response = (HttpServletResponse)res;
        if (!this.requiresAuthentication(request, response)) {
            chain.doFilter(request, response);
        } else {
            Authentication authResult;
            try {
                //开始认证
                authResult = this.attemptAuthentication(request, response);
                if (authResult == null) {
                    return;
                }
                this.sessionStrategy.onAuthentication(authResult, request, response);
            } catch (InternalAuthenticationServiceException var8) {
                //认证失败调用
                this.unsuccessfulAuthentication(request, response, var8);
                return;
            } catch (AuthenticationException var9) {
                //认证失败调用
                this.unsuccessfulAuthentication(request, response, var9);
                return;
            }
            if (this.continueChainBeforeSuccessfulAuthentication) {
                chain.doFilter(request, response);
            }
            //认证成功调用
            this.successfulAuthentication(request, response, chain, authResult);
        }
}


//认证成功调用
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        SecurityContextHolder.getContext().setAuthentication(authResult);
        this.rememberMeServices.loginSuccess(request, response, authResult);
        if (this.eventPublisher != null) {
            this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
        }
        this.successHandler.onAuthenticationSuccess(request, response, authResult);
}

//认证失败调用
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        SecurityContextHolder.clearContext();
        this.rememberMeServices.loginFail(request, response);
}

2.执行this.attemptAuthentication(request, response)

由于org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter继承了AbstractAuthenticationProcessingFilter,并且重写了attemptAuthentication方法,如下:

1)判断请求方式
2)组装UsernamePasswordAuthenticationToken,再组装的构造函数在中this.setAuthenticated(false); 设置未认证。
3)getAuthenticationManager() 获取AuthenticationManager(接口)
public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        if (this.postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        } else {
            String username = this.obtainUsername(request);
            String password = this.obtainPassword(request);
            if (username == null) {
                username = "";
            }
            if (password == null) {
                password = "";
            }
            username = username.trim();
            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
            this.setDetails(request, authRequest);
            return this.getAuthenticationManager().authenticate(authRequest);
        }
    }
}
4)org.springframework.security.authentication.ProviderManager实现了AuthenticationManager,实现了authenticate()方法

上一步this.getAuthenticationManager().authenticate(authRequest);直接调用ProviderManager类中的authenticate()方法,如下:

public class ProviderManager implements AuthenticationManager, MessageSourceAware, InitializingBean {
    private List<AuthenticationProvider> providers;
    private AuthenticationManager parent;
    
    public Authentication authenticate(Authentication authentication) {
        	//获取当前的Authentication的认证类型
            Class<? extends Authentication> toTest = authentication.getClass();
            AuthenticationException lastException = null;
            Authentication result = null;
            boolean debug = logger.isDebugEnabled();
            Iterator var6 = this.getProviders().iterator();
			//遍历providers
            while(var6.hasNext()) {
                //遍历所有的providers
                AuthenticationProvider provider = (AuthenticationProvider)var6.next();
                //使用supports方法判断该provider是否支持当前的认证类型,不支持的话继续遍历
                if (provider.supports(toTest)) {
                    try {
                        //支持的话调用provider的authenticat方法认证
                        result = provider.authenticate(authentication);
                        if (result != null) {
                            //认证通过的话重新生成Authentication对应的Token
                            this.copyDetails(authentication, result);
                            break;
                        }
                    } catch (AccountStatusException var11) {
                        this.prepareException(var11, authentication);
                        throw var11;
                    } catch (InternalAuthenticationServiceException var12) {
                        this.prepareException(var12, authentication);
                        throw var12;
                    } catch (AuthenticationException var13) {
                        lastException = var13;
                    }
                }
            }

        	//如果上面没有验证通过
            if (result == null && this.parent != null) {
                try {
                    //使用父类型AuthenticationManager进行验证
                    result = this.parent.authenticate(authentication);
                } catch (ProviderNotFoundException var9) {
                    ;
                } catch (AuthenticationException var10) {
                    lastException = var10;
                }
            }

            if (result != null) {
                //erase 擦除   是否擦除敏感信息
                if (this.eraseCredentialsAfterAuthentication && result instanceof CredentialsContainer) {
                    ((CredentialsContainer)result).eraseCredentials();
                }

                this.eventPublisher.publishAuthenticationSuccess(result);
                return result;
            } else {
                //1.没有认证通过 2.parent为null 则抛出异常
                if (lastException == null) {
                    lastException = new ProviderNotFoundException(this.messages.getMessage("ProviderManager.providerNotFound", new Object[]{toTest.getName()}, "No AuthenticationProvider found for {0}"));
                }
                this.prepareException((AuthenticationException)lastException, authentication);
                throw lastException;
            }
        }
}
  1. 遍历所有的providers,然后依次执行authenticate()方法,进行验证

    1)如果某一个Provider验证成功,则跳出循环不再执行后续验证。

    2)如果验证成功,会将返回的 result 既 Authentication 对象进一步封装为 Authentication Token;
    比如 UsernamePasswordAuthenticationToken、RememberMeAuthenticationToken 等;这些 Authentication Token 也都继承自 Authentication 对象;

  2. 如果没有任何一个 Provider 验证成功,则试图使用其 parent Authentication Manager 进行验证;

  3. 是否需要擦除密码等敏感信息;

3.org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider实现了AuthenticationProvider接口的authenticate方法
public abstract class AbstractUserDetailsAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware {
   
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        
        String username = authentication.getPrincipal() == null ? "NONE_PROVIDED" : authentication.getName();
        boolean cacheWasUsed = true;
        UserDetails user = this.userCache.getUserFromCache(username);
        if (user == null) {
            cacheWasUsed = false;
            try {
                //获取用户信息由子类实现即DaoAuthenticationProvider
                user = this.retrieveUser(username, (UsernamePasswordAuthenticationToken)authentication);
            } catch (UsernameNotFoundException var6) {
                if (this.hideUserNotFoundExceptions) {
                    throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
                }
                throw var6;
            }
        }

        try {
       //前检查由DefaultPreAuthenticationChecks类实现(主要判断当前用户是否锁定,过期,冻结User接口)
            this.preAuthenticationChecks.check(user);
            //子类实现
            this.additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken)authentication);
        } catch (AuthenticationException var7) {
            if (!cacheWasUsed) {
                throw var7;
            }
            //检测失败,再次尝试检测
            cacheWasUsed = false;
            user = this.retrieveUser(username, (UsernamePasswordAuthenticationToken)authentication);
            this.preAuthenticationChecks.check(user);
            this.additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken)authentication);
        }
		//检测用户密码是否过期
        this.postAuthenticationChecks.check(user);
        if (!cacheWasUsed) {
            this.userCache.putUserInCache(user);
        }

        Object principalToReturn = user;
        if (this.forcePrincipalAsString) {
            principalToReturn = user.getUsername();
        }

        return this.createSuccessAuthentication(principalToReturn, authentication, user);
    }
    
    //将已通过验证的用户信息封装成 UsernamePasswordAuthenticationToken 对象并返回;该对象封装了用户的身份信息,以及相应的权限信息,相关源码如下,
    protected Authentication createSuccessAuthentication(Object principal, Authentication authentication, UserDetails user) {
        UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(
            principal, authentication.getCredentials(), this.authoritiesMapper.mapAuthorities(user.getAuthorities()));
        
        result.setDetails(authentication.getDetails());
        return result;
    }
    
    
    //获取用户 是抽象方法,在子类(org.springframework.security.authentication.dao.DaoAuthenticationProvider)中寻找具体实现
    protected abstract UserDetails retrieveUser(String var1, UsernamePasswordAuthenticationToken var2) throws AuthenticationException;
   
}
4.org.springframework.security.authentication.dao.DaoAuthenticationProvider继承AbstractUserDetailsAuthenticationProvider并且重写retrieveUser()和additionalAuthenticationChecks()方法
public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
	protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        UserDetails loadedUser;
        try {
            //用户自定义,*****重要*****
            //实现接口org.springframework.security.core.userdetails.UserDetailsService
            //通过数据库获取用户信息,或其他方式
            loadedUser = this.getUserDetailsService().loadUserByUsername(username);
            
        } catch (UsernameNotFoundException var6) {
            if (authentication.getCredentials() != null) {
                String presentedPassword = authentication.getCredentials().toString();
                this.passwordEncoder.isPasswordValid(this.userNotFoundEncodedPassword, presentedPassword, (Object)null);
            }

            throw var6;
        } catch (Exception var7) {
            throw new InternalAuthenticationServiceException(var7.getMessage(), var7);
        }

        if (loadedUser == null) {
            throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation");
        } else {
            return loadedUser;
        }
    }
    
    //主要验证密码
    protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        Object salt = null;
        if (this.saltSource != null) {
            salt = this.saltSource.getSalt(userDetails);
        }
        if (authentication.getCredentials() == null) {
            this.logger.debug("Authentication failed: no credentials provided");
            throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
        } else {
            String presentedPassword = authentication.getCredentials().toString();
            //验证密码是否正确
            if (!this.passwordEncoder.isPasswordValid(userDetails.getPassword(), presentedPassword, salt)) {
                this.logger.debug("Authentication failed: password does not match stored value");
                throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
            }
        }
    }
    
}
5.自定义实现UserDetailsService接口重写loadUserByUsername()方法,如下:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
	@Bean
    public UserDetailsService userDetailsService() {    //用户登录实现
        return new UserDetailsService() {
            @Autowired
            private UserRepository userRepository;

            @Override
            public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
                User user = userRepository.findByUsername(s);
                if (user == null) throw new UsernameNotFoundException("Username " + s + " not found");
              //SecurityUser类要实现org.springframework.security.core.userdetails.UserDetails接口,重写方法
                return new SecurityUser(user);
            }
        };
    }
}
6.实现UserDetails接口,重写方法,如下:
public class SecurityUser implements UserDetails {
    private static final long serialVersionUID = 1L;
    
    private String username;    //用户名
    private String password;    //用户密码

    public SecurityUser(User user) {
        if (user != null) {
            this.setUsername(user.getUsername());
            this.setPassword(user.getPassword());
        }
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Collection<GrantedAuthority> authorities = new ArrayList<>();
        String username = this.getUsername();
        if (username != null) {
            SimpleGrantedAuthority authority = new SimpleGrantedAuthority(username);
            authorities.add(authority);
        }
        return authorities;
    }

    //账户是否未过期,过期无法验证
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    //指定用户是否解锁,锁定的用户无法进行身份验证
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    //指示是否已过期的用户的凭据(密码),过期的凭据防止认证
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    //是否可用 ,禁用的用户不能身份验证
    @Override
    public boolean isEnabled() {
        return true;
    }
    
    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }
    
    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值