Spring Security 认证源码分析

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

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

响彻天堂丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值