SpringSecurity基础及源码分析

网上有几篇文章都写得很好,可以自行参考:

spring security原理和机制 | Spring Boot 35_哪 吒的博客-CSDN博客_spring security

Spring Security介绍_普通网友的博客-CSDN博客_spring security

1.基本介绍

Spring 是非常流行和成功的 Java 应用开发框架。Spring Security 正是 Spring 家族中的成员。Spring Security 基于 Spring 框架提供了一套 Web 应用安全性的完整解决方案。

正如你可能知道的关于安全方面的两个主要区域是“认证”和“授权”或者访问控制。一般来说,Web 应用的安全性包括用户认证Authentication和用户授权Authorization两个部分。这两点也是 Spring Security 重要核心功能。

  1. 认证:验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统。用户认证一般要求用户提供用户名和密码。系统通过校验用户名和密码来完成认证过程。通俗点说就是系统认为用户是否能登录。
  2. 授权:验证某个用户是否有权限执行某个操作。在一个系统中不同用户所具有的权限是不同的。比如对一个文件来说有的用户只能进行读取,而有的用户可以进行修改。一般来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的权限。通俗点讲就是系统判断用户是否有权限去做某些事情。

2.SpringSecurity与shiro总结

相对于 Shiro,在 SSM 中整合 Spring Security 都是比较麻烦的操作,所以SpringSecurity 虽然功能比 Shiro 强大,但是使用反而没有 Shiro 多。Shiro 虽然功能没有Spring Security 多,但是对于大部分项目而言Shiro 也够用了。自从有了 Spring Boot 之后,Spring Boot 对于 Spring Security 提供了自动化配置方案,可以使用更少的配置来使用 Spring Security。因此,一般来说,常见的安全管理技术栈的组合是这样的。

SSM + Shiro

Spring Boot/Spring Cloud + Spring Security

以上只是一个推荐的组合而已。如果单纯从技术上来说,怎么组合都是可以运行的。0c;无论怎么组合,都是可以运行

3.SpringSecurity 过滤器

SpringSecurity 采用的是责任链的设计模式,它有一条很长的过滤器链。现在对这条过滤器链的 15 个过滤器进行说明:

  1. WebAsyncManagerIntegrationFilter:将 Security 上下文与 Spring Web 中用于处理异步请求映射的 WebAsyncManager 进行集成。
  2. SecurityContextPersistenceFilter:在每次请求处理之前将该请求相关的安全上下文信息加载到 SecurityContextHolder 中,然后在该次请求处理完成之后,将SecurityContextHolder 中关于这次请求的信息存储到一个“仓储”中,然后将SecurityContextHolder 中的信息清除,例如在 Session 中维护一个用户的安全信 息就是这个过滤器处理的。
  3. HeaderWriterFilter:用于将头信息加入响应中。
  4. CsrfFilter:用于处理跨站请求伪造。
  5. LogoutFilter:用于处理退出登录。
  6. UsernamePasswordAuthenticationFilter:用于处理基于表单的登录请求,从表单中获取用户名和密码。默认情况下处理来自 /login 的请求。从表单中获取用户名和密码时,默认使用的表单 name 值为 username 和 password,这两个值可以通过设置这个过滤器的 usernameParameter 和 passwordParameter 两个参数的值进行修改。
  7. DefaultLoginPageGeneratingFilter:如果没有配置登录页面,那系统初始化时就会配置这个过滤器,并且用于在需要进行登录时生成一个登录表单页面。
  8. BasicAuthenticationFilter:检测和处理 http basic 认证。
  9. RequestCacheAwareFilter:用来处理请求的缓存。
  10. SecurityContextHolderAwareRequestFilter:主要是包装请求对象 request。
  11. AnonymousAuthenticationFilter:检测 SecurityContextHolder 中是否存在Authentication 对象,如果不存在为其提供一个匿名 Authentication。
  12. SessionManagementFilter:管理 session 的过滤器
  13. ExceptionTranslationFilter:处理 AccessDeniedException 和 AuthenticationException 异常。
  14. FilterSecurityInterceptor:可以看做过滤器链的出口。
  15. RememberMeAuthenticationFilter:当用户没有登录而直接访问资源时, 从 cookie里找出用户的信息, 如果 Spring Security 能够识别出用户提供的 remember me cookie, 用户将不必填写用户名和密码, 而是直接登录进入系统,该过滤器默认不开启。

4.实现原理

5.基本流程

绿色部分是认证过滤器,需要我们自己配置,可以配置多个认证过滤器。认证过滤器可以使用 Spring Security 提供的认证过滤器,也可以自定义过滤器(例如:短信验证)。认证过滤器要在 配置类的configure方法中配置,没有配置不生效。

