Spring Security源码阅读(1)

Spring Security

1. 介绍

为基于 Spring 的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在 Spring 应用上下文中配置的 Bean,充分利用了 Spring IoC(Inversion of Control 控制反转),DI(Dependency Injection 依赖注入)和 AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。 --百度百科

功能

  • 身份认证和授权
  • 支持Servlet API集成
  • 支持Spring Web MVC集成
  • 其他

2. 核心组件

2.1 核心接口

Authentication
interface Authentication extends Principal, Serializable
    // 权限信息,比如角色或者权限字符串
    Collection<? extends GrantedAuthority> getAuthorities();
    // 凭证信息,比如键入的密码
    Object getCredentials();
    // 额外信息,比如请求附带的IP
    Object getDetails();
    // 认证主体,比如实现DetailServices
    Object getPrincipal();
    // 是否认证标识位
    boolean isAuthenticated();
    void setAuthenticated(boolean isAuthenticated)
        
abstract class AbstractAuthenticationToken implements Authentication,
		CredentialsContainer
	private final Collection<GrantedAuthority> authorities;
	private Object details;
	private boolean authenticated = false;

class UsernamePasswordAuthenticationToken extends AbstractAuthenticationToken
	private final Object principal;
	private Object credentials;
AuthenticationManager
interface AuthenticationManager
	Authentication authenticate(Authentication authentication)

class ProviderManager implements AuthenticationManager, MessageSourceAware,
		InitializingBean
	private List<AuthenticationProvider> providers = Collections.emptyList();
	private AuthenticationManager parent;
	// 其他
	
	// 遍历providers执行authenticate
	Authentication authenticate(Authentication authentication) {}
AuthenticationProvider
abstract class AbstractUserDetailsAuthenticationProvider implements
		AuthenticationProvider, InitializingBean, MessageSourceAware
	Authentication authenticate(Authentication authentication) {
    	// 1. 获取username
    	// 2. 查缓存
    	// 3. retrieveUser: 模板方法,子类DaoAuthenticationProvider执行UserDetailsService loadUserByUsername获取UserDetails
    	// 4. pre check:isAccountNonLocked、isEnabled、isAccountNonExpired
    	// 5. 密码检查: additionalAuthenticationChecks
    	// 6. post check: isCredentialsNonExpired
    	// 7. createSuccessAuthentication: 用户名、密码、权限等填充,返回
    }
		
		
class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider
	UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) {
		UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
		// null,抛异常,否则返回
	}
UserDetails和UserDetailsService

关键是获取权限列表

interface UserDetails extends Serializable
	Collection<? extends GrantedAuthority> getAuthorities();
	String getUsername();
	String getPassword();
	boolean isAccountNonExpired();
	boolean isAccountNonLocked();
	boolean isCredentialsNonExpired();
	boolean isEnabled();
interface UserDetailsService
	UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
SecurityContext和SecurityContextHolder

持有Authentication

class SecurityContextHolder
    // 默认实现: ThreadLocalSecurityContextHolderStrategy
    private static SecurityContextHolderStrategy strategy;
    
class ThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy
    private static final ThreadLocal<SecurityContext> contextHolder = new ThreadLocal<>();
    
class SecurityContextImpl implements SecurityContext
    private Authentication authentication;

2.2 过滤器链

过滤器链
14:48:21.041 [main] INFO
o.s.s.w.DefaultSecurityFilterChain - [,43] -
Creating filter chain: any request, [
org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@9679750,
org.springframework.security.web.context.SecurityContextPersistenceFilter@77865933,
org.springframework.security.web.header.HeaderWriterFilter@18578491,
org.springframework.security.web.csrf.CsrfFilter@2373ad99,
org.springframework.security.web.authentication.logout.LogoutFilter@f88bfbe,
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@5ed4bc,
org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@4364712f,
org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter@9b9a327,
org.springframework.security.web.authentication.www.BasicAuthenticationFilter@7f1ef916,
org.springframework.security.web.savedrequest.RequestCacheAwareFilter@4d18b73a,
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@53c6f96d,
org.springframework.security.web.authentication.AnonymousAuthenticationFilter@75a0c890,
org.springframework.security.web.session.SessionManagementFilter@671c4166,
org.springframework.security.web.access.ExceptionTranslationFilter@67064bdc,
org.springframework.security.web.access.intercept.FilterSecurityInterceptor@d613308
]

过滤器:UsernamePasswordAuthenticationFilter
抽象父类:AbstractAuthenticationProcessingFilter

匹配到的url,执行认证

private RequestMatcher requiresAuthenticationRequestMatcher;

例如:

public UsernamePasswordAuthenticationFilter() {
    super(new AntPathRequestMatcher("/login", "POST"));
}

this.requiresAuthenticationRequestMatcher = requiresAuthenticationRequestMatcher;

