SpringSecurity


SpringSecurity实战

权限管理

基本上涉及到用户参与的系统都要进行权限管理,权限管理属于系统安全的范畴,权限管理实现对用户访问系统的控制按照安全规则或者安全策略控制用户可以访问而且只能访问自己
权限管理包括身份认证和授权两部分,简称认证授权。对于需要访问控制的资源用户 首先经过身份认证,认证通过后用户具有该资源的权限方可访问

认证

身份认证,就是判断一个用户是否为合法用户的处理过程,最常用的简单身份认证是系统通过核对用户输入的用户名和口令,看其是否与系统中存储的该用户的用户名和口令一致,来判断用户身份是否正确,对于采用指纹等系统,则出示指纹;对于硬件Key等刷卡系统,则需要刷卡

授权

授权, 既访问控制,控制谁能访问哪些资源。主体进行身份认证后需要分配权限方可访问系统的资源,对于某些资源没有权限是无法访问的

解决方案

和其他领域不同,在JAVA企业级开发中,安全管理框架非常少,目前比较常见的就是:

  • Shrio
    • Shiro本身是一个老牌的安全管理框架,有着众多的有点,例如轻量、简单、易于集成、可以在JavaSE环境中使用等。不过,在微服务时代,Shrio就显得力不从心了,在微服务面前和扩展方面,无法充分发展自己的优势
  • 开发者自定义
    • 也有很多公司选择自定义权限,既自己开发权限管理。但是一个系统的交全,不仅仅是登录和权限控制这么简单,我们还要考虑各种各样可能存在的网络攻击以及防御策略,从这个角度来说,开发者自己实现安全管理也并非是一件容易的事情,只有大公司才有足够的人力物力去支持这件事情
  • SpringSecurity
    • Spring Security作为Spring家族的一员,在和Spring家族的其他成员如Spring Boot Spring Cloud进行整合时,具有其他框架无可比拟的优势,同时对OAuth2有着良好的支持 再加上Spring Cloud对Spring Security的断加持(如 推出Spring Cloud Security),让Spring Security不知不觉中成为微服务目前的首选安全管理框架

简介

官方定义

  • 官网地址 https://spring.io/projects/spring-security
    Spring Security is a powerful and highly customizabl authentication and access control framework it is the de-facto standard for securing Spring-based applications.
    Spring Security is a framework that focuses on providing both authentication and authorization to Java applications. Like all Spring projects, the real power of Spring Security is found in how easily it can be extended to meet custom requirements
    Spring Security是一个功能强大、 可高度定制的身份验证和访问控制框架。它是保护基于Spring的应用程序的事实标准。
    Spring Security是一个面向Java应用程序提供身份验证和安全性的框架。与所有Spring项目一样,Spring Security的真正威力在于它可以轻松地扩展以满足定制需求。
  • 总结

    Spring Security是一个功能强大、 可高度定制的身份验证和访问控制的框架。或者说用来实现系统中权限管理的框架。

    整体架构

    的架构设计中, 认证和授权是分开的,无论使用什么样的认证方式。都不会影响授权,这是两个独立的存在,这种独立带来的好处之一,就是可以非常方便地整合一些外部的解决方案。
    bBm4Z4.png

    认证

AuthenticationManager
在Spring Security中认证是由AuthenticationManager接口来负责的,接口定义为
bBmLQK.png

  • 返回Authentication 表示认证成功
  • 返回AuthenticationException异常,表示认证失败
    AuthenticationManager主要实现类为ProvilerManager, 在ProviderManager中管理了众多AuthenticationProvider实例。在一次完整的认证流程中, Spring Security允许存在多个
    AuthenticationProvider,用来实现多种认证方式,这些AuthenticationProvider都是由ProviderManager进行统一管理的
    bBnZwQ.png
  • Authentication*
    认证以及认证成功的信息主要是由Authentication的实现类进行保存的,其接口定义为:
    bBnKWq.png
    public interface Authentication extends Principal, Serializable {
    Collection<? extends GrantedAuthority> getAuthorities();
    Object getCredentials();
    Object getDetails();
    Object getPrincipal();
    boolean isAuthenticated();
    void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
    }
  • getAuthorities 获取用户权限信息
  • getCredentials 获取用户凭证信息,一般指密码
  • getDetails 获取用户详细信息
  • getPrincipal 获取用户身份信息,用户名、用户对象等
  • isAuthenticated 用户是否认证成功

SecurityContextHolder

