Spring Security笔记---基本原理及认证流程

Spring Security笔记—基本原理及认证流程

1. 基本原理

在这里插入图片描述

  • 黄色模块:SecurityContextPersistenceFilter是承接容器的session与spring security的重要filter,主要工作是从session中获取SecurityContext,然后放到上下文中,之后的filter大多依赖这个来获取登录态。其主要是通过HttpSessionSecurityContextRepository来存取的。
  • 绿色模块:可选择添加的认证过滤器,用于验证用户登入信息是否正确,正确则保存在session,主要代码AbstractAuthenticationProcessingFilter抽象类的SecurityContextHolder.getContext().setAuthentication(authResult)
  • 橘色模块:FilterSecurityInterceptor 最后的大门,通过WebSecurityConfigurerAdapter或其实现类的protected void configure(HttpSecurity http) throws Exception的配置方法,来确定请求是否能访问
  • 深蓝色模块:ExceptionTranslationFilter 用于获取橘色模块的异常信息,凡是橘色模块不通过的请求就会报错,被其接受

2.认证流程

2.1 流程图

在这里插入图片描述

2.2 认证源码大致方向

  • 大致源码流程比较绕口,不过不是想象中麻烦,具体源码我会在2.3 源码详情追综,希望大伙能坚持一下
  • 递进流程:用户提交登入信息 —》AbstractAuthenticationProcessingFilter 的 dofilter(…)方法 —》UsernamePasswordAuthenticationFilterr 的 attemptAuthentication(…)方法获取用户名和密码生成未认证的UsernamePasswordAuthenticationToken(是Authentication的子类) —》 ProviderManager(AuthenticatonManager的子类)的 authenticate(…) 方法接受UsernamePasswordAuthenticationToken参数 —》 AbstractUserDetailsAuthenticationProvider(AuthenticationProvider的子类)authenticate(…)的方法里面的的 retrieveUser(…) —》DaoAuthenticationProvider的 retrieveUser(…) ----》CustomUserDetailService(自己写的,是UserDetailService的子类)的loadUserByUsername(…) 获取用户信息
  • 返回流程:CustomUserDetailService 的 loadUserByUsername(…) 获取用户信息后—》AbstractUserDetailsAuthenticationProvider 的 createSuccessAuthentication(…) 把未认证的UsernamePasswordAuthenticationToken变成已认证的状态 —》AbstractAuthenticationProcessingFilter的successfulAuthentication(…) 的SecurityContextHolder.getContext().setAuthentication(authResult);把UsernamePasswordAuthenticationToken存到Session

2.2 源码详解

我会抠出 2.2 认证源码大致方向的代码里面关键部分并标序号注释讲解,建议大家用debug模式打点,亲自测试更好记忆

"---------------AbstractAuthenticationProcessingFilter ---------------------------------"

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
			throws IOException, ServletException {
		... 省略部分
		try {
            // 1. 调用UsernamePasswordAuthenticationFilterr(其子类)的attemptAuthentication(...)方法,跳到下个分割线处
			authResult = attemptAuthentication(request, response);
           
			if (authResult == null) {
				return;
			}
			sessionStrategy.onAuthentication(authResult, request, response);
		}
		catch (InternalAuthenticationServiceException failed) ...异常处理省略

	
		if (continueChainBeforeSuccessfulAuthentication) {
			chain.doFilter(request, response);
		}
		  // 16. 获取序号15的Authentication对象,调用该方法
		successfulAuthentication(request, response, chain, authResult);
	}

      
      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);
		}
		 // 17. 获取序号15的Authentication对象,保存在session中
		SecurityContextHolder.getContext().setAuthentication(authResult);

		rememberMeServices.loginSuccess(request, response, authResult);

		// Fire event
		if (this.eventPublisher != null) {
			eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(
					authResult, this.getClass()));
		}
		// 18.成功处理
		successHandler.onAuthenticationSuccess(request, response, authResult);
	}

"--------------------UsernamePasswordAuthenticationFilterr--------------------"

    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();
		// 2. 生成UsernamePasswordAuthenticationToken(Authentication的子类)对象,跳到下个分割线处
		UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
				username, password);

		setDetails(request, authRequest);

    	// 4. this.getAuthenticationManager() 获取Authentication的实现类,即ProviderManager,跳到下个分割线处
       //  15.获取序号14的Authenticaition对象,返回上个分割线
		return this.getAuthenticationManager().authenticate(authRequest);
	}

"--------------UsernamePasswordAuthenticationToken构造方法------------------------------"

       // 3. 因为刚登入所以是false,未认证状态, 回到上个分割线处
	public UsernamePasswordAuthenticationToken(Object principal, Object credentials) {
		super(null);
		this.principal = principal;
		this.credentials = credentials;
		setAuthenticated(false);   
	}

