Spring Security 认证源码分析

导读:本篇文章讲解 Spring Security 认证源码分析,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

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

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

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/15080.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!