SecurityContextHolder 用来获取登录之后用户信息。Spring Security 会将登录用户数据保存在 Session 中。但是,为了使用方便,Spring Security在此基础上还做了一些改进,其中最主要的一个变化就是线程绑定。当用户登录成功后,Spring Security 会将登录成功的用户信息保存到 SecurityContextHolder 中。SecurityContextHolder 中的数据保存默认是通过ThreadLocal 来实现的,使用 ThreadLocal 创建的变量只能被当前线程访问,不能被其他线程访问和修改,也就是用户数据和请求线程绑定在一起。当登录请求处理完毕后,Spring Security 会将 SecurityContextHolder 中的数据拿出来保存到 Session 中,同时将 SecurityContexHolder 中的数据清空。以后每当有请求到来时,Spring Security 就会先从 Session 中取出用户登录数据,保存到 SecurityContextHolder 中,方便在该请求的后续处理过程中使用,同时在请求结束时将 SecurityContextHolder 中的数据拿出来保存到 Session 中,然后将 Security SecurityContextHolder 中的数据清空。这一策略非常方便用户在 Controller、Service 层以及任何代码中获取当前登录用户数据。

授权

当完成认证后,接下米就是授权了。在 Spring Security 的授权体系中,有两个关键接口,
AccessDecisionManager

AccessDecisionManager (访问决策管理器),用来决定此次访问是否被允许

bBn0l6.png
AccessDecisionVoter

AccessDecisionVoter (访问决定投票器),投票器会检查用户是否具备应有的角色,进而投出赞成、反对或者弃权票。

AccesDecisionVoter 和 AccessDecisionManager 都有众多的实现类,在 AccessDecisionManager 中会换个遍历 AccessDecisionVoter,进而决定是否允许用户访问,因而 AaccesDecisionVoter 和 AccessDecisionManager 两者的关系类似于 AuthenticationProvider 和 ProviderManager 的关系。

ConfigAttribute

ConfigAttribute,用来保存授权时的角色信息

在 Spring Security 中,用户请求一个资源(通常是一个接口或者一个 Java 方法)需要的角色会被封装成一个 ConfigAttribute 对象,在 ConfigAttribute 中只有一个 getAttribute方法,该方法返回一个 String 字符串,就是角色的名称。一般来说,角色名称都带有一个 ROLE_ 前缀,投票器 AccessDecisionVoter 所做的事情,其实就是比较用户所具各的角色和请求某个 资源所需的 ConfigAtuibute 之间的关系。

环境搭建

  • Spring Boot
  • Spring Security
    • 认证:判断用户是否是系统合法用户过程
    • 授权:判断系统内用户可以访问或具有访问那些资源权限过程
  • 创建项目*

    1创建SpringBoot项目

2创建 controller

1
2
3
4
5
6
7
8
@RestController
public class HelloController {
@GetMapping("hello")
public String hello(){
System.out.println("hello Security");
return "hello Security";
}
}

启动项目进行调试

整合Spring Security

1 引入Spring Security相关依赖

1
2
3
4
5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>2.6.4</version>
</dependency>

2 在次启动项目

  • 1.启动完成后控制台生成一个密码
  • 2.访问hello发现直接跳转到登录页面


3 登录系统
默认用户名为:user
默认密码为: 控制台打印的uuid
这就是Spring Security的强大之处,只需要引入一个依赖,所有的接口就会自动保护起来!

  • 为什么引入Spring Security之后 没有任何配置所有请求都要认证呐
  • 在项目中明明没有登录界面,登录界面怎么来的呢?
  • 为什么使用user和控制台密码能登陆,登录时验证数据源存在哪里呢?

实现原理

https://docs.spring.io/spring-security/site/docs/5.5.4/reference/html5/#servlet-architecture
虽然开发者只需要引入-个依赖,就可以让Spring Security对应用进行保护。SpringSecurity又是如何做到的呢?
在Spring Security中认证、授权等功能都是基于过滤器完成的。


需要注意的是,默认过滤器并不是直接放在Web项目的原生过滤器链中,而是通过一个FlterChainProxy来统干管理。Spring Security中的过滤器链通过FilterChainProxy 嵌入到
Web项目的原生过滤器链中。FilterChainProxy 作为一一个顶层的管理者,将统一管理 Security Fiter FilterChainProxy 本身是通过Spring 框架提供DelegatingFilterProxy整合到原生的过滤器链中

Security Filters

