前言:本章提及的类都是与用户名、密码相关的类
UserDetailsService.class
用于加载用户信息
DaoAuthenticationProvider.class
将数据库的信息拿出来进行认证
AbstractUserDetailsAuthenticationProvider.class
DaoAuthenticationProvider的父类,通过模板模式,真正进行认证的模块,
一个允许子类重写和处理UserDetails对象的基AuthenticationProvider。该类旨在响应UsernamePasswordAuthenticationToken身份验证请求。
AuthenticationProvider.class
@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,() -> this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports","Only UsernamePasswordAuthenticationToken is supported"));String username = determineUsername(authentication);boolean cacheWasUsed = true;UserDetails user = this.userCache.getUserFromCache(username);if (user == null) {cacheWasUsed = false;try {user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);}catch (UsernameNotFoundException ex) {this.logger.debug("Failed to find user '" + username + "'");// 抛出一个含糊的异常,提供安全保护机制if (!this.hideUserNotFoundExceptions) {throw ex;}throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));}Assert.notNull(user, "retrieveUser returned null - a violation of the interface contract");}try {this.preAuthenticationChecks.check(user);additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);}catch (AuthenticationException ex) {if (!cacheWasUsed) {throw ex;}// 可能存在用户数据变更的问题,需要再次尝试获取// There was a problem, so try again after checking// we're using latest data (i.e. not from the cache)cacheWasUsed = false;user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);this.preAuthenticationChecks.check(user);additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);}this.postAuthenticationChecks.check(user);if (!cacheWasUsed) {this.userCache.putUserInCache(user);}Object principalToReturn = user;if (this.forcePrincipalAsString) {principalToReturn = user.getUsername();}return createSuccessAuthentication(principalToReturn, authentication, user);}
获取用户名
从缓存中获取UserDetail对象
private UserCache userCache = new NullUserCache();
默认都是null,可以通过setUserCache设置Cache
this.preAuthenticationChecks.check(user);
additionalAuthenticationChecks
密码校验
public interface PasswordEncoder {String encode(CharSequence rawPassword);boolean matches(CharSequence rawPassword, String encodedPassword);default boolean upgradeEncoding(String encodedPassword) {return false;}
}
this.postAuthenticationChecks.check(user);
createSuccessAuthentication
权限管理
private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();
retrieveUser
- 建议直接抛出UsernameNotFoundException
- 时间攻击防护
通过预先编码这个密码,可以确保在后续的时间攻击防护方法中,不会因为实时编码操作而导致时间差异,这些时间差异可能会被攻击者用来推断密码或其他敏感信息。
加密方法升级后,重新更新密码
UsernamePasswordAuthenticationFilter.class
根据请求中的username
AbstractAuthenticationProcessingFilter.class
private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)throws IOException, ServletException {if (!requiresAuthentication(request, response)) {chain.doFilter(request, response);return;}try {Authentication authenticationResult = attemptAuthentication(request, response);if (authenticationResult == null) {// return immediately as subclass has indicated that it hasn't completedreturn;}// 给session策略提供钩子this.sessionStrategy.onAuthentication(authenticationResult, request, response);// Authentication successif (this.continueChainBeforeSuccessfulAuthentication) {chain.doFilter(request, response);}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) {// Authentication failedunsuccessfulAuthentication(request, response, ex);}}
FormLoginConfigurer.class
默认页面,生产不会使用
默认密码来源
默认加载的类
SecurityConfigurerAdapter.class
链式调用
AbstractAuthenticationFilterConfigurer.class
@Overridepublic void configure(B http) throws Exception {PortMapper portMapper = http.getSharedObject(PortMapper.class);if (portMapper != null) {this.authenticationEntryPoint.setPortMapper(portMapper);}// 设置请求缓存器RequestCache requestCache = http.getSharedObject(RequestCache.class);if (requestCache != null) {this.defaultSuccessHandler.setRequestCache(requestCache);}// 设置处理器this.authFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));this.authFilter.setAuthenticationSuccessHandler(this.successHandler);this.authFilter.setAuthenticationFailureHandler(this.failureHandler);if (this.authenticationDetailsSource != null) {this.authFilter.setAuthenticationDetailsSource(this.authenticationDetailsSource);}// 设置Session策略SessionAuthenticationStrategy sessionAuthenticationStrategy = http.getSharedObject(SessionAuthenticationStrategy.class);if (sessionAuthenticationStrategy != null) {this.authFilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy);}// 记住我RememberMeServices rememberMeServices = http.getSharedObject(RememberMeServices.class);if (rememberMeServices != null) {this.authFilter.setRememberMeServices(rememberMeServices);}// 安全上下文SecurityContextConfigurer securityContextConfigurer = http.getConfigurer(SecurityContextConfigurer.class);if (securityContextConfigurer != null && securityContextConfigurer.isRequireExplicitSave()) {// 安全上下文仓库SecurityContextRepository securityContextRepository = securityContextConfigurer.getSecurityContextRepository();this.authFilter.setSecurityContextRepository(securityContextRepository);}// 添加过滤器F filter = postProcess(this.authFilter);http.addFilter(filter);}