认证

private AuthenticationManager authenticationManager;

成功和失败的处理handler,可实现接口自定义处理流程

private AuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();
子类:UsernamePasswordAuthenticationFilter

默认处理post /login

extends UsernamePasswordAuthenticationFilter,自定义认证url和登录处理(比如额外的验证码等信息需要处理)

public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)

// 1. 请求URL是否为POST: request.getMethod().equals("POST")

// 2. 获取用户名和密码: request.getParameter

// 3. 获取需要认证的实体: UsernamePasswordAuthenticationToken
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
				username, password);

// 4. 填充detail:比如remoteAddress和sessionId
setDetails(request, authRequest);

// 5. AuthenticationManager执行authenticate,并返回Authentication:填充权限、(DB)用户信息等
return this.getAuthenticationManager().authenticate(authRequest);
过滤器:FilterSecurityInterceptor

doFilter --> invoke

​ --> super.beforeInvocation(fi);

​ --> super.afterInvocation(token, null);

public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
    FilterInvocation fi = new FilterInvocation(request, response, chain);
    invoke(fi);
}

public void invoke(FilterInvocation fi) throws IOException, ServletException {
    if ((fi.getRequest() != null)
        && (fi.getRequest().getAttribute(FILTER_APPLIED) != null)
        && observeOncePerRequest) {
		fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
    } else {
        // first time this request being called, so perform security checking
        if (fi.getRequest() != null && observeOncePerRequest) {
            fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);
        }

        InterceptorStatusToken token = super.beforeInvocation(fi);

        try {
            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
        } finally {
            super.finallyInvocation(token);
        }

        super.afterInvocation(token, null);
    }
}

3. 流程

关键:

将request请求封装到UsernamePasswordAuthenticationToken

调用authenticationManager.authenticate(authencationToken),得到Authentication authentication

保存到Spring Security:SecurityContextHolder.getContext().setAuthentication(authentication);

缓存存储并返回

输入:用户密码

处理filter: UsernamePasswordAuthenticationFilter

  1. 从request获取输入,组装成UsernamePasswordAuthenticationToken

    UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
          username, password);
    
  2. 认证,并返回填充信息的Authentication authResult对象

    return this.getAuthenticationManager().authenticate(authRequest);
    
    1. class ProviderManager implements AuthenticationManager

      List<AuthenticationProvider> providers
      
      result = provider.authenticate(authentication);
      
    2. abstract class AbstractUserDetailsAuthenticationProvider implements AuthenticationProvider

      class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider

      UserDetails user = this.userCache.getUserFromCache(username);
      if (user == null) {
          cacheWasUsed = false;
      	user = retrieveUser(username, authentication);
      }
      
      preAuthenticationChecks.check(user);
      additionalAuthenticationChecks(user, authentication);
      postAuthenticationChecks.check(user);
      
      if (!cacheWasUsed) {
          this.userCache.putUserInCache(user);
      }	
      
      return createSuccessAuthentication(principalToReturn, authentication, user);
      
      protected final UserDetails retrieveUser(String username,
             		UsernamePasswordAuthenticationToken authentication) {
          UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
          if (loadedUser == null) {
              throw new InternalAuthenticationServiceException("..");
          }
          return loadedUser;
      }
      
    3. 成功or失败:

      try {
          authResult = attemptAuthentication(request, response);
          if (authResult == null) {
              return;
          }
      } catch (InternalAuthenticationServiceException failed) {
          logger.error("..");
          unsuccessfulAuthentication(request, response, failed);
          return;
      } catch (AuthenticationException failed) {
          unsuccessfulAuthentication(request, response, failed);
          return;
      }
      
      // Authentication success
      if (continueChainBeforeSuccessfulAuthentication) {
          chain.doFilter(request, response);
      }
      
      successfulAuthentication(request, response, chain, authResult);
      

      success: successHandler.onAuthenticationSuccess(request, response, authResult);

      failure: failureHandler.onAuthenticationFailure(request, response, failed);

4. 入口

入口:@EnableWebSecurity
@Import({ WebSecurityConfiguration.class,
SpringWebMvcImportSelector.class,
OAuth2ImportSelector.class })
@EnableGlobalAuthentication
@Import(AuthenticationConfiguration.class)

WebSecurityConfiguration
AuthenticationConfiguration
    生成bean AuthenticationManager authenticationManager

入口:继承WebSecurityConfigurerAdapter

JWT

在3的基础之上:

jwt过滤器:在UsernamePasswordAuthenticationFilter之前过滤

读取token,拿到登录信息,查询缓存,得到认证用户信息,填充到SecurityContextHolder

token刷新

参考

(推荐)SpringSecurity+JWT认证流程解析https://juejin.im/post/6846687598442708999

芋道源码http://www.iocoder.cn/Spring-Security/good-collection/?vip

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值