那么在Spring Security中给我们提供那些过滤器?默认情况下那些过滤器会被加载呢?
| 过滤器 | 过滤器作用 | 默认是否加载 |
| ChannelProcessingFilter | 过滤请求协议 HTTP 、HTTPS | No |
| WebAsyncManagerIntegrationFilter | 将 WebAsyncManger 与 SpringSecurity 上下文进行集成 | YES |
| SecurityContextPersistenceFilter | 在处理请求之前,将安全信息加载到 SecurityContextHolder 中 | YES |
| HeaderWriterFilter | 处理头信息加入响应中 | YES |
| CorsFilter | 处理跨域问题 | NO |
| CsrfFilter | 处理 CSRF 攻击 | YES |
| LogoutFilter | 处理注销登录 | YES |
| OAuth2AuthorizationRequestRedirectFilter | 处理 OAuth2 认证重定向 | NO |
| Saml2WebSsoAuthenticationRequestFilter | 处理 SAML 认证 | NO |
| X509AuthenticationFilter | 处理 X509 认证 | NO |
| AbstractPreAuthenticatedProcessingFilter | 处理预认证问题 | NO |
| CasAuthenticationFilter | 处理 CAS 单点登录 | NO |
| OAuth2LoginAuthenticationFilter | 处理 OAuth2 认证 | NO |
| Saml2WebSsoAuthenticationFilter | 处理 SAML 认证 | NO |
| UsernamePasswordAuthenticationFilter | 处理表单登录 | YES |
| OpenIDAuthenticationFilter | 处理 OpenID 认证 | NO |
| DefaultLoginPageGeneratingFilter | 配置默认登录页面 | YES |
| DefaultLogoutPageGeneratingFilter | 配置默认注销页面 | YES |
| ConcurrentSessionFilter | 处理 Session 有效期 | NO |
| DigestAuthenticationFilter | 处理 HTTP 摘要认证 | NO |
| BearerTokenAuthenticationFilter | 处理 OAuth2 认证的 Access Token | NO |
| BasicAuthenticationFilter | 处理 HttpBasic 登录 | YES |
| RequestCacheAwareFilter | 处理请求缓存 | YES |
| SecurityContextHolder /AwareRequestFilter | 包装原始请求 | YES |
| JaasApiIntegrationFilter | 处理 JAAS 认证 | NO |
| RememberMeAuthenticationFilter | 处理 RememberMe 登录 | NO |
| AnonymousAuthenticationFilter | 配置匿名认证 | YES |
| OAuth2AuthorizationCodeGrantFilter | 处理OAuth2认证中授权码 | NO |
| SessionManagementFilter | 处理 session 并发问题 | YES |
| ExceptionTranslationFilter | 处理认证/授权中的 | YES |
| FilterSecurityInterceptor | 处理授权相关 | YES |
| SwitchUserFilter | 处理账户切换 | NO |
可以看出,Spring Security 提供了 30 多个过滤器。默认情况下Spring Boot 在对 Spring Security 进入自动化配置时,会创建一个名为 SpringSecurityFilerChain 的过滤器,并注入到 Spring 容器中,这个过滤器将负责所有的安全管理,包括用户认证、授权、重定向到登录页面等。具体可以参考WebSecurityConfiguration的源码:


SpringBootWebSecurityConfiguration
这个类是spring boot自动配置类,通过这个源码得知,默认情况下对所有请求进行权限控制:

1
2
3
4
5
6
7
8
9
10
11
12
@Configuration(proxyBeanMethods = false)
@ConditionalOnDefaultWebSecurity
@ConditionalOnWebApplication(type = Type.SERVLET)
class SpringBootWebSecurityConfiguration {

@Bean
@Order(SecurityProperties.BASIC_AUTH_ORDER)
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().httpBasic();
return http.build();
}

这就是为什么在引入Spring Security中没有任何配置情况下,请求会被拦截的原因! “
通过上面对自动配置分析,我们也能看出默认生效条件为:

1
2
3
4
5
6
7
8
9
10
11
class DefaultWebSecurityCondition extends AllNestedConditions {
DefaultWebSecurityCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@ConditionalOnClass({ SecurityFilterChain.class, HttpSecurity.class })
static class Classes {
}
@ConditionalOnMissingBean({ WebSecurityConfigurerAdapter.class, SecurityFilterChain.class })
static class Beans {
}
}
  • 条件一 classpath中存在 SecurityFilterChain.class, HttpSecurity.class
  • 条件二 没有自定义 WebSecurityConfigurerAdapter.class, SecurityFilterChain.class
    默认情况下,条件都是满足的。WebSecurityConfigurerAdapter 这个类极其重要,Spring核心配置都在这个类中:

    流程分析

1.请求 /hello接口,在引入spring security之后会先经过一些列过滤器
2.在请求到达FilterSecurityInterceptor时,发现请求并未认证。请求拦截下来,并抛出AccessDeniedException异常。
3.抛出 AccessDeniedException的异常会被ExceptionTranslationFilter捕获,这个Filter中会调用LoginUrlAuthenticationEntryPointtconpmence方法给客户端返回302,要求客户端进行重定向到/login页面
4.客户端发送 /login请求。
5 login 请求会再次被拦截器中DefautLoginPageGeneratingFilter拦截到,并在拦截器中返回生成登录页面。

就是通过这种方式,Spring Security默认讨滤器中生成了登录页面,井返回!


文章作者: YueYue
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 YueYue !
  目录