From eec283bc64f419989fe9e9e6490462d12ea31126 Mon Sep 17 00:00:00 2001 From: "DESKTOP-9KF7BEV\\desktop" Date: 2021年12月12日 21:36:54 +0800 Subject: [PATCH] add doc --- ...345円210円235円350円257円206円SpringSecurity.md" | 185 ++++++++ ...70345円277円203円351円200円273円350円276円221円.md" | 308 +++++++++++++ ...46273円244円345円231円250円FilterChainProxy.md" | 370 ++++++++++++++++ ...04350円277円207円346円273円244円345円231円250円.md" | 416 ++++++++++++++++++ .../springsecurity/config/CorsConfig.java | 2 +- .../springsecurity/config/SwaggerConfig.java | 2 +- .../config/WebSecurityConfig.java | 2 +- .../controller/LoginController.java | 2 +- .../controller/UserController.java | 2 +- .../com/bruis/springsecurity/dto/User.java | 2 +- .../springsecurity/model/HttpResult.java | 2 +- .../springsecurity/model/HttpStatus.java | 2 +- .../model/LoginRequestParam.java | 2 +- .../service/SysUserServiceImpl.java | 2 +- .../springsecurity/service/UserService.java | 2 +- .../bruis/springsecurity/utils/HttpUtils.java | 2 +- .../springsecurity/utils/JwtTokenUtils.java | 2 +- .../utils/security/GrantedAuthorityImpl.java | 2 +- .../security/JwtAuthenticationFilter.java | 2 +- .../security/JwtAuthenticationProvider.java | 2 +- .../security/JwtAuthenticationToken.java | 2 +- .../utils/security/JwtLoginFilter.java | 2 +- .../utils/security/JwtUserDetails.java | 2 +- .../utils/security/SecurityUtils.java | 2 +- .../security/UserDetailsServiceImpl.java | 2 +- 25 files changed, 1300 insertions(+), 21 deletions(-) create mode 100644 "Spring-Security/src/main/doc/SpringSecurity/SpringSecurity345円222円214円OAuth2357円274円210円344円270円200円357円274円211円342円200円224円342円200円224円 345円210円235円350円257円206円SpringSecurity.md" create mode 100644 "Spring-Security/src/main/doc/SpringSecurity/SpringSecurity345円222円214円OAuth2357円274円210円344円270円211円357円274円211円342円200円224円342円200円224円 WebSecurity345円273円272円351円200円240円346円240円270円345円277円203円351円200円273円350円276円221円.md" create mode 100644 "Spring-Security/src/main/doc/SpringSecurity/SpringSecurity345円222円214円OAuth2357円274円210円344円272円214円357円274円211円342円200円224円342円200円224円 345円256円211円345円205円250円350円277円207円346円273円244円345円231円250円FilterChainProxy.md" create mode 100644 "Spring-Security/src/main/doc/SpringSecurity/SpringSecurity345円222円214円OAuth2357円274円210円345円233円233円357円274円211円342円200円224円342円200円224円 FilterChainProxy350円277円207円346円273円244円345円231円250円351円223円276円344円270円255円347円232円204円345円207円240円344円270円252円351円207円215円350円246円201円347円232円204円350円277円207円346円273円244円345円231円250円.md" diff --git "a/Spring-Security/src/main/doc/SpringSecurity/SpringSecurity345円222円214円OAuth2357円274円210円344円270円200円357円274円211円342円200円224円342円200円224円 345円210円235円350円257円206円SpringSecurity.md" "b/Spring-Security/src/main/doc/SpringSecurity/SpringSecurity345円222円214円OAuth2357円274円210円344円270円200円357円274円211円342円200円224円342円200円224円 345円210円235円350円257円206円SpringSecurity.md" new file mode 100644 index 0000000..9f61fff --- /dev/null +++ "b/Spring-Security/src/main/doc/SpringSecurity/SpringSecurity345円222円214円OAuth2357円274円210円344円270円200円357円274円211円342円200円224円342円200円224円 345円210円235円350円257円206円SpringSecurity.md" @@ -0,0 +1,185 @@ + +- [前言](#前言) +- [正文](#正文) + - [1. 初识SpringSecurity](#1-初识springsecurity) + - [2. Spring Security项目核心jar包介绍](#2-spring-security项目核心jar包介绍) + - [3. SpringSecurity核心注解](#3-springsecurity核心注解) + - [3.1 @EnableWebSecurity](#31-enablewebsecurity) + - [3.2 @EnableGlobalAuthentication](#32-enableglobalauthentication) + - [3.3 @EnableGlobalMethodSecurity](#33-enableglobalmethodsecurity) + - [4. SpringSecurity核心组件](#4-springsecurity核心组件) + - [4.1 认证](#41-认证) + - [4.2 授权](#42-授权) + - [5. SpringSecurity中的Filter](#5-springsecurity中的filter) + - [5.1 FilterChainProxy以及其内部的Filter](#51-filterchainproxy以及其内部的filter) +- [总结](#总结) +- [参考](#参考) +- [相关文章](#相关文章) + +## 前言 +相信了解过SpringSecurity或者是OAuth2的读者,会发现网上会有非常多的相关文章,或是纯概念的,或是带有demo的,无论是哪种类型的文章,本人去阅读之后,对于整个框架的概念还是一知半解,也仅仅是实现了某些功能、某些效果而已,若遇到某些问题时无从下手,只能去百度去Google。这是因为对于SpringSecurity和OAuth2的知识没有一个整体概念的把握,知识体系没有形成系统,遂决定写一个关于SpringSecurity和OAuth2的系列专栏,在构建自己知识体系的同时还希望能帮助有同样困惑的同学。 + +**本专栏希望通过通俗易懂的语言来达到从入门概念、demo开始,由简入深,最终能达到深谙其底层源码的目的。** + +## 正文 +### 1. 初识SpringSecurity +对于SpringSecurity,可以由其官网中的介绍了解到其概述: +1. SpringSecurity就是一个功能强大且高定制化的身份验证和访问控制框架,它事实上就是一个保护基于Spring的应用框架安全性的标准。 +2. Spring Security是一个重点为Java应用程序提供身份验证和授权的框架。 +3. 与所有Spring项目一样,Spring Security的真正强大之处在于它可以很容易地扩展以满足定制需求 + +对于SpringSecurity的特性,可以总结为以下几点: +1. 对身份认证和授权提供全面和可扩展的支持。 +2. 防止会话固定、劫持请求、跨站点请求伪造等攻击。 +3. Servlet API的集成。 +4. 与Spring Web MVC的可选集成。 + +在进行代码编写时,先得对SpringSecurity有一个整体上的认识,等进行coding时才能知道对应代码的对应作用。 + +### 2. Spring Security项目核心jar包介绍 +在SpringSecurity继承的项目中,主要有四个核心的jar包: + +- spring-security-core.jar + SpringSecurity的核心包,任何SpringSecurity的功能都需要此包。 +- spring-security-web.jar + web工程必备,包含过滤器和相关的Web安全基础结构代码。 +- spring-security-config.jar + 用于解析xml配置文件,用到SpringSecurity的xml配置文件的就要用到此包。 + +由于spring-security-web.jar和spring-security-config.jar 都依赖了spring-security-core.jar,所以只需要导入spring-security-web.jar和spring-security-config.jar 即可。 + +> 本专栏系列文章相关demo版本: +> SpringBoot: 2.1.14.RELEASE + +``` + + + org.springframework.security + spring-security-config + 5.1.10.RELEASE + + + + org.springframework.security + spring-security-web + 5.1.10.RELEASE + +``` + +![在这里插入图片描述](https://img-blog.csdnimg.cn/20200712224312526.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0NvZGVyQnJ1aXM=,size_16,color_FFFFFF,t_70) + +不过,在SpringBoot中的spring-boot-starter-security中,就已经依赖好了spring-security-web.jar和spring-security-config.jar,直接拿来用即可。 + +![在这里插入图片描述](https://img-blog.csdnimg.cn/20200712224733553.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0NvZGVyQnJ1aXM=,size_16,color_FFFFFF,t_70) + +既然SpringBoot都已经定义好了spring-boot-starter-security,那就用spring-boot-starter-security吧,这样做的好处是能够方便SpringBoot统一SpringSecurity的版本。 + +### 3. SpringSecurity核心注解 +SpringBoot集成SpringSecurity需要配置几个配置文件,并且需要几个核心注解,下面来依次介绍这些注解。 + +#### 3.1 @EnableWebSecurity +@EnableWebSecurity是Spring Security用于启用Web安全的注解。典型的用法是该注解用在某个Web安全配置类上(实现了接口WebSecurityConfigurer或者继承自WebSecurityConfigurerAdapter)。 +``` +@Configuration +@EnableWebSecurity +public class WebSecurityConfig extends WebSecurityConfigurerAdapter { +... +} +``` + +![在这里插入图片描述](https://img-blog.csdnimg.cn/20200713110634549.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0NvZGVyQnJ1aXM=,size_16,color_FFFFFF,t_70) +从注解源码可以看到,该注解有如下作用: +1. 控制Spring Security是否使用调试模式(通过注解属性debug指定),默认为false,表示缺省不使用调试模式。 +2. 导入 WebSecurityConfiguration,用于配置Web安全过滤器FilterChainProxy,并创建过滤器链springSecurityFilterChain来保护你的Spring应用。 +3. 如果是Servlet 环境,导入WebMvcSecurityConfiguration。 +4. 如果是OAuth2环境(spring-security-oauth2-client),导入OAuth2ClientConfiguration。 +5. 使用注解@EnableGlobalAuthentication启用全局认证机制,即全局的AuthenticationManager,AuthenticationManager会在运行时对请求着进行身份验证。 + +#### 3.2 @EnableGlobalAuthentication +@EnableGlobalAuthentication是包含在了@EnableWebSecurity注解中的,作用通过启用认证管理器(AuthenticationManager)来启用全局认证机制。 +![在这里插入图片描述](https://img-blog.csdnimg.cn/20200726025353554.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0NvZGVyQnJ1aXM=,size_16,color_FFFFFF,t_70) +核心逻辑就在AuthenticationConfiguration类里。 + +在AuthenticationConfiguration在SpringSecurity中扮演者非常重要的作用,它内部包含了AuthenticationManager用于核心的认证工作。接下来将会重点讲解该类。 + + +#### 3.3 @EnableGlobalMethodSecurity +Spring Security默认是禁用注解的,要想开启注解,需要在继承WebSecurityConfigurerAdapter的类上加@EnableGlobalMethodSecurity注解,来判断用户对某个控制层的方法是否具有访问权限。 + +除此之外,还可以在@EnableGlobalMethodSecurity中添加几个属性。 + +1. @EnableGlobalMethodSecurity(securedEnabled=true)开启@Secured 注解过滤权限。 +2. @EnableGlobalMethodSecurity(jsr250Enabled=true)开启@RolesAllowed 注解过滤权限 。 +3. @EnableGlobalMethodSecurity(prePostEnabled=true)使用表达式时间方法级别的安全性 4个注解可用。 + - @PreAuthorize 在方法调用之前,基于表达式的计算结果来限制对方法的访问 + - @PostAuthorize 允许方法调用,但是如果表达式计算结果为false,将抛出一个安全性异常 + - @PostFilter 允许方法调用,但必须按照表达式来过滤方法的结果 + - @PreFilter 允许方法调用,但必须在进入方法之前过滤输入值 + + +### 4. SpringSecurity核心组件 +在SpringSecurity中,有着许多的组件包括AuthenticationManager、AccessDesicionManager和UsernamePasswordAuthenticationFilter等。 + +对于SpringSecurity来说,最大的两个问题就是:**认证(Authentication,即你是谁?)和授权(Authorization,允许你做什么?)**。SpringSecurity框架旨在将认证从授权中剥离出来,并也有适用于二者的策略和可扩展的设计。 + +#### 4.1 认证 +在SpringSecurity中,用于认证的主要接口是AuthenticationManager,它只有一个方法: +``` +public interface AuthenticationManager { + Authentication authenticate(Authentication authentication) + throws AuthenticationException; +} +``` + +AuthenticationManger最普遍的实现类是ProviderManager,而ProviderManager会将认证委托给AuthenticationProvider。 +![在这里插入图片描述](https://img-blog.csdnimg.cn/20200714010510964.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0NvZGVyQnJ1aXM=,size_16,color_FFFFFF,t_70) +![在这里插入图片描述](https://img-blog.csdnimg.cn/20200714010535442.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0NvZGVyQnJ1aXM=,size_16,color_FFFFFF,t_70) +AuthenticationProvider接口和AuthenticationManager相似,但是它有一个额外的方法允许查询它支持的Authentication方式: +``` +public interface AuthenticationProvider { + Authentication authenticate(Authentication authentication) + throws AuthenticationException; + + boolean supports(Class authentication); +} +``` + + +#### 4.2 授权 + +一旦认证成功,我们就可以进行授权了,它核心的策略就是AccessDecisionManager。同样的,它将授权逻辑全部委托给AccessDecisionVoter来实现。 + +一个AccessDecisionVoter考虑一个Authentication(代表一个Principal)和一个被ConfigAttributes装饰的安全对象,这里的ConfigAttributes就是一个包含了URL以及这个URL该有权限的对象的集合。 +``` +boolean supports(ConfigAttribute attribute); + +boolean supports(Class clazz); + +int vote(Authentication authentication, S object, + Collection attributes); +``` + +现在,已经拥有了认证和授权组件了,那么一个HTTP请求进入SpringSecurity应用时,经过过滤器链中都发生了哪些逻辑?接下来就看下SpringSecurity中过滤器链底层都发生了什么。 + +### 5. SpringSecurity中的Filter +除了认证和授权外,SpringSecurity的另外一个核心就是Servlet的Filter来实现的。先简单回顾下Servlet中Filter的调用原理。 +下图展示了处理单个HTTP请求的经典分层结构图: + ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200726104520271.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0NvZGVyQnJ1aXM=,size_16,color_FFFFFF,t_70) +客户端向服务器发起请求,然后Servlet容器(Tomcat)会根据URI来决定哪个Filter和哪个Servlet适用于这个请求,一个Servlet最多处理一个请求,过滤器是链式的,它们是有顺序的。事实上一个过滤器可以否决接下来的过滤器,上一个过滤器请求处理完就会动过调用Filter的doFilter()向下传递请求,知道请求最终到达Servlet处理完毕后,再过滤器以相反的顺序调用,再以Response返回给客户端。 + +#### 5.1 FilterChainProxy以及其内部的Filter + +回归正题,SpringSecurity在过滤器链中扮演的就是一个Filter,其类型是FilterChainProxy。但它又不是一个普通的Filter,因为FilterChainProxy中包含了额外的过滤器,每个过滤器都发挥特殊的作用。下面用一张图展示下FliterChainProxy中包含的过滤器链。 +![在这里插入图片描述](https://img-blog.csdnimg.cn/20200726110652665.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0NvZGVyQnJ1aXM=,size_16,color_FFFFFF,t_70) +在SpringBoot中,SpringSecurity的FilterChainProxy是以bean的形式注入到Spring容器中的,并且它是默认配置,所以在每次请求中都会存在,所以在SpringSecurity保护的应用中,每次请求都会经过FilterChainProxy。 + +## 总结 +本片文章粗略的讲解了SpringSecurity里的几个核心概念,包括:核心注解、认证和授权的核心组件以及SpringSecurity中的FilterChainProxy,整个SpringSecurity框架就是围绕着这几个核心概念运行,下面几章将会深入分析每个核心概念的底层运行机制。 + +## 参考 +- [《spring-security-architecture》](https://spring.io/guides/topicals/spring-security-architecture) +- [《Spring Security Reference》](https://docs.spring.io/spring-security/site/docs/current/reference/html5/#preface) + +## 相关文章 +- [从零开始系统学习SpringSecurity和OAuth2(二)—— 安全过滤器FilterChainProxy](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/SpringSecurity/%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E7%B3%BB%E7%BB%9F%E5%AD%A6%E4%B9%A0SpringSecurity%E5%92%8COAuth2%EF%BC%88%E4%BA%8C%EF%BC%89%E2%80%94%E2%80%94%20%E5%AE%89%E5%85%A8%E8%BF%87%E6%BB%A4%E5%99%A8FilterChainProxy.md) +- [从零开始系统学习SpringSecurity和OAuth2(三)—— WebSecurity建造核心逻辑](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/SpringSecurity/%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E7%B3%BB%E7%BB%9F%E5%AD%A6%E4%B9%A0SpringSecurity%E5%92%8COAuth2%EF%BC%88%E4%B8%89%EF%BC%89%E2%80%94%E2%80%94%20WebSecurity%E5%BB%BA%E9%80%A0%E6%A0%B8%E5%BF%83%E9%80%BB%E8%BE%91.md) \ No newline at end of file diff --git "a/Spring-Security/src/main/doc/SpringSecurity/SpringSecurity345円222円214円OAuth2357円274円210円344円270円211円357円274円211円342円200円224円342円200円224円 WebSecurity345円273円272円351円200円240円346円240円270円345円277円203円351円200円273円350円276円221円.md" "b/Spring-Security/src/main/doc/SpringSecurity/SpringSecurity345円222円214円OAuth2357円274円210円344円270円211円357円274円211円342円200円224円342円200円224円 WebSecurity345円273円272円351円200円240円346円240円270円345円277円203円351円200円273円350円276円221円.md" new file mode 100644 index 0000000..3d6cfb6 --- /dev/null +++ "b/Spring-Security/src/main/doc/SpringSecurity/SpringSecurity345円222円214円OAuth2357円274円210円344円270円211円357円274円211円342円200円224円342円200円224円 WebSecurity345円273円272円351円200円240円346円240円270円345円277円203円351円200円273円350円276円221円.md" @@ -0,0 +1,308 @@ + + - [前言](#前言) + - [正文](#正文) + - [1. AbstractConfiguredSecurityBuilder中安全配置类](#1-abstractconfiguredsecuritybuilder中安全配置类) + - [2. AbstractConfiguredSecurityBuilder的doBuild()方法](#2-abstractconfiguredsecuritybuilder的dobuild方法) + - [3. WebSecurity中的performBuild()方法](#3-websecurity中的performbuild方法) +- [总结](#总结) +- [相关文章](#相关文章) + +## 前言 +相信了解过SpringSecurity或者是OAuth2的读者,会发现网上会有非常多的相关文章,或是纯概念的,或是带有demo的,无论是哪种类型的文章,本人去阅读之后,对于整个框架的概念还是一知半解,也仅仅是实现了某些功能、某些效果而已,若遇到某些问题时无从下手,只能去百度去Google。这是因为对于SpringSecurity和OAuth2的知识没有一个整体概念的把握,知识体系没有形成系统,遂决定写一个关于SpringSecurity和OAuth2的系列专栏,在建造自己知识体系的同时还希望能帮助有同样困惑的同学。 + +============== 分割线 ============== + +上一章已经讲解到了SpringSecurity用到了建造者模式来建造FilterChainProxy。本片就来详细分析下WebSecurity的核心逻辑以及AbstractConfiguredSecurityBuilder的doBuild()方法。 + +## 正文 + +### 1. AbstractConfiguredSecurityBuilder中安全配置类 +SpringSecurity通过SecurityConfigurer来建造FilterChainProxy,建造前还需要进行配置。因此AbstractConfiguredSecurityBuilder还需要注入配置组件SecurityConfigurer,初始化配置组件SecurityConfigurer,调用SecurityConfigurer的configure方法。 + +在AbstractConfiguredSecurityBuilder类中,看下安全配置类的定义:‘ + +``` +private final LinkedHashMap>, List>> configurers = new LinkedHashMap>, List>>(); +``` +这是定义的安全配置器的子类Map集合,这个configurers就是用于初始化以及配置FilterChainProxy中的filters用的。Map集合中,Key是SecurityConfigurer的子类的Class类型,Value是SecurityConfigurer的list集合。 + +作为一个成员变量,自然会有方法从外部注入安全配置类。在AbstractConfiguredSecurityBuilder的类中,定义了add方法。 + +``` + private > void add(C configurer) throws Exception { + Assert.notNull(configurer, "configurer cannot be null"); + // 获取安全配置类的Class类型 + Class> clazz = (Class>) configurer + .getClass(); + // 同步去操作安全配置类集合 + synchronized (configurers) { + // 查看建造状态是否是已经配置 + if (buildState.isConfigured()) { + throw new IllegalStateException("Cannot apply " + configurer + + " to already built object"); + } + // 如果allowConfigurersOfSameType为true,则从configurers集合中获取clazz类型的安全配置类集合 + List> configs = allowConfigurersOfSameType ? this.configurers + .get(clazz) : null; + if (configs == null) { + // 初始化安全配置类结合 + configs = new ArrayList>(1); + } + // 将安全配置类添加至configs的list集合中 + configs.add(configurer); + // 以clazz为key,configs为value存入configurers的LinkedHashMap集合中 + this.configurers.put(clazz, configs); + if (buildState.isInitializing()) { + this.configurersAddedInInitializing.add(configurer); + } + } + } + +``` + +通过IDEA来查看下哪些地方调用了add方法 +![在这里插入图片描述](https://img-blog.csdnimg.cn/2020080918440196.png) + +看下apply方法 +``` + // 传入的C是SecurityConfigurerAdapter的子类, + public > C apply(C configurer) + throws Exception { + // 传入objectPostProcessor,该对象用于创建各种"实例",具体什么作用下问会讲解,请留意 + configurer.addObjectPostProcessor(objectPostProcessor); + // 将当前对象设置为建造者 + configurer.setBuilder((B) this); + // 调用add方法,向configurers集合中添加configurer + add(configurer); + return configurer; + } + +``` + +继续查看apply方法有哪些地方调用了的 +![在这里插入图片描述](https://img-blog.csdnimg.cn/20200809224904457.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0NvZGVyQnJ1aXM=,size_16,color_FFFFFF,t_70) +图上的HttpSecurity的getOrApply方法值得注意一下,查看其方法 +``` + private > C getOrApply( + C configurer) throws Exception { + // 从configurers集合中获取安全配置类 + C existingConfig = (C) getConfigurer(configurer.getClass()); + if (existingConfig != null) { + // 如果存在则直接返回该安全配置类 + return existingConfig; + } + // 如果不存在则调用apply方法去应用该安全配置类,并缓存到configurers集合中 + return apply(configurer); + } + +``` + +getOrApply方法主要是从configurers集合中获取配置类,如果存在则直接返回,否则则应用该配置类。 + +继续查看getOrApply有哪些地方在调用。这下终于看到了安全配置类了。 + +![在这里插入图片描述](https://img-blog.csdnimg.cn/20200809225003886.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0NvZGVyQnJ1aXM=,size_16,color_FFFFFF,t_70) +这里有一个问题值得思考,这些配置类到底是干嘛用的?这里就以ExpressionUrlAuthorizationConfigurer配置类为例,看下其类内部configure方法逻辑。 + +找了一圈,发现configure的实现是在ExpressionUrlAuthorizationConfigurer的抽象父类AbstractInterceptUrlConfigurer定义的。 + +``` + @Override + public void configure(H http) throws Exception { + // 创建元数据,该抽象方法由ExpressionUrlAuthorizationConfigurer定义,返回一个ExpressionBasedFilterInvocationSecurityMetadataSource对象 + FilterInvocationSecurityMetadataSource metadataSource = createMetadataSource(http); + // 如果配置失败,则数据源配置失败 + if (metadataSource == null) { + return; + } + // 创建一个FilterSecurityInterceptor对象 + FilterSecurityInterceptor securityInterceptor = createFilterSecurityInterceptor( + http, metadataSource, http.getSharedObject(AuthenticationManager.class)); + if (filterSecurityInterceptorOncePerRequest != null) { + securityInterceptor + .setObserveOncePerRequest(filterSecurityInterceptorOncePerRequest); + } + // 通过objectPostProcessor创建一个securityInterceptor实例对象 + securityInterceptor = postProcess(securityInterceptor); + http.addFilter(securityInterceptor); + // 将实例对象存入SharedObject缓存中 + http.setSharedObject(FilterSecurityInterceptor.class, securityInterceptor); + } + +``` + +从ExpressionUrlAuthorizationConfigurer的抽象父类AbstractInterceptUrlConfigurer可以看出,configure方法中调用了一个postProcess方法,该方法用于生成FilterSecurityInterceptor对象,在本系列文章前面第一章以及列出来的FilterChainProxy拦截器链对象,可以知道FilterSecurityInterceptor对象就属于FilterChainProxy拦截器链中的对象,并且是处在最后一个位置。 + +到此处,安全配置类的作用已经提现出来了,就是向sharedObject中添加过滤器,并最终注入到FilterChainProxy中。 + +### 2. AbstractConfiguredSecurityBuilder的doBuild()方法 +随着configurers集合元素的注入,下面就是进行建造工作,调用doBuild()方法。 + +``` + @Override + protected final O doBuild() throws Exception { + synchronized (configurers) { + // 设置建造状态为初始化中 + buildState = BuildState.INITIALIZING; + + // 进行初始化前的工作 + beforeInit(); + // 初始化 + init(); + + // 设置建造状态为配置中 + buildState = BuildState.CONFIGURING; + + // 配置前的工作 + beforeConfigure(); + // 调用配置 + configure(); + + // 设置建造状态为建造中 + buildState = BuildState.BUILDING; + + // 执行建造核心逻辑 + O result = performBuild(); + + // 设置建造状态为已建造 + buildState = BuildState.BUILT; + + return result; + } + } + +``` +beforeInit()和beforeConfigure()是一个空方法体,没有逻辑。 +``` + protected void beforeInit() throws Exception { + } + protected void beforeConfigure() throws Exception { + } +``` + +``` + private void init() throws Exception { + // 调用getConfigurers()方法获取this.configurers的所有value值,并以List集合的形式返回 + Collection> configurers = getConfigurers(); + // 遍历configurers,并依次调用安全配置类的init方法 + for (SecurityConfigurer configurer : configurers) { + // 调用安全配置类的init初始化方法 + configurer.init((B) this); + } + + for (SecurityConfigurer configurer : configurersAddedInInitializing) { + configurer.init((B) this); + } + } +``` + +这需要注意的是,init和configure方法是有接口SecurityConfigurer接口定义的,但其实现以由SecurityConfigurerAdapter这个抽象的适配器类实现了,所以最终的安全配置类可重写init()和configure(),也可以不重写。所以可以发现,很多安全配置类是没有重写init()方法的。 + + +**接着就是configure()方法的调用** + +``` + private void configure() throws Exception { + // 调用getConfigurers()方法获取this.configurers的所有value值,并以List集合的形式返回 + Collection> configurers = getConfigurers(); + + for (SecurityConfigurer configurer : configurers) { + // 遍历configurers,然后依次调用其configure方法 + configurer.configure((B) this); + } + } +``` + +在前面已经已经以ExpressionUrlAuthorizationConfigurer为例,去查看安全配置类configure方法的作用,都知道安全配置类的configure方法是用于将过滤器添加到sharedObject缓存中。 + +经过init()和configure()方法的执行,以及可以开始进行建造工作了,因而调用performBuild()方法执行建造过程。 + +``` + protected abstract O performBuild() throws Exception; +``` +可以看到在AbstractConfiguredSecurityBuilder中,performBuild是以抽象方法的形式存在的,所以实现逻辑都在其子类中。 + +![在这里插入图片描述](https://img-blog.csdnimg.cn/20200810112226524.png) + +这里HttpSecurity和WebSecurity类名这么像,有什么异同呢? + +> HttpSecurity + +HttpSecurity允许为特定的http请求配置基于Web的安全特性。默认情况下,HttpSecurity将应用于所有请求,但是如果需要限制请求,则可以通过RequestMatcher或其他类似方法来进行限制。 + +> WebSecurity + +而WebSecurity是通过WebSecurityConfiguration创建的,用于去创建类FilterChainProxy。在SpringSecurity中,WebSecurity通过HttpSecurity来对某些请求进行拦截限制。 + +> 区别 + +WebSecurity用于建造FilterChainProxy,WebSecurity是包含HttpSecurity的一个更大的概念,而HttpSecurity仅是用于建造FilterChainProxy中的一个SecurityFilterChain。 + + +### 3. WebSecurity中的performBuild()方法 +WebSecurity重写了AbstractConfiguredSecurityBuilder的perfomBuild()方法,核心逻辑如下: + +``` + @Override + protected Filter performBuild() throws Exception { + Assert.state( + !securityFilterChainBuilders.isEmpty(), + () -> "At least one SecurityBuilder needs to be specified. " + + "Typically this done by adding a @Configuration that extends WebSecurityConfigurerAdapter. " + + "More advanced users can invoke " + + WebSecurity.class.getSimpleName() + + ".addSecurityFilterChainBuilder directly"); + // 获取chain大小 + int chainSize = ignoredRequests.size() + securityFilterChainBuilders.size(); + // 设置安全过滤器链集合 + List securityFilterChains = new ArrayList( + chainSize); + // 遍历igoredRequests集合,该集合表示存储着忽略拦截的请求 + for (RequestMatcher ignoredRequest : ignoredRequests) { + securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest)); + } + for (SecurityBuilder securityFilterChainBuilder : securityFilterChainBuilders) { + // 先调用securityFilterChainBuilder执行build()方法,然后添加进securityFilterChains集合中。之列build()方法是去迭代执行doBuild()逻辑。 + securityFilterChains.add(securityFilterChainBuilder.build()); + } + + // 创建一个FilterChainProxy对象,传入一个securityFilterChains对象 + FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains); + if (httpFirewall != null) { + // 设置http防火墙属性 + filterChainProxy.setFirewall(httpFirewall); + } + filterChainProxy.afterPropertiesSet(); + + Filter result = filterChainProxy; + if (debugEnabled) { + logger.warn("\n\n" + + "********************************************************************\n" + + "********** Security debugging is enabled. *************\n" + + "********** This may include sensitive information. *************\n" + + "********** Do not use in a production system! *************\n" + + "********************************************************************\n\n"); + result = new DebugFilter(filterChainProxy); + } + + // 执行一个线程 + postBuildAction.run(); + return result; + } + +``` + +在performBuild()方法中,会执行securityFilterChainBuilder的build()方法,该方法又会去迭代执行doBuild()方法,最终返回一个SecurityFilterChain对象。 + + +综上,本文对WebSecurity的建造逻辑进行了概括性的讲解,更加深入的内容还需要进行代码debug跟踪深入查看,这样才能加深对于WebSecurity的理解。 + +## 总结 +- 安全配置类是在HttpSecurity、XXConfigurerAdapter等类中进行添加 +- WebSecurity和HttpSecurity区别在于,WebSecurity是用于建造FilterChainProxy的,它是包含HttpSecurity;而HttpSecurity是用于对请求进行限制,同时还用于建造DefaultSecurityFilterChain +- 安全配置类主要是通过configure方法向sharedObject缓存对象中添加过滤器,并最终添加进FilterChainProxy过滤器链中 +- WebSecurity建造FilterChainProxy的核心逻辑可以笼统的分为三步:安全配置了初始化、安全配置类configure的调用、performBuild的调用 + +## 相关文章 +- [从零开始系统学习SpringSecurity和OAuth2(一)—— 初识SpringSecurity](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/SpringSecurity/%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E7%B3%BB%E7%BB%9F%E5%AD%A6%E4%B9%A0SpringSecurity%E5%92%8COAuth2%EF%BC%88%E4%B8%80%EF%BC%89%E2%80%94%E2%80%94%20%E5%88%9D%E8%AF%86SpringSecurity.md) +- [从零开始系统学习SpringSecurity和OAuth2(二)—— 安全过滤器FilterChainProxy](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/SpringSecurity/%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E7%B3%BB%E7%BB%9F%E5%AD%A6%E4%B9%A0SpringSecurity%E5%92%8COAuth2%EF%BC%88%E4%BA%8C%EF%BC%89%E2%80%94%E2%80%94%20%E5%AE%89%E5%85%A8%E8%BF%87%E6%BB%A4%E5%99%A8FilterChainProxy.md) \ No newline at end of file diff --git "a/Spring-Security/src/main/doc/SpringSecurity/SpringSecurity345円222円214円OAuth2357円274円210円344円272円214円357円274円211円342円200円224円342円200円224円 345円256円211円345円205円250円350円277円207円346円273円244円345円231円250円FilterChainProxy.md" "b/Spring-Security/src/main/doc/SpringSecurity/SpringSecurity345円222円214円OAuth2357円274円210円344円272円214円357円274円211円342円200円224円342円200円224円 345円256円211円345円205円250円350円277円207円346円273円244円345円231円250円FilterChainProxy.md" new file mode 100644 index 0000000..f3d7189 --- /dev/null +++ "b/Spring-Security/src/main/doc/SpringSecurity/SpringSecurity345円222円214円OAuth2357円274円210円344円272円214円357円274円211円342円200円224円342円200円224円 345円256円211円345円205円250円350円277円207円346円273円244円345円231円250円FilterChainProxy.md" @@ -0,0 +1,370 @@ +- [前言](#前言) +- [正文](#正文) + - [1. FilterChainProxy什么时候注入Spring容器中的](#1--filterchainproxy什么时候注入spring容器中的) + - [2. WebSecurityConfiguration类](#2-websecurityconfiguration类) + - [3. WebSecurity类](#3-websecurity类) + - [4. AbstractConfiguredSecurityBuilder类](#4-abstractconfiguredsecuritybuilder类) + - [5. SecurityConfigurer类](#5-securityconfigurer类) + - [6. doBuild()方法](#6-dobuild方法) + - [7. WebSecurity什么时候被创建的?](#7-websecurity什么时候被创建的) +- [总结](#总结) +- [参考](#参考) +- [相关文章](#相关文章) + + +## 前言 +相信了解过SpringSecurity或者是OAuth2的读者,会发现网上会有非常多的相关文章,或是纯概念的,或是带有demo的,无论是哪种类型的文章,本人去阅读之后,对于整个框架的概念还是一知半解,也仅仅是实现了某些功能、某些效果而已,若遇到某些问题时无从下手,只能去百度去Google。这是因为对于SpringSecurity和OAuth2的知识没有一个整体概念的把握,知识体系没有形成系统,遂决定写一个关于SpringSecurity和OAuth2的系列专栏,在建造自己知识体系的同时还希望能帮助有同样困惑的同学。 + +**本专栏希望通过通俗易懂的语言来达到从入门概念、demo开始,由简入深,最终能达到深谙其底层源码的目的。** + +============== 分割线 ============== + +上篇文章粗略的讲解了SpringSecurity里的几个核心概念,本篇文章就来深扒一下安全过滤器FilterChainProxy的底层原理。 + +## 正文 +### 1. FilterChainProxy什么时候注入Spring容器中的 +在SpringSecurity中,FilterChainProxy是作为整个框架的核心,它是一个过滤器。在SpringSecurity中,一个过滤器名字叫springSecurityFilterChain,它的类型就是FilterChainProxy。 + +在@EnableWebSecurity注解中,@Import了一个关键类:WebSecurityConfiguration。 + +首先,WebSecurityConfiguration实现了ImportAware和BeanClassLoaderAware接口,分别实现了setImportMetadata()和setBeanClassLoader() + +setImportMetadata()方法的作用是注入注解元数据。 +``` + public void setImportMetadata(AnnotationMetadata importMetadata) { + // 从注入的importMetadata中获取@EnableWebSecurity注解map值 + Map enableWebSecurityAttrMap = importMetadata + .getAnnotationAttributes(EnableWebSecurity.class.getName()); + // 将map值封装成AnnotationAttributes + AnnotationAttributes enableWebSecurityAttrs = AnnotationAttributes + .fromMap(enableWebSecurityAttrMap); + // 从AnnotationAttributes中获取debug属性值 + debugEnabled = enableWebSecurityAttrs.getBoolean("debug"); + if (webSecurity != null) { + // 将debugEnabled存到WebSecurity属性中 + webSecurity.debug(debugEnabled); + } + } +``` +setBeanClassLoader方法作用就是注入类加载器ClassLoader。 +``` + public void setBeanClassLoader(ClassLoader classLoader) { + this.beanClassLoader = classLoader; + } +``` + +废话不多说,看下FilterChainProxy是什么时候注入到Spring容器中的。 + +答案就在WebSecurityConfiguration的springSecurityFilterChain()方法中 + +``` + // AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME的值是:springSecurityFilterChain + // 所以springSecurityFilterChain()的作用就是想Spring容器中注入一个名为springSecurityChain的bean。 + @Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME) + public Filter springSecurityFilterChain() throws Exception { + // 判断是否有webSecurityConfigurers + boolean hasConfigurers = webSecurityConfigurers != null + && !webSecurityConfigurers.isEmpty(); + // 如果没有配置,则通过objectObjectPostProcessor来生成一个新的WebSecurityConfigurerAdapter + if (!hasConfigurers) { + WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor + .postProcess(new WebSecurityConfigurerAdapter() { + }); + webSecurity.apply(adapter); + } + return webSecurity.build(); + } +``` + +### 2. WebSecurityConfiguration类 +在深入springSecurityFilterChain()方法底层原理之前,需要先了解WebSecurityConfiguration中几个重要的成员变量。 +``` +@Configuration +public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware { + + // WebSecurity是用于建造FilterChainProxy过滤器的核心类 + private WebSecurity webSecurity; + + // 读取@EnableWebSecurity中的注解属性debugEnabled的值 + private Boolean debugEnabled; + + // SecurityConfigurer属性值集合 + private List> webSecurityConfigurers; + + // 类加载器 + private ClassLoader beanClassLoader; + + // 省略 +} +``` + +### 3. WebSecurity类 +第一个重要的类就是WebSecurity。 + +由WebSecurity的类注释可以了解到: + +- WebSecurity是由WebSecurityConfiguration创建的,用于创建FilterChainProxy +- FilterChainProxy在SpringSecurity中,以springSecurityFilterChainProxy的bean名称暴露于容器之中的 +- 可以通过基础抽象类WebSecurityConfigurerAdapter来定制化WebSecurity + +由于@EnableWebSecurity导入了WebSecurityConfiguration,因而WebSecurity也被创建。 + +我们先来看下WebSecurity的类结构: + +``` +public final class WebSecurity extends + AbstractConfiguredSecurityBuilder implements + SecurityBuilder, ApplicationContextAware { + // 省略 +} +``` + +首先,WebSecurity是一个final修饰的类,即该类: + +- 不能被继承,没有子类 +- 该类方法都为final + +其次,WebSecurity继承了AbstractConfiguredSecurityBuilder。继续跟进看下AbstractConfiguredSecurityBuilder这个类有什么作用? + +### 4. AbstractConfiguredSecurityBuilder类 +由其类名:AbstractConfiguredSecurityBuilder就可以知道,该类是一个抽象类,作为抽象类,必然会抽象出abstract方法让子类去实现。浏览AbstractConfiguredSecurityBuilder的方法定义,可以看到它内部只定义了一个抽象方法: +``` +protected abstract O performBuild() throws Exception; +``` + +这个方法会在建造FilterChainProxy时有使用到,这里先留个印象。 + +回到AbstractConfiguredSecurityBuilder类定义 +``` +public abstract class AbstractConfiguredSecurityBuilder> + extends AbstractSecurityBuilder { + // 省略 +} +``` +可以知道它继承了AbstractSecurityBuilder,但自身带的一个泛型>让人捉摸不透是啥意思。O是啥?B又是啥? + +这里其实是用到了**建造者模式**。 + +目光聚焦到SecurityBuilder这个接口 + +``` +// 安全建造者 +// 顾名思义是一个builder建造者,创建并返回一个类型为O的对象 +public interface SecurityBuilder { + O build() throws Exception; +} +``` + +``` +// 抽象安全建造者 +public abstract class AbstractSecurityBuilder implements SecurityBuilder { + private AtomicBoolean building = new AtomicBoolean(); + private O object; + + // build建造方法被final修饰,不允许子类覆盖。 + public final O build() throws Exception { + if (this.building.compareAndSet(false, true)) { + this.object = doBuild(); + return this.object; + } + throw new AlreadyBuiltException("This object has already been built"); + } + + // 子类需要重写doBuild()方法 + protected abstract O doBuild() throws Exception; +} +``` +所以B extends SecurityBuilder就是指B是SecurityBuilder的子类,用于建造O。 + +从WebSecurity中的类定义可以发现 +``` +AbstractConfiguredSecurityBuilder +``` + +AbstractConfiguredSecurityBuilder作用就是通过WebSecurity这个建造者建造出一个Filter,而这个Filter就是FilterChainProxy。 + +**因此O就是指FilterChainProxy,B就是指的安全建造者WebSecurity。** + +


+ +再来看下AbstractConfiguredSecurityBuilder的成员变量。 + +``` +public abstract class AbstractConfiguredSecurityBuilder> + extends AbstractSecurityBuilder { + private final Log logger = LogFactory.getLog(getClass()); + + // 安全配置器的子类集合,这个configurers就是用于初始化以及配置FilterChainProxy中的filters用的 + private final LinkedHashMap>, List>> configurers = new LinkedHashMap(); + // 初始化期间添加的配置类 + private final List> configurersAddedInInitializing = new ArrayList(); + // 缓存 + private final Map, Object> sharedObjects = new HashMap(); + + private final boolean allowConfigurersOfSameType; + + // 建造者的建造状态,初始状态为:未建造 + private BuildState buildState = BuildState.UNBUILT; + + // 用于生成bean + private ObjectPostProcessor objectPostProcessor; + // 省略 +} +``` + + +### 5. SecurityConfigurer类 +SecurityConfigurer是一个接口,它就是指的安全配置器,看下它的类定义信息。 + +- SecurityConfigurer允许用于配置安全建造者(SecurityBuilder) +- 安全建造者(SecurityBuilder)在初始化时调用init方法,同时会初始化所有的SecurityConfigurer +- 安全建造者(SecurityBuilder)在调用configure方法时,同时会调用所有SecurityConfigurer的configure + +``` +public interface SecurityConfigurer> { + + void init(B builder) throws Exception; + + void configure(B builder) throws Exception; +} +``` + +SecurityConfigurer> 这个该怎么理解呢? + +这里同上,O指的FilterChainProxy,B指的SecurityBuilder。 + +那么这里的SecurityConfigurer到底有什么作用呢? + +> 答:SecurityConfigurer就是用于向FilterChainProxy中添加过滤器。 + +

+ +``` +private final LinkedHashMap>, List>> configurers = new LinkedHashMap(); +``` +所以configurers就是以建造者为key,各种配置类为value的一个LinkedHashMap。 + +### 6. doBuild()方法 +下面来看下AbstractConfiguredSecurityBuilder的doBuild()方法。doBuild()方法在之前提到过,它是AbstractSecurityBuilder抽象出来的方法,由AbstractConfiguredSecurityBuilder类来实现。这个方法会通过使用SecurityConfigurer去执行建造逻辑,建造逻辑主要分为五大步: + +1. 执行初始化前的所需做的额外工作(beforeInit()) +2. 执行初始化(init()) +3. 执行配置前所需的额外工作(beforeConfigure()) +4. 执行配置调用(configure) +5. **执行建造(performBuild())** + +``` + @Override + protected final O doBuild() throws Exception { + synchronized (configurers) { + // 标记建造状态为初始化中 + buildState = BuildState.INITIALIZING; + + beforeInit(); + init(); + + // 标记正在配置 + buildState = BuildState.CONFIGURING; + + beforeConfigure(); + configure(); + + // 标记正在建造 + buildState = BuildState.BUILDING; + + O result = performBuild(); + + // 标记以及建造完成 + buildState = BuildState.BUILT; + + return result; + } + } + +``` + +performBuild()方法是AbstractConfiguredSecurityBuilder提供的抽象方法,具体有其子类:HttpSecurity和WebSecurity来实现。 + +### 7. WebSecurity什么时候被创建的? +答案就是在WebSecurityConfiguration的setFilterChainProxySecurityConfiguere()方法里。 +``` + @Autowired(required = false) + public void setFilterChainProxySecurityConfigurer( + ObjectPostProcessor objectPostProcessor, + @Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List> webSecurityConfigurers) + throws Exception { + + // 通过objectPostProcessor来创建一个WebSecurity,同时将objectPostProcessor传进WebSecurity对象中 + webSecurity = objectPostProcessor + .postProcess(new WebSecurity(objectPostProcessor)); + // 从@EnableWebSecurity中获取debugEnabled注解值,是否开启debug模式 + if (debugEnabled != null) { + webSecurity.debug(debugEnabled); + } + + // 对webSecurityConfigurers进行排序 + webSecurityConfigurers.sort(AnnotationAwareOrderComparator.INSTANCE); + + Integer previousOrder = null; + Object previousConfig = null; + + for (SecurityConfigurer config : webSecurityConfigurers) { + Integer order = AnnotationAwareOrderComparator.lookupOrder(config); + if (previousOrder != null && previousOrder.equals(order)) { + throw new IllegalStateException( + "@Order on WebSecurityConfigurers must be unique. Order of " + + order + " was already used on " + previousConfig + ", so it cannot be used on " + + config + " too."); + } + previousOrder = order; + previousConfig = config; + } + + // 遍历webSecurityConfigurers + for (SecurityConfigurer webSecurityConfigurer : webSecurityConfigurers) { + // 添加进WebSecurity中的配置器Map中 + webSecurity.apply(webSecurityConfigurer); + } + this.webSecurityConfigurers = webSecurityConfigurers; + } +``` +方法中的webSecurityConfigurers是通过了@Value注解来注入的bean集合,@Value表达式中又包含了一个autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()的调用。 + +进入AutowiredWebSecurityConfigurersIgnoreParents类,查看其方法getWebSecurityConfigurers()方法。 +``` + public List> getWebSecurityConfigurers() { + // 初始化webSecurityConfigurers集合 + List> webSecurityConfigurers = new ArrayList(); + // 通过beanFactory来获取所有注入Spring容器中的WebSecurityConfigurer子类,实际就是WebSecurityConfigurer + Map beansOfType = beanFactory + .getBeansOfType(WebSecurityConfigurer.class); + for (Entry entry : beansOfType.entrySet()) { + // 遍历出bean对象 + webSecurityConfigurers.add(entry.getValue()); + } + return webSecurityConfigurers; + } +``` + +## 总结 +全文讲解了FilterChainProxy以及WebSecurityConfiguration,但是对于doBuild()方法没有进行细致的分析讲解,由于doBuild()的逻辑比较复杂,将在下一章进行细致分析。 + +在WebSecurityConfiguration中,需要关注两个方法: + +1. springSecurityFilterChain()方法 +2. setFilterChainProxySecurityConfigurer()方法 + +**springSecurityFilterChain方法用于通过安全建造器WebSecurity来建造出FilterChainProxy。** + +**setFilterChainProxySecurityConfigurer()方法是用于创建出安全建造起WebSecurity。** + +**在SpringSecurity中,对于WebSecurity,使用了建造者设计模式。** + + + +## 参考 +- [深入浅出Spring Security(一):三句话解释框架原理](https://blog.csdn.net/zimou5581/article/details/102457672) + +## 相关文章 +- [从零开始系统学习SpringSecurity和OAuth2(一)—— 初识SpringSecurity](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/SpringSecurity/%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E7%B3%BB%E7%BB%9F%E5%AD%A6%E4%B9%A0SpringSecurity%E5%92%8COAuth2%EF%BC%88%E4%B8%80%EF%BC%89%E2%80%94%E2%80%94%20%E5%88%9D%E8%AF%86SpringSecurity.md) +- [从零开始系统学习SpringSecurity和OAuth2(三)—— WebSecurity建造核心逻辑](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/SpringSecurity/%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E7%B3%BB%E7%BB%9F%E5%AD%A6%E4%B9%A0SpringSecurity%E5%92%8COAuth2%EF%BC%88%E4%B8%89%EF%BC%89%E2%80%94%E2%80%94%20WebSecurity%E5%BB%BA%E9%80%A0%E6%A0%B8%E5%BF%83%E9%80%BB%E8%BE%91.md) diff --git "a/Spring-Security/src/main/doc/SpringSecurity/SpringSecurity345円222円214円OAuth2357円274円210円345円233円233円357円274円211円342円200円224円342円200円224円 FilterChainProxy350円277円207円346円273円244円345円231円250円351円223円276円344円270円255円347円232円204円345円207円240円344円270円252円351円207円215円350円246円201円347円232円204円350円277円207円346円273円244円345円231円250円.md" "b/Spring-Security/src/main/doc/SpringSecurity/SpringSecurity345円222円214円OAuth2357円274円210円345円233円233円357円274円211円342円200円224円342円200円224円 FilterChainProxy350円277円207円346円273円244円345円231円250円351円223円276円344円270円255円347円232円204円345円207円240円344円270円252円351円207円215円350円246円201円347円232円204円350円277円207円346円273円244円345円231円250円.md" new file mode 100644 index 0000000..dc1387b --- /dev/null +++ "b/Spring-Security/src/main/doc/SpringSecurity/SpringSecurity345円222円214円OAuth2357円274円210円345円233円233円357円274円211円342円200円224円342円200円224円 FilterChainProxy350円277円207円346円273円244円345円231円250円351円223円276円344円270円255円347円232円204円345円207円240円344円270円252円351円207円215円350円246円201円347円232円204円350円277円207円346円273円244円345円231円250円.md" @@ -0,0 +1,416 @@ +## 前言 +相信了解过SpringSecurity或者是OAuth2的读者,会发现网上会有非常多的相关文章,或是纯概念的,或是带有demo的,无论是哪种类型的文章,本人去阅读之后,对于整个框架的概念还是一知半解,也仅仅是实现了某些功能、某些效果而已,若遇到某些问题时无从下手,只能去百度去Google。这是因为对于SpringSecurity和OAuth2的知识没有一个整体概念的把握,知识体系没有形成系统,遂决定写一个关于SpringSecurity和OAuth2的系列专栏,在建造自己知识体系的同时还希望能帮助有同样困惑的同学。 + +============== 分割线 ============== + +本章重点介绍一下FilterChainProxy过滤器链中的SecurityContextPersistenceFilter以及ExceptionTranslationFilter。 + +## 正文 +在系列文章中的第一篇中,已经用一张图介绍了FilterChainProxy在整个Filter中的所处位置以及包含在FilterChainProxy的各种Filter。 +地址如下: + +[从零开始系统学习SpringSecurity和OAuth2(一)—— 初识SpringSecurity](https://blog.csdn.net/CoderBruis/article/details/107297547) + +本篇文章就讲解一下SecurityContextPersistenceFilter、ExceptionTranslationFilter和FilterSecurityInterceptor这三个过滤器,注意这里的FilterSecurityInterceptor就是一个过滤器。 + +### 1. SecurityContextPersistenceFilter +这个过滤器是FilterChainProxy过滤器链的中第一个调用的,先看下它的官方类注释的内容,总结为以下几点: +1. 在请求之前,使用从已配置的SecurityContextRepository中获取的认证信息来填充SecurityContextHolder,在请求完成后清除上下文所有者。 +2. 默认情况下,SecurityContextRepository使用的是HttpSessionSecurityContextRepository作为实现类,有关于HttpSession的配置选项信息请查看HttpSessionSecurityContextRepository。 +3. 这个过滤器会在每次请求时都会调用,为的是解决servlet容器的兼容性(特别是Weblogic)。 +4. 这个过滤器必须在任何认证处理机制调用前执行,例如BASIC、CAS认证处理过滤器等都期望在它们执行时能从SecurityContextHolder中获取一个合法的SecurityContext。 +5. 这个过滤器实质上是对HttpSessionSecurityContextRepository进行了重构,以将存储问题委托给了单独的策略,从而允许在请求之间维护安全上下文的方式进行更多自定义。 + +首先看下SecurityContextPersistenceFilter的类结构: +![在这里插入图片描述](https://img-blog.csdnimg.cn/20200819004933998.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0NvZGVyQnJ1aXM=,size_16,color_FFFFFF,t_70#pic_center) +首先它的父类GenericeFilterBean实现了很多接口,其中有三个XXAware的接口,表示的是具备注入XX到GenericFilterBean能力,而这一般都是通过setter来注入XX实现的。 + +而实现了DisposalbleBean表明了在注销bean时能进行额外的工作,实现InitializingBean表明了能在初始化时进行额外的工作。 + +#### 1.1 源码分析 + +``` +public class SecurityContextPersistenceFilter extends GenericFilterBean { + + static final String FILTER_APPLIED = "__spring_security_scpf_applied"; + + private SecurityContextRepository repo; + + private boolean forceEagerSessionCreation = false; + + // 默认构造方法,传入HttpSessionSecurityContextRepository + public SecurityContextPersistenceFilter() { + this(new HttpSessionSecurityContextRepository()); + } + + public SecurityContextPersistenceFilter(SecurityContextRepository repo) { + this.repo = repo; + } + + // 过滤核心方法:doFilter + public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) + throws IOException, ServletException { + HttpServletRequest request = (HttpServletRequest) req; + HttpServletResponse response = (HttpServletResponse) res; + + // 判断请求中是否包含属性:__spring_security_scpf_applied,表示已经调用过SecurityContextPersistenceFilter了 + if (request.getAttribute(FILTER_APPLIED) != null) { + // 确保每次请求只调用一次该过滤器 + chain.doFilter(request, response); + return; + } + + final boolean debug = logger.isDebugEnabled(); + // 为此次请求设置__spring_security_scpf_applied为true,防止下次同样请求再次调用进来时,重复执行以下逻辑 + request.setAttribute(FILTER_APPLIED, Boolean.TRUE); + + // 急切的想要创建Session + if (forceEagerSessionCreation) { + HttpSession session = request.getSession(); + + if (debug && session.isNew()) { + logger.debug("Eagerly created session: " + session.getId()); + } + } + + HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, + response); + // 从HttpSessionSecurityContextRepository中获取SecurityContext安全上下文 + SecurityContext contextBeforeChainExecution = repo.loadContext(holder); + + try { + // 将获取的安全上下文存储到SecurityContextHolder中 + SecurityContextHolder.setContext(contextBeforeChainExecution); + // 放过滤器链执行 + chain.doFilter(holder.getRequest(), holder.getResponse()); + + } + finally { + // 等FilterChainProxy后面所有过滤器链都执行完毕时,进入finally块 + // 获取FilterChainProxy调用后的SecurityContext + SecurityContext contextAfterChainExecution = SecurityContextHolder + .getContext(); + // 清除SecurityContextHolder + SecurityContextHolder.clearContext(); + // 将安全上下文存储到HttpSessionSecurityContextRepository中,也就是持久化到Session中 + repo.saveContext(contextAfterChainExecution, holder.getRequest(), + holder.getResponse()); + request.removeAttribute(FILTER_APPLIED); + + if (debug) { + logger.debug("SecurityContextHolder now cleared, as request processing completed"); + } + } + } + + public void setForceEagerSessionCreation(boolean forceEagerSessionCreation) { + this.forceEagerSessionCreation = forceEagerSessionCreation; + } +} + +``` + +从SecurityContextPersistenceFilter类的作用可以看出,它其实就是持久化SecurityContext。 + +### 2. ExceptionTranslationFilter +先看下ExceptionTranslationFilter的类注释,总结为以下几点: +1. 此过滤器会处理任何AccessDeniedException和AuthenticationException的异常。 +2. 此过滤器是必要的,因为它提供了一个桥梁用于连接Java异常和HTTP响应。它仅和维护用户界面有关,而不会执行任何的安全性强制措施。 +3. 如果此过滤器捕获到了AuthenticationException,该Filter会加载AuthenticationEntrypoint。它允许处理任何从AbstractSecurityInterceptor子类抛出的authentication异常,AbstractSecurityInterceptor的子类即FilterChainProxy中包含的哪些过滤器。 +4. 如果捕获到了AccessDeniedException,此过滤器会判断当前用户是否是一个匿名用户。如果是匿名用户,则加载authenticationEntryPoint。如果不是匿名用户,则此过滤器会将逻辑代理到AccessDeniedHandler,由其处理接下来的逻辑。 +5. authenticationEntryPoint指示如果检测到AuthenticationException,则通过调用authenticationEntrypoint的commence方法开始认证过程的处理。需要注意的是,在ExceptionTranslationFilter中的requestCache用于保存身份验证过程中的认证结果,一边可以在用户认证通过后即可检索以及重用,requestCache的默认实现是HttpSessionRequestCache。 + +**小结:ExceptionTranslationFilter的作用即捕获AuthenticationException和AccessDeniedException,并作出相应的处理;对于捕获AccessDeniedException时,如果是匿名用户则去调用authenticationEntryPoint去进行身份验证,如果不是匿名用户则直接抛出AccessDeniedException。** + +#### 2.1 源码分析 + +先判断看下ExceptionTranslationFilter的成员变量 +``` +public class ExceptionTranslationFilter extends GenericFilterBean { + + // AccessDeniedException处理器 + private AccessDeniedHandler accessDeniedHandler = new AccessDeniedHandlerImpl(); + // 用于进行身份验证的端点 + private AuthenticationEntryPoint authenticationEntryPoint; + + // 身份认证信任机制,包括判断是否是匿名,判断是否是RememberMe + private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl(); + // 异常分析器 + private ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer(); + + // 将身份认证结果存储在HttpSession中 + private RequestCache requestCache = new HttpSessionRequestCache(); + + // 消息源转化器 + private final MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor(); + + public ExceptionTranslationFilter(AuthenticationEntryPoint authenticationEntryPoint) { + this(authenticationEntryPoint, new HttpSessionRequestCache()); + } + + public ExceptionTranslationFilter(AuthenticationEntryPoint authenticationEntryPoint, + RequestCache requestCache) { + Assert.notNull(authenticationEntryPoint, + "authenticationEntryPoint cannot be null"); + Assert.notNull(requestCache, "requestCache cannot be null"); + this.authenticationEntryPoint = authenticationEntryPoint; + this.requestCache = requestCache; + } + + // 省略 +} + +``` + +#### 2.2 doFilter源码分析 + +``` + public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) + throws IOException, ServletException { + HttpServletRequest request = (HttpServletRequest) req; + HttpServletResponse response = (HttpServletResponse) res; + + try { + // 继续调用下一个过滤器链 + chain.doFilter(request, response); + + logger.debug("Chain processed normally"); + } + catch (IOException ex) { + throw ex; + } + catch (Exception ex) { + // 尝试去获取SpringSecurityException异常 + Throwable[] causeChain = throwableAnalyzer.determineCauseChain(ex); + // 转化为运行时异常 + RuntimeException ase = (AuthenticationException) throwableAnalyzer + .getFirstThrowableOfType(AuthenticationException.class, causeChain); + + if (ase == null) { + ase = (AccessDeniedException) throwableAnalyzer.getFirstThrowableOfType( + AccessDeniedException.class, causeChain); + } + + if (ase != null) { + if (response.isCommitted()) { + throw new ServletException("Unable to handle the Spring Security Exception because the response is already committed.", ex); + } + // 处理SpringSecurity异常 + handleSpringSecurityException(request, response, chain, ase); + } + else { + // Rethrow ServletExceptions and RuntimeExceptions as-is + if (ex instanceof ServletException) { + throw (ServletException) ex; + } + else if (ex instanceof RuntimeException) { + throw (RuntimeException) ex; + } + + // Wrap other Exceptions. This shouldn't actually happen + // as we've already covered all the possibilities for doFilter + throw new RuntimeException(ex); + } + } + } + +``` + +``` + // SpringSecurityException异常处理的核心逻辑 + private void handleSpringSecurityException(HttpServletRequest request, + HttpServletResponse response, FilterChain chain, RuntimeException exception) + throws IOException, ServletException { + // 如果是认证异常 + if (exception instanceof AuthenticationException) { + logger.debug( + "Authentication exception occurred; redirecting to authentication entry point", + exception); + // 开始进行身份认证 + sendStartAuthentication(request, response, chain, + (AuthenticationException) exception); + } + else if (exception instanceof AccessDeniedException) { // 如果是访问拒绝异常 + // 尝试从SecurityContextHolder缓存中获取认证结果 + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + // 判断认证结果是否是匿名的或者是rememberme + if (authenticationTrustResolver.isAnonymous(authentication) || authenticationTrustResolver.isRememberMe(authentication)) { + logger.debug( + "Access is denied (user is " + (authenticationTrustResolver.isAnonymous(authentication) ? "anonymous" : "not fully authenticated") + "); redirecting to authentication entry point", + exception); + // 开始进行身份认证 + sendStartAuthentication( + request, + response, + chain, + new InsufficientAuthenticationException( + messages.getMessage( + "ExceptionTranslationFilter.insufficientAuthentication", + "Full authentication is required to access this resource"))); + } + else { + + logger.debug( + "Access is denied (user is not anonymous); delegating to AccessDeniedHandler", + exception); + // 如果既不是匿名用户也不是rememberme用户,则调用访问拒绝处理器 + accessDeniedHandler.handle(request, response, + (AccessDeniedException) exception); + } + } + } +``` + +``` + protected void sendStartAuthentication(HttpServletRequest request, + HttpServletResponse response, FilterChain chain, + AuthenticationException reason) throws ServletException, IOException { + // 清空缓存中的认证结果,重新进行身份验证 + SecurityContextHolder.getContext().setAuthentication(null); + // 将认证请求request和响应response存储在session中 + requestCache.saveRequest(request, response); + logger.debug("Calling Authentication entry point."); + // 进行身份验证 + authenticationEntryPoint.commence(request, response, reason); + } +``` +先看下commence的实现类: +![在这里插入图片描述](https://img-blog.csdnimg.cn/20200819095038864.png#pic_center) + +这里这么多实现类,到底调用哪一个呢?这就要看下authenticationEntryPoint注入的什么实现类了,可以将断点打在ExceptionTranslationFilter的构造方法中。 +![在这里插入图片描述](https://img-blog.csdnimg.cn/20200819095634222.png#pic_center) + +启动项目之后,进入方法调用栈,可以在图中位置看到在进行安全配置类配置时,会调用ExceptionHandlingConfigurer这个配置类的configure方法。 +![在这里插入图片描述](https://img-blog.csdnimg.cn/20200819095929151.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0NvZGVyQnJ1aXM=,size_16,color_FFFFFF,t_70#pic_center) +#### 2.3 ExceptionHandlingConfigurer + +进入其configure方法查看 + +``` + @Override + public void configure(H http) throws Exception { + // 获取authenticationEntryPoint + AuthenticationEntryPoint entryPoint = getAuthenticationEntryPoint(http); + // 新建一个ExceptionTranslationFilter对象 + ExceptionTranslationFilter exceptionTranslationFilter = new ExceptionTranslationFilter( + entryPoint, getRequestCache(http)); + // 或获取访问拒绝处理器 + AccessDeniedHandler deniedHandler = getAccessDeniedHandler(http); + exceptionTranslationFilter.setAccessDeniedHandler(deniedHandler); + exceptionTranslationFilter = postProcess(exceptionTranslationFilter); + // 往FilterChainProxy中添加ExceptionTranslationFilter + http.addFilter(exceptionTranslationFilter); + } +``` +![在这里插入图片描述](https://img-blog.csdnimg.cn/20200819100503737.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0NvZGVyQnJ1aXM=,size_16,color_FFFFFF,t_70#pic_center) +可以发现在实例化完ExceptionHandlingConfigurer后,依然没有注入authenticationEntryPoint。所以是在调用configure方法时,去调用getAuthenticationEntryPoint()去获取authenticationEntryPoint。 + +下面接着查看一下getAuthenticationEntryPoint()方法 + +``` + AuthenticationEntryPoint getAuthenticationEntryPoint(H http) { + AuthenticationEntryPoint entryPoint = this.authenticationEntryPoint; + // 由于entryPoint为空,所以调用createDefaultEntryPoint去创建entryPoint + if (entryPoint == null) { + entryPoint = createDefaultEntryPoint(http); + } + return entryPoint; + } +``` + +``` + private AuthenticationEntryPoint createDefaultEntryPoint(H http) { + // 如果entryPointMappings为空,则返回Http403ForbiddenEntryPoint + if (this.defaultEntryPointMappings.isEmpty()) { + return new Http403ForbiddenEntryPoint(); + } + if (this.defaultEntryPointMappings.size() == 1) { + // 遍历defaultEntryPointMappings,获取其中存储的entrypoint + return this.defaultEntryPointMappings.values().iterator().next(); + } + // 创建DelegatingAuthenticationEntryPoint这个代理类 + DelegatingAuthenticationEntryPoint entryPoint = new DelegatingAuthenticationEntryPoint( + this.defaultEntryPointMappings); + entryPoint.setDefaultEntryPoint(this.defaultEntryPointMappings.values().iterator() + .next()); + return entryPoint; + } +``` + +可以看出,最终返回的就是:Http403ForbiddenEntryPoint +![在这里插入图片描述](https://img-blog.csdnimg.cn/20200819101823377.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0NvZGVyQnJ1aXM=,size_16,color_FFFFFF,t_70#pic_center) + +可以看到,HTTP403ForbiddenEntryPiont这个类代码非常少 +``` +public class Http403ForbiddenEntryPoint implements AuthenticationEntryPoint { + private static final Log logger = LogFactory.getLog(Http403ForbiddenEntryPoint.class); + + /** + * Always returns a 403 error code to the client. + */ + public void commence(HttpServletRequest request, HttpServletResponse response, + AuthenticationException arg2) throws IOException, ServletException { + if (logger.isDebugEnabled()) { + logger.debug("Pre-authenticated entry point called. Rejecting access"); + } + // 在response响应中添加403 Forbidden,访问拒绝异常 + response.sendError(HttpServletResponse.SC_FORBIDDEN, "Access Denied"); + } +} +``` + +这里,还讲解一下另外一个类LoginURLAuthenticationEntryPoint的方法commence。 + +``` + public void commence(HttpServletRequest request, HttpServletResponse response, + AuthenticationException authException) throws IOException, ServletException { + // 重定向url + String redirectUrl = null; + + if (useForward) { + // 判断下请求协议是否是http + if (forceHttps && "http".equals(request.getScheme())) { + // 获取重定向完整的URL路径 + redirectUrl = buildHttpsRedirectUrlForRequest(request); + } + + if (redirectUrl == null) { + // 如果重定向地址为空,则获取默认的登录form表单地址;用户可以自定义设置; + String loginForm = determineUrlToUseForThisRequest(request, response, + authException); + + if (logger.isDebugEnabled()) { + logger.debug("Server side forward to: " + loginForm); + } + + RequestDispatcher dispatcher = request.getRequestDispatcher(loginForm); + + dispatcher.forward(request, response); + + return; + } + } + else { + redirectUrl = buildRedirectUrlToLoginPage(request, response, authException); + + } + // 发送重定向请求 + redirectStrategy.sendRedirect(request, response, redirectUrl); + } +``` + +LoginURLAuthenticationEntryPoint这个类其实就是重定向到login页面,如果用户不指定login页面,则重定向到默认的login页面。 + + +## 总结 + +本篇文章重点讲解了FilterChainProxy中的SecurityContextPersistenceFilter以及ExceptionTranslationFilter过滤器链,它们在SpringSecurity中都扮演着很重要的角色,用一句话来概括就是: +**SecurityContextPersistenceFilter用于持久化SecurityContext,而ExceptionTranslationFilter则用于捕获身份认证异常(AuthenticationException)和访问异常(AccessDeniedException),并处理这些异常。** + +然而FitlerChainProxy中还有一个非常重要的过滤器:FilterSecurityInterceptor,下一篇将重点讲解。 + +## 相关文章 + +- [从零开始系统学习SpringSecurity和OAuth2(一)—— 初识SpringSecurity](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/SpringSecurity/%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E7%B3%BB%E7%BB%9F%E5%AD%A6%E4%B9%A0SpringSecurity%E5%92%8COAuth2%EF%BC%88%E4%B8%80%EF%BC%89%E2%80%94%E2%80%94%20%E5%88%9D%E8%AF%86SpringSecurity.md) +- [从零开始系统学习SpringSecurity和OAuth2(二)—— 安全过滤器FilterChainProxy](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/SpringSecurity/%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E7%B3%BB%E7%BB%9F%E5%AD%A6%E4%B9%A0SpringSecurity%E5%92%8COAuth2%EF%BC%88%E4%BA%8C%EF%BC%89%E2%80%94%E2%80%94%20%E5%AE%89%E5%85%A8%E8%BF%87%E6%BB%A4%E5%99%A8FilterChainProxy.md) +- [从零开始系统学习SpringSecurity和OAuth2(三)—— WebSecurity建造核心逻辑](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/SpringSecurity/%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E7%B3%BB%E7%BB%9F%E5%AD%A6%E4%B9%A0SpringSecurity%E5%92%8COAuth2%EF%BC%88%E5%9B%9B%EF%BC%89%E2%80%94%E2%80%94%20FilterChainProxy%E8%BF%87%E6%BB%A4%E5%99%A8%E9%93%BE%E4%B8%AD%E7%9A%84%E5%87%A0%E4%B8%AA%E9%87%8D%E8%A6%81%E7%9A%84%E8%BF%87%E6%BB%A4%E5%99%A8.md) diff --git a/Spring-Security/src/main/java/com/bruis/springsecurity/config/CorsConfig.java b/Spring-Security/src/main/java/com/bruis/springsecurity/config/CorsConfig.java index b5bfb3f..3ea93c7 100644 --- a/Spring-Security/src/main/java/com/bruis/springsecurity/config/CorsConfig.java +++ b/Spring-Security/src/main/java/com/bruis/springsecurity/config/CorsConfig.java @@ -5,7 +5,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** - * @author LuoHaiYang + * @author super_he */ @Configuration public class CorsConfig implements WebMvcConfigurer { diff --git a/Spring-Security/src/main/java/com/bruis/springsecurity/config/SwaggerConfig.java b/Spring-Security/src/main/java/com/bruis/springsecurity/config/SwaggerConfig.java index 14477e6..fcca4f5 100644 --- a/Spring-Security/src/main/java/com/bruis/springsecurity/config/SwaggerConfig.java +++ b/Spring-Security/src/main/java/com/bruis/springsecurity/config/SwaggerConfig.java @@ -17,7 +17,7 @@ import java.util.List; /** - * @author LuoHaiYang + * @author super_he */ @Configuration @EnableSwagger2 diff --git a/Spring-Security/src/main/java/com/bruis/springsecurity/config/WebSecurityConfig.java b/Spring-Security/src/main/java/com/bruis/springsecurity/config/WebSecurityConfig.java index 22a00ee..e5f12b4 100644 --- a/Spring-Security/src/main/java/com/bruis/springsecurity/config/WebSecurityConfig.java +++ b/Spring-Security/src/main/java/com/bruis/springsecurity/config/WebSecurityConfig.java @@ -20,7 +20,7 @@ import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler; /** - * @author LuoHaiYang + * @author super_he */ @Configuration @EnableWebSecurity diff --git a/Spring-Security/src/main/java/com/bruis/springsecurity/controller/LoginController.java b/Spring-Security/src/main/java/com/bruis/springsecurity/controller/LoginController.java index fddaa44..576b78f 100644 --- a/Spring-Security/src/main/java/com/bruis/springsecurity/controller/LoginController.java +++ b/Spring-Security/src/main/java/com/bruis/springsecurity/controller/LoginController.java @@ -14,7 +14,7 @@ import java.io.IOException; /** - * @author LuoHaiYang + * @author super_he */ @RestController public class LoginController { diff --git a/Spring-Security/src/main/java/com/bruis/springsecurity/controller/UserController.java b/Spring-Security/src/main/java/com/bruis/springsecurity/controller/UserController.java index f83c150..df4fb98 100644 --- a/Spring-Security/src/main/java/com/bruis/springsecurity/controller/UserController.java +++ b/Spring-Security/src/main/java/com/bruis/springsecurity/controller/UserController.java @@ -7,7 +7,7 @@ import org.springframework.web.bind.annotation.RestController; /** - * @author LuoHaiYang + * @author super_he */ @RestController @RequestMapping("user") diff --git a/Spring-Security/src/main/java/com/bruis/springsecurity/dto/User.java b/Spring-Security/src/main/java/com/bruis/springsecurity/dto/User.java index 7b2eae6..33d132e 100644 --- a/Spring-Security/src/main/java/com/bruis/springsecurity/dto/User.java +++ b/Spring-Security/src/main/java/com/bruis/springsecurity/dto/User.java @@ -1,7 +1,7 @@ package com.bruis.springsecurity.dto; /** - * @author LuoHaiYang + * @author super_he */ public class User { private Long id; diff --git a/Spring-Security/src/main/java/com/bruis/springsecurity/model/HttpResult.java b/Spring-Security/src/main/java/com/bruis/springsecurity/model/HttpResult.java index 9a3d4d3..83c7262 100644 --- a/Spring-Security/src/main/java/com/bruis/springsecurity/model/HttpResult.java +++ b/Spring-Security/src/main/java/com/bruis/springsecurity/model/HttpResult.java @@ -1,7 +1,7 @@ package com.bruis.springsecurity.model; /** - * @author LuoHaiYang + * @author super_he */ public class HttpResult { private int code = 200; diff --git a/Spring-Security/src/main/java/com/bruis/springsecurity/model/HttpStatus.java b/Spring-Security/src/main/java/com/bruis/springsecurity/model/HttpStatus.java index 915c861..251a0d3 100644 --- a/Spring-Security/src/main/java/com/bruis/springsecurity/model/HttpStatus.java +++ b/Spring-Security/src/main/java/com/bruis/springsecurity/model/HttpStatus.java @@ -1,7 +1,7 @@ package com.bruis.springsecurity.model; /** - * @author LuoHaiYang + * @author super_he */ public interface HttpStatus { // --- 1xx Informational --- diff --git a/Spring-Security/src/main/java/com/bruis/springsecurity/model/LoginRequestParam.java b/Spring-Security/src/main/java/com/bruis/springsecurity/model/LoginRequestParam.java index 91f9f92..0b50b3f 100644 --- a/Spring-Security/src/main/java/com/bruis/springsecurity/model/LoginRequestParam.java +++ b/Spring-Security/src/main/java/com/bruis/springsecurity/model/LoginRequestParam.java @@ -1,7 +1,7 @@ package com.bruis.springsecurity.model; /** - * @author LuoHaiYang + * @author super_he */ public class LoginRequestParam { diff --git a/Spring-Security/src/main/java/com/bruis/springsecurity/service/SysUserServiceImpl.java b/Spring-Security/src/main/java/com/bruis/springsecurity/service/SysUserServiceImpl.java index 0d2ddb9..e4a4f1b 100644 --- a/Spring-Security/src/main/java/com/bruis/springsecurity/service/SysUserServiceImpl.java +++ b/Spring-Security/src/main/java/com/bruis/springsecurity/service/SysUserServiceImpl.java @@ -8,7 +8,7 @@ import java.util.Set; /** - * @author LuoHaiYang + * @author super_he */ @Service public class SysUserServiceImpl implements UserService{ diff --git a/Spring-Security/src/main/java/com/bruis/springsecurity/service/UserService.java b/Spring-Security/src/main/java/com/bruis/springsecurity/service/UserService.java index cd1acb9..87a1dc1 100644 --- a/Spring-Security/src/main/java/com/bruis/springsecurity/service/UserService.java +++ b/Spring-Security/src/main/java/com/bruis/springsecurity/service/UserService.java @@ -5,7 +5,7 @@ import java.util.Set; /** - * @author LuoHaiYang + * @author super_he */ public interface UserService { diff --git a/Spring-Security/src/main/java/com/bruis/springsecurity/utils/HttpUtils.java b/Spring-Security/src/main/java/com/bruis/springsecurity/utils/HttpUtils.java index 0371428..5d5945c 100644 --- a/Spring-Security/src/main/java/com/bruis/springsecurity/utils/HttpUtils.java +++ b/Spring-Security/src/main/java/com/bruis/springsecurity/utils/HttpUtils.java @@ -10,7 +10,7 @@ import java.io.IOException; /** - * @author LuoHaiYang + * @author super_he */ public class HttpUtils { /** diff --git a/Spring-Security/src/main/java/com/bruis/springsecurity/utils/JwtTokenUtils.java b/Spring-Security/src/main/java/com/bruis/springsecurity/utils/JwtTokenUtils.java index 303d376..e3956a1 100644 --- a/Spring-Security/src/main/java/com/bruis/springsecurity/utils/JwtTokenUtils.java +++ b/Spring-Security/src/main/java/com/bruis/springsecurity/utils/JwtTokenUtils.java @@ -15,7 +15,7 @@ import java.util.*; /** - * @author LuoHaiYang + * @author super_he */ public class JwtTokenUtils implements Serializable { diff --git a/Spring-Security/src/main/java/com/bruis/springsecurity/utils/security/GrantedAuthorityImpl.java b/Spring-Security/src/main/java/com/bruis/springsecurity/utils/security/GrantedAuthorityImpl.java index 7f51ff0..7d56926 100644 --- a/Spring-Security/src/main/java/com/bruis/springsecurity/utils/security/GrantedAuthorityImpl.java +++ b/Spring-Security/src/main/java/com/bruis/springsecurity/utils/security/GrantedAuthorityImpl.java @@ -3,7 +3,7 @@ import org.springframework.security.core.GrantedAuthority; /** - * @author LuoHaiYang + * @author super_he */ public class GrantedAuthorityImpl implements GrantedAuthority { diff --git a/Spring-Security/src/main/java/com/bruis/springsecurity/utils/security/JwtAuthenticationFilter.java b/Spring-Security/src/main/java/com/bruis/springsecurity/utils/security/JwtAuthenticationFilter.java index 6c5515e..b03d274 100644 --- a/Spring-Security/src/main/java/com/bruis/springsecurity/utils/security/JwtAuthenticationFilter.java +++ b/Spring-Security/src/main/java/com/bruis/springsecurity/utils/security/JwtAuthenticationFilter.java @@ -10,7 +10,7 @@ import java.io.IOException; /** - * @author LuoHaiYang + * @author super_he * * 登录检查过滤器, 原逻辑是在BasicAuthenticationFilter类中. * 即判断请求头中是否包含:Authorization, 是否包含basic。 diff --git a/Spring-Security/src/main/java/com/bruis/springsecurity/utils/security/JwtAuthenticationProvider.java b/Spring-Security/src/main/java/com/bruis/springsecurity/utils/security/JwtAuthenticationProvider.java index 5abe6c7..f983e01 100644 --- a/Spring-Security/src/main/java/com/bruis/springsecurity/utils/security/JwtAuthenticationProvider.java +++ b/Spring-Security/src/main/java/com/bruis/springsecurity/utils/security/JwtAuthenticationProvider.java @@ -9,7 +9,7 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; /** - * @author LuoHaiYang + * @author super_he * * DaoAuthenticationProvider 系统默认的身份认证组件 * diff --git a/Spring-Security/src/main/java/com/bruis/springsecurity/utils/security/JwtAuthenticationToken.java b/Spring-Security/src/main/java/com/bruis/springsecurity/utils/security/JwtAuthenticationToken.java index 61c1973..7154350 100644 --- a/Spring-Security/src/main/java/com/bruis/springsecurity/utils/security/JwtAuthenticationToken.java +++ b/Spring-Security/src/main/java/com/bruis/springsecurity/utils/security/JwtAuthenticationToken.java @@ -9,7 +9,7 @@ * * 自定义令牌 * - * @author LuoHaiYang + * @author super_he */ public class JwtAuthenticationToken extends UsernamePasswordAuthenticationToken { private static final long serialVersionUID = 1L; diff --git a/Spring-Security/src/main/java/com/bruis/springsecurity/utils/security/JwtLoginFilter.java b/Spring-Security/src/main/java/com/bruis/springsecurity/utils/security/JwtLoginFilter.java index 5230299..8c80ce7 100644 --- a/Spring-Security/src/main/java/com/bruis/springsecurity/utils/security/JwtLoginFilter.java +++ b/Spring-Security/src/main/java/com/bruis/springsecurity/utils/security/JwtLoginFilter.java @@ -25,7 +25,7 @@ /** * 启动登录认证流程过滤器 - * @author LuoHaiYang + * @author super_he */ public class JwtLoginFilter extends UsernamePasswordAuthenticationFilter { diff --git a/Spring-Security/src/main/java/com/bruis/springsecurity/utils/security/JwtUserDetails.java b/Spring-Security/src/main/java/com/bruis/springsecurity/utils/security/JwtUserDetails.java index dcfd63d..fde32f7 100644 --- a/Spring-Security/src/main/java/com/bruis/springsecurity/utils/security/JwtUserDetails.java +++ b/Spring-Security/src/main/java/com/bruis/springsecurity/utils/security/JwtUserDetails.java @@ -6,7 +6,7 @@ import java.util.Collection; /** - * @author LuoHaiYang + * @author super_he */ public class JwtUserDetails extends User { diff --git a/Spring-Security/src/main/java/com/bruis/springsecurity/utils/security/SecurityUtils.java b/Spring-Security/src/main/java/com/bruis/springsecurity/utils/security/SecurityUtils.java index d982afd..77533e4 100644 --- a/Spring-Security/src/main/java/com/bruis/springsecurity/utils/security/SecurityUtils.java +++ b/Spring-Security/src/main/java/com/bruis/springsecurity/utils/security/SecurityUtils.java @@ -9,7 +9,7 @@ import javax.servlet.http.HttpServletRequest; /** - * @author LuoHaiYang + * @author super_he */ public class SecurityUtils { diff --git a/Spring-Security/src/main/java/com/bruis/springsecurity/utils/security/UserDetailsServiceImpl.java b/Spring-Security/src/main/java/com/bruis/springsecurity/utils/security/UserDetailsServiceImpl.java index 93e0763..5d1a837 100644 --- a/Spring-Security/src/main/java/com/bruis/springsecurity/utils/security/UserDetailsServiceImpl.java +++ b/Spring-Security/src/main/java/com/bruis/springsecurity/utils/security/UserDetailsServiceImpl.java @@ -14,7 +14,7 @@ import java.util.stream.Collectors; /** - * @author LuoHaiYang + * @author super_he */ @Service public class UserDetailsServiceImpl implements UserDetailsService {

AltStyle によって変換されたページ (->オリジナル) /