核心过滤器:

  1. UsernamePasswordAuthenticationFilter:该过滤器会拦截前端提交的 POST 方式的登录表单请求,并进行身份认证。
  2. ExceptionTranslationFilter :该过滤器不需要我们配置,对于前端提交的请求会直接放行,捕获后续抛出的异常并进行处理,如AccessDeniedException和AuthenticationException。
  3. FilterSecurityInterceptor:该过滤器是过滤器链的最后一个过滤器,根据资源权限配置来判断当前请求是否有权限访问对应的资源。如果访问受限会抛出相关异常,并由 ExceptionTranslationFilter 过滤器进行捕获和处理。

5.1.SpringSecurity 认证流程

引用一张网络上流传比较广泛的、来自大牛作者三更的流程图。

5.1.1.说明

Authentication:它的实现类,表示当前访问系统的用户,封装了用户相关信息。

AuthenticationManager:定义了认证Authentication的方法。

UserDetailsService:加载用户特定数据的核心接口。里面定义了一个根据用户名查询用户信息的方法。

UserDetails:提供核心用户信息。通过UserDetailsService根据用户名获取处理的用户信息要封装成UserDetails对象返回。然后将这些信息封装到Authentication对象中。

5.1.2.源码分析流程

  1. 当用户提交用户名和密码后,首先进入AbstractAuthenticationProcessingFilter的实现类UsernamePasswordAuthenticationFilter过滤器,身份认证的工作就是有这个过滤器完成的。这个过滤器里面也定义了默认请求方式为post。
//如果请求方式不对就会抛异常

if (postOnly && !request.getMethod().equals("POST")) {
    throw new AuthenticationServiceException( "Authentication method not supported: " + request.getMethod());
}
  1. 在这个过滤器中将用户提交的用户名和密码封装成一个UsernamePasswordAuthenticationToken对象。此时里面只有用户名和密码,没有权限信息,并且其中的认证标示为false。
    UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken( username, password); 
    // Allow subclasses to set the "details" property 
    setDetails(request, authRequest); 
    return this.getAuthenticationManager().authenticate(authRequest); 


# 构造方法 
public UsernamePasswordAuthenticationToken(Object principal, Object credentials) {         
    super((Collection)null); 
    this.principal = principal; 
    this.credentials = credentials; 
    //未认证状态 
    this.setAuthenticated(false); 
}
  1. 方法中最后,获取了一个认证管理其对象并调用其authenticate方法进行用户信息认证,最终调用的是AuthenticationManager的实现类ProviderManager中的authenticate方法。

  1. ProviderManager中的authenticate方法获取可以支持的认证功能提供器,通过Debug我们知道最终使用的是DaoAuthenticationProvider的,但是DaoAuthenticationProvider并没有提供authenticate方法实现,走的是他的父类AbstractUserDetailsAuthenticationProvider的默认实现authenticate方法。authenticate方法中执行了retrieveUser方法,这是个抽象方法,具体实现仍由DaoAuthenticationProvider提供。

  1. DaoAuthenticationProvider的具体实现方法中通过获取用户详情UserDetailService对象来loadUserByUsername加载用户信息。通常我们就是要去实现UserDetailService接口,重写loadUserByUsername方法,根据自己的业务情况自定义从数据库获取用户信息和权限的逻辑,获取到用户信息后,我们需要把它封装成UserDetail对象返回。
  2. 回到AbstractUserDetailsAuthenticationProvider中继续执行,会进行一系列的check检查。
//用户状态检查:是否被锁定?是否可用?是否已过期? 
this.preAuthenticationChecks.check(user); 
//用户密码校验 
this.additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken)authentication);
    1. 校验用户状态:校验UserDetail中的那几个boolean值的,通常我们自定义一个类去实现UserDetail的时候,会把这这几个Boolean值都设置为true。

    1. 校验用户密码:这里就会涉及到算法加密器PasswordEncoder,通常我们都是使用BcryptPasswordEncoder,,需要我们在Security的核心配置中自行配置。

    1. 将用户信息放入缓存的操作,可以自定义实现,其实现类支持Redis,可以根据需要配置,但一般可以不配置,使用其默认的缓存操作即可。
if (!cacheWasUsed) { 
    this.userCache.putUserInCache(user); 
}

  1. 检查和校验完毕后,回到AbstractUserDetailsAuthenticationProvider中继续执行,最后重新构建了一个对象,此时使用的构造方法和之前的不同,其权限列表被赋值,认证状态也变成了true。最终返回包含有用户名、密码和权限信息的对象。

通过对上述的分析,再去看现在这张图,就会格外清晰。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值