ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Spring Security 源码学习(三): Spring Security认证流程

2022-05-25 20:01:09  阅读:184  来源: 互联网

标签:AuthenticationProvider Spring request 认证 Authentication 源码 result Security respo


【参考文章】: Spring Security 认证流程 (写的很形象)

认证功能由 springSecurityFilterChain 中的 UsernamePasswordAuthenticationFilter 实现

认证流程

  1. UsernamePasswordAuthenticationFilter 创建一个未认证的Authentication, 然后交给 AuthenticationManager 进行认证
  2. AuthenticationManager 的默认实现 ProviderManager 管理负责认证的AuthenticationProvider, 然后遍历AuthenticationProvider, 如果这个AuthenticationProvider 支持这种类型的认证, 将未认证信息交给 AuthenticationProvider 处理认证
  3. 认证成功则会返回一个通过认证的Authentication对象,否则抛异常表示认证失败

1. AbstractAuthenticationProcessingFilter(认证入口)

该类下有三个子类,子类都没有重写doFilter(),都是调用父类的doFilter()

  1. ClientCredentialsTokenEndpointFilter
  2. OAuth2ClientAuthenticationProcessingFilter(OAuth2下实现)
  3. UsernamePasswordAuthenticationFilter(默认实现)

doFilter()中调用了子类的attemptAuthentication()进行真正的认证逻辑处理

public abstract class AbstractAuthenticationProcessingFilter extends GenericFilterBean
		implements ApplicationEventPublisherAware, MessageSourceAware {
			
	public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) res;
		if (!requiresAuthentication(request, response)) {
			chain.doFilter(request, response);
			return;
		}
		Authentication authResult;
		try {
			// 调用子类实现的抽象方法,返回一个验证对象
			// 该方法的实现才是真正处理验证逻辑
			authResult = attemptAuthentication(request, response);
			if (authResult == null) {
				return;
			}
			sessionStrategy.onAuthentication(authResult, request, response);
		}
		catch (InternalAuthenticationServiceException failed) {
			// 验证失败
			logger.error("An internal error occurred while trying to authenticate the user.",failed);
			unsuccessfulAuthentication(request, response, failed);
			return;
		}
		catch (AuthenticationException failed) {
			// 验证失败
			unsuccessfulAuthentication(request, response, failed);
			return;
		}
		// 验证成功
		if (continueChainBeforeSuccessfulAuthentication) {
			chain.doFilter(request, response);
		}
		successfulAuthentication(request, response, chain, authResult);
	}
}

2. UsernamePasswordAuthenticationFilter

  1. 根据用户名和密码生成一个 UsernamePasswordAuthenticationToken 类型的 Authentication
  2. 将 Authentication 交给 ProviderManager 处理
public class UsernamePasswordAuthenticationFilter extends
		AbstractAuthenticationProcessingFilter {
	// 默认处理POST方法的 /login请求(这是默认的登录请求URI)
	public UsernamePasswordAuthenticationFilter() {
		super(new AntPathRequestMatcher("/login", "POST"));
	}

	public Authentication attemptAuthentication(HttpServletRequest request,
			HttpServletResponse response) throws AuthenticationException {
		// 只处理POST方法的请求
		if (postOnly && !request.getMethod().equals("POST")) {
			throw new AuthenticationServiceException(
					"Authentication method not supported: " + request.getMethod());
		}

		String username = obtainUsername(request);
		String password = obtainPassword(request);
		if (username == null) {
			username = "";
		}
		if (password == null) {
			password = "";
		}
		username = username.trim();
		// 后续AuthenticationProvider 会根据 authRequest 的 class 类型判断自己是否进行认证
		UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
				username, password);
		setDetails(request, authRequest);
		// 此处 his.getAuthenticationManager() 返回的是 ProviderManager
		return this.getAuthenticationManager().authenticate(authRequest);
	}
}

3. ProviderManager

管理负责认证的 AuthenticationProvider
默认的 providers 只有一个,类型为 AnonymousAuthenticationProvider
默认的 parent 类型为 ProviderManager , 其 providers 为 DaoAuthenticationProvider, 默认情况下最终由 DaoAuthenticationProvider 处理认证

public class ProviderManager implements AuthenticationManager, MessageSourceAware,InitializingBean {
	public Authentication authenticate(Authentication authentication)throws AuthenticationException {

		for (AuthenticationProvider provider : getProviders()) {
                        // 不支持认证, 则跳过
			if (!provider.supports(toTest)) {
				continue;
			}
			try {
				result = provider.authenticate(authentication);
				if (result != null) {
					copyDetails(authentication, result);
					// 有一个 AuthenticationProvider 认证通过则结束
					break;
				}
			}
			catch (AccountStatusException | InternalAuthenticationServiceException e) {
				prepareException(e, authentication);
				throw e;
			} catch (AuthenticationException e) {
				lastException = e;
			}
		}

		if (result == null && parent != null) {
			try {
				// 所有的provider都不支持校验, 则由parent进行校验
				result = parentResult = parent.authenticate(authentication);
			}
			catch (ProviderNotFoundException e) {
			}
			catch (AuthenticationException e) {
				lastException = parentException = e;
			}
		}

		if (result != null) {
			if (eraseCredentialsAfterAuthentication&& (result instanceof CredentialsContainer)) {
				((CredentialsContainer) result).eraseCredentials();
			}
			if (parentResult == null) {
				eventPublisher.publishAuthenticationSuccess(result);
			}
			return result;
		}
		...省略其他代码
	}
}

4. DaoAuthenticationProvider

处理认证默认配置

  1. DaoAuthenticationProvider 的 authenticate() 继承自父类 AbstractUserDetailsAuthenticationProvider, 自己本身并未实现
  2. 父类中的 authenticate() 调用子类实现的 retrieveUser() 进行认证, 认证成功则返回一个 UserDetails 的实例, 否则认证失败
  3. DaoAuthenticationProvider 的 retrieveUser() 通过 UserDetailsService 的 loadUserByUsername() 方法获取 UserDetails 的实例
  4. UserDetailsService 就是我们一般实现的接口,并在 WebSecurityConfigurerAdapter 的实现类中进行配置

标签:AuthenticationProvider,Spring,request,认证,Authentication,源码,result,Security,respo
来源: https://www.cnblogs.com/virgosnail/p/16287755.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有