"--------------------------------ProviderManager-----------------------------------"

    public Authentication authenticate(Authentication authentication)
			throws AuthenticationException {
		...省略部分

            // 5. 选择那种认证方式
		for (AuthenticationProvider provider : getProviders()) {
			if (!provider.supports(toTest)) {   // 判断该认证用那种AuthenticationProvider的实现类实现
				continue;
			}

			if (debug) {
				logger.debug("Authentication attempt using "
						+ provider.getClass().getName());
			}

			try {
                // 6. 找到相应的认证方式的子类后,即AbstractUserDetailsAuthenticationProvider类调用方法,跳到下个分割线处
				result = provider.authenticate(authentication);

				if (result != null) {
                    // 13.把序号12的UserDetails对象,赋值到result(Authentication)对象
					copyDetails(authentication, result);
					break;
				}
			}
			catch (AccountStatusException e) ...异常处理省略

	

		if (result != null) {
			if (eraseCredentialsAfterAuthentication
					&& (result instanceof CredentialsContainer)) {
				// Authentication is complete. Remove credentials and other secret data
				// from authentication
				((CredentialsContainer) result).eraseCredentials();
			}

			eventPublisher.publishAuthenticationSuccess(result);
            // 14. 序号13的result对象,到上个分割线
			return result;
		}

		
	}

"--------------------AbstractUserDetailsAuthenticationProvider----------------------"

    public Authentication authenticate(Authentication authentication)
			throws AuthenticationException {
		...省略部分

		if (user == null) {
			cacheWasUsed = false;

			try {
                // 7. 通过DaoAuthenticationProvider(即AbstractUserDetailsAuthenticationProvide的子类)实现类方法来获取UserDetail对象,跳到下个分割线处
				user = retrieveUser(username,
						(UsernamePasswordAuthenticationToken) authentication);
                // 11. 获取序号10的的UserDetails对象
			}
			catch (UsernameNotFoundException notFound) ... 异常处理省略

			Assert.notNull(user,
					"retrieveUser returned null - a violation of the interface contract");
		}

    	// xxx.check(user) 用于验证用户信息是否合理,跟UserDetails构造方法有关,里面有一个为false,则报错不通过
    	// User user = new User("zhangsan",  /// 用户名
        //      password,  // 密码
        //        true,  // 账号是否失效
        //        true,   // 账号是否过期
        //        true,  // 密码是否过期
        //        true,  // 账号是否冻结
        //        AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
		try {
			preAuthenticationChecks.check(user);  
			additionalAuthenticationChecks(user,
					(UsernamePasswordAuthenticationToken) authentication);
		}
		catch (AuthenticationException exception) ...异常处理省略

		postAuthenticationChecks.check(user);

		if (!cacheWasUsed) {
			this.userCache.putUserInCache(user);
		}

		Object principalToReturn = user;

		if (forcePrincipalAsString) {
			principalToReturn = user.getUsername();
		}
		// 12. 返回序号11的的UserDetails对象, 通过createSuccessAuthentication使其变成已认证状态
		return createSuccessAuthentication(principalToReturn, authentication, user);
	}

"------------------------------------DaoAuthenticationProvider--------------------------"
    
    protected final UserDetails retrieveUser(String username,
			UsernamePasswordAuthenticationToken authentication)
			throws AuthenticationException {
		prepareTimingAttackProtection();
		try {
            // 8. 通过自定义UserDetailService的子类来获取UserDetails对象---用户信息,跳到下个分割线
			UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
			if (loadedUser == null) {
				throw new InternalAuthenticationServiceException(
						"UserDetailsService returned null, which is an interface contract violation");
			}
            // 10. 获取序号9的UserDetails对象,返回上个分割线
			return loadedUser;
		}
		catch (UsernameNotFoundException ex) ...异常处理省略
	}
"----------------------CustomUserDetailService-----------------------------------"
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    // 9。获取用户信息,不连接数据库(虚拟账号),获取成功跳到上个分割线
        log.info("【CustomUserDetailService】根据用户名获取用户信息, username = {}", username);
        String password = passwordEncoder.encode("123");
        User user = new User("zhangsan",  /// 用户名
                password,  // 密码
                true,  // 账号是否失效
                true,   // 账号是否过期
                true,  // 密码是否过期
                true,  // 账号是否冻结
                AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
        log.info("【CustomUserDetailService】 password = {}", password);
        log.info("【CustomUserDetailService】获取User对象,user = {}", user);
        return user;
    }
发布了3 篇原创文章 · 获赞 1 · 访问量 108

猜你喜欢

转载自blog.csdn.net/weixin_39147889/article/details/99783346