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;
}
}
}
-
遍历所有的providers,然后依次执行authenticate()方法,进行验证
1)如果某一个Provider验证成功,则跳出循环不再执行后续验证。
2)如果验证成功,会将返回的 result 既 Authentication 对象进一步封装为 Authentication Token;
比如 UsernamePasswordAuthenticationToken、RememberMeAuthenticationToken 等;这些 Authentication Token 也都继承自 Authentication 对象; -
如果没有任何一个 Provider 验证成功,则试图使用其 parent Authentication Manager 进行验证;
-
是否需要擦除密码等敏感信息;
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;
}
}