1 简介
描述: Spring Security 认证是通过UsernamePasswordAuthenticationFilter过滤器实现的,分析该过滤器流程即可。
2 源码分析
2.1 主流程分析
描述: UsernamePasswordAuthenticationFilter认证主流程查看父类AbstractAuthenticationProcessingFilter doFilter方法。
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
//【1】 判断当前请求是否是post,如果不是post那么直接放行
if (!requiresAuthentication(request, response)) {
chain.doFilter(request, response);
return;
}
if (logger.isDebugEnabled()) {
logger.debug("Request is to process authentication");
}
// 保存认证结果
Authentication authResult;
try {
//【2】 调用子类UsernamePasswordAuthenticationFilter进行身份认证
authResult = attemptAuthentication(request, response);
if (authResult == null) {
// return immediately as subclass has indicated that it hasn't completed
// authentication
return;
}
//【3】 session策略处理
sessionStrategy.onAuthentication(authResult, request, response);
}
catch (InternalAuthenticationServiceException failed) {
logger.error(
"An internal error occurred while trying to authenticate the user.",
failed);
//【4】 认证失败的处理
unsuccessfulAuthentication(request, response, failed);
return;
}
catch (AuthenticationException failed) {
// Authentication failed
//【4】 认证失败的处理
unsuccessfulAuthentication(request, response, failed);
return;
}
//【5】 认证成功的处理
// Authentication success
if (continueChainBeforeSuccessfulAuthentication) {
chain.doFilter(request, response);
}
//【6】 调用认证成功的处理器
successfulAuthentication(request, response, chain, authResult);
}
2.2 认证源码分析
描述: 查看UsernamePasswordAuthenticationFilter类attemptAuthentication方法。
2.2.1 类属性介绍
2.2.2 认证流程
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
// 【1】 如果不是post请求那么直接抛异常
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
// 【2】 获取表单提交的账号、密码
String username = obtainUsername(request);
String password = obtainPassword(request);
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
username = username.trim();
// 【3】 分装认证对象Authentication
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
// Allow subclasses to set the "details" property
// 【4】 认证对象设置相关属性
setDetails(request, authRequest);
// 【5】调用ProviderManager类进行认证
return this.getAuthenticationManager().authenticate(authRequest);
}
描述: Authentication接口的实现用于存储主体(用户)信息:
public interface Authentication extends Principal, Serializable {
/**
* 权限
*/
Collection<? extends GrantedAuthority> getAuthorities();
/**
* 密码
*/
Object getCredentials();
/**
* 其它信息
*/
Object getDetails();
/**
* 用户主体信息
*/
Object getPrincipal();
/**
* 是否被认证
*/
boolean isAuthenticated();
/**
* 设置认真信息,true认证,false未认证
*/
void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}
2.2.3 ProviderManager 认证源码分析
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
//【1】 获取认证对象类
Class<? extends Authentication> toTest = authentication.getClass();
AuthenticationException lastException = null;
AuthenticationException parentException = null;
Authentication result = null;
Authentication parentResult = null;
boolean debug = logger.isDebugEnabled();
//【2】 遍历认证迭代器
for (AuthenticationProvider provider : getProviders()) {
//不匹配continue
if (!provider.supports(toTest)) {
continue;
}
if (debug) {
logger.debug("Authentication attempt using "
+ provider.getClass().getName());
}
try {
//【3】 具体认证,会调用用户自定义认证类UserDetailsService进行认证,返回UserDetails对象
result = provider.authenticate(authentication);
// 认证成功后保存相关信息
if (result != null) {
copyDetails(authentication, result);
break;
}
}
catch (AccountStatusException | InternalAuthenticationServiceException e) {
prepareException(e, authentication);
// SEC-546: Avoid polling additional providers if auth failure is due to
// invalid account status
throw e;
} catch (AuthenticationException e) {
lastException = e;
}
}
//【4】 如果认证结果为空,调用父认证器进行认证
if (result == null && parent != null) {
// Allow the parent to try.
try {
//认证结果
result = parentResult = parent.authenticate(authentication);
}
catch (ProviderNotFoundException e) {
// ignore as we will throw below if no other exception occurred prior to
// calling parent and the parent
// may throw ProviderNotFound even though a provider in the child already
// handled the request
}
catch (AuthenticationException e) {
lastException = parentException = e;
}
}
if (result != null) {
if (eraseCredentialsAfterAuthentication
&& (result instanceof CredentialsContainer)) {
// Authentication is complete. Remove credentials and other secret data
// from authentication
((CredentialsContainer) result).eraseCredentials();
}
// If the parent AuthenticationManager was attempted and successful than it will publish an AuthenticationSuccessEvent
// This check prevents a duplicate AuthenticationSuccessEvent if the parent AuthenticationManager already published it
if (parentResult == null) {
eventPublisher.publishAuthenticationSuccess(result);
}
return result;
}
// Parent was null, or didn't authenticate (or throw an exception).
if (lastException == null) {
lastException = new ProviderNotFoundException(messages.getMessage(
"ProviderManager.providerNotFound",
new Object[] { toTest.getName() },
"No AuthenticationProvider found for {0}"));
}
// If the parent AuthenticationManager was attempted and failed than it will publish an AbstractAuthenticationFailureEvent
// This check prevents a duplicate AbstractAuthenticationFailureEvent if the parent AuthenticationManager already published it
if (parentException == null) {
prepareException(lastException, authentication);
}
throw lastException;
}
3 认证成功
protected void successfulAuthentication(HttpServletRequest request,
HttpServletResponse response, FilterChain chain, Authentication authResult)
throws IOException, ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Authentication success. Updating SecurityContextHolder to contain: "
+ authResult);
}
//【1】 认证对象存入Spring Security Cotext 上下文中
SecurityContextHolder.getContext().setAuthentication(authResult);
//【2】remember(记住我)处理
rememberMeServices.loginSuccess(request, response, authResult);
//【3】 发布事件
// Fire event
if (this.eventPublisher != null) {
eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(
authResult, this.getClass()));
}
successHandler.onAuthenticationSuccess(request, response, authResult);
}
4 Debug