格式化及符号和springSecurity名称完整

pull/142/head
poo0054 2 years ago
parent b809c2287f
commit ec936747c3

@ -5,20 +5,23 @@
1. 基于 spring-boot-dependencies:2.7.7
2. 首先需要了解[springboot2.7 升级](https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.7-Release-Notes)
`Changes to Auto-configuration` 以后使用`autoconfigure`进行自动注入
3. 代码地址[io.github.poo0054](https://github.com/poo0054/security-study/blob/master/starter/src/main/java/io/github/poo0054/security/StarterApplication.java)
3.
代码地址[io.github.poo0054](https://github.com/poo0054/security-study/blob/master/starter/src/main/java/io/github/poo0054/security/StarterApplication.java)
## 启动
我们每次添加` <artifactId>spring-boot-starter-security</artifactId>`
,启动的时候启动日志会有一条类似
`Using generated security password: 1db8eb87-e2ee-4c72-88e7-9b85268c4430
启动的时候启动日志会有一条类似
`Using generated springSecurity password: 1db8eb87-e2ee-4c72-88e7-9b85268c4430
This generated password is for development use only. Your security configuration must be updated before running your
This generated password is for development use only. Your springSecurity configuration must be updated before running your
application in production.`
的日志.找到`UserDetailsServiceAutoConfiguration#InMemoryUserDetailsManager`类,它是springboot自动装配的.
的日志。找到`UserDetailsServiceAutoConfiguration#InMemoryUserDetailsManager`类,它是 springboot 自动装配的。
下面这些都是springboot自动装配类,在`spring-boot-autoconfigure-2.7.7.jar`>META-INF>spring>
org.springframework.boot.autoconfigure.AutoConfiguration.imports中. 这些类就是security的全部了.
下面这些都是 springboot 自动装配类,在`spring-boot-autoconfigure-2.7.7.jar`>META-INF>spring>
org.springframework.boot.autoconfigure.AutoConfiguration.imports 中. 这些类就是 springSecurity 的全部了.
```imports
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
@ -64,27 +67,27 @@ public class SecurityAutoConfiguration {
### @EnableConfigurationProperties(SecurityProperties.class)
这个是security的核心配置类`SecurityProperties`,里面能配置
`filter`: 过滤,`user` : 用户信息
这个是 springSecurity 的核心配置类`SecurityProperties`里面能配置
`filter`: 过滤`user` : 用户信息
`这个有个问题,filter是属于tomcat的,security中使用什么方式让filter变的有序的`
`这个有个问题filter是属于tomcat的springSecurity 中使用什么方式让filter变的有序的`
### @Import({ SpringBootWebSecurityConfiguration.class, SecurityDataConfiguration.class })
这里导入了2个类 `SpringBootWebSecurityConfiguration`和`SecurityDataConfiguration`,`SecurityDataConfiguration`是Spring
Security与Spring数据的集成,暂时不做讲解,重点是`SpringBootWebSecurityConfiguration`
这里导入了 2 个类 `SpringBootWebSecurityConfiguration`和`SecurityDataConfiguration``SecurityDataConfiguration`是 Spring
springSecurity 与 Spring 数据的集成,暂时不做讲解,重点是`SpringBootWebSecurityConfiguration`
#### SpringBootWebSecurityConfiguration
这个类就是一个`Configuration`类,条件必须为`@ConditionalOnWebApplication(type = Type.SERVLET)`才会注入
这个类就是一个`Configuration`类条件必须为`@ConditionalOnWebApplication(type = Type.SERVLET)`才会注入
##### SecurityFilterChainConfiguration
其中第一个子类`SecurityFilterChainConfiguration`添加了`@ConditionalOnDefaultWebSecurity`,这个类有个注解
`@Conditional(DefaultWebSecurityCondition.class)`,而`DefaultWebSecurityCondition`类继承了`AllNestedConditions`
其中第一个子类`SecurityFilterChainConfiguration`添加了`@ConditionalOnDefaultWebSecurity`这个类有个注解
`@Conditional(DefaultWebSecurityCondition.class)`而`DefaultWebSecurityCondition`类继承了`AllNestedConditions`
所以下面代码就是判断该类是否生效,如果不存在`SecurityFilterChain`和`WebSecurityConfigurerAdapter`
bean,就生效.创建默认的`SecurityFilterChain`
所以下面代码就是判断该类是否生效如果不存在`SecurityFilterChain`和`WebSecurityConfigurerAdapter`
bean就生效。创建默认的`SecurityFilterChain`
```java
/**
@ -115,18 +118,18 @@ class DefaultWebSecurityCondition extends AllNestedConditions {
}
```
`SecurityFilterChain`就是整个springsecurity的流程了,有俩个方法,一个是`boolean matches(HttpServletRequest request);`
,是否匹配这次请求,匹配成功就获取当前所有`Filter`进行处理
`SecurityFilterChain`就是整个 springsecurity 的流程了,有俩个方法,一个是`boolean matches(HttpServletRequest request);`
,是否匹配这次请求,匹配成功就获取当前所有`Filter`进行处理
`SecurityFilterChain`类会放在最下面单独讲解
##### ErrorPageSecurityFilterConfiguration
这是第二个子类,主要就是通过`FilterRegistrationBean`注入了一个`ErrorPageSecurityFilter`. 用于拦截错误调度,以确保对错误页面的授权访问。
这是第二个子类,主要就是通过`FilterRegistrationBean`注入了一个`ErrorPageSecurityFilter`。 用于拦截错误调度,以确保对错误页面的授权访问。
```java
/**
* Configures the {@link ErrorPageSecurityFilter}.
* Configures the {@link ErrorPageSecurityFilter}
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(WebInvocationPrivilegeEvaluator.class)
@ -146,11 +149,11 @@ static class ErrorPageSecurityFilterConfiguration {
##### WebSecurityEnablerConfiguration
这个类主要就是添加了`@EnableWebSecurity`注解,这个注解也很重要,后面跟`SecurityFilterChain`一起讲解
这个类主要就是添加了`@EnableWebSecurity`注解,这个注解也很重要,后面跟`SecurityFilterChain`一起讲解
### DefaultAuthenticationEventPublisher
在类中还存在`SecurityAutoConfiguration`bean,这个是属于spring的发布订阅.改装一下,就是security的成功和失败事件,可以订阅失败后的一些处理,如日志打印等
在类中还存在`SecurityAutoConfiguration`bean,这个是属于 spring 的发布订阅。改装一下,就是 springSecurity 的成功和失败事件,可以订阅失败后的一些处理,如日志打印等
```java
/**
@ -194,26 +197,26 @@ Spring Security的过滤器
### DelegatingFilterProxyRegistrationBean
这个类是继承了`AbstractFilterRegistrationBean`,`FilterRegistrationBean`也是继承的`AbstractFilterRegistrationBean`
.但是`DelegatingFilterProxyRegistrationBean`是使用的一个`targetBeanName`
找到bean.进行注入.其中`private static final String DEFAULT_FILTER_NAME = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME;`
这个类是继承了`AbstractFilterRegistrationBean``FilterRegistrationBean`也是继承的`AbstractFilterRegistrationBean`
但是`DelegatingFilterProxyRegistrationBean`是使用的一个`targetBeanName`
找到 bean。进行注入。其中`private static final String DEFAULT_FILTER_NAME = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME;`
也就是`springSecurityFilterChain`
其中有俩个属性, Order和DispatcherTypes
其中有俩个属性 Order 和 DispatcherTypes
* Order默认为-100
- Order 默认为-100
* `DispatcherType`就是`DispatcherType`类
- `DispatcherType`就是`DispatcherType`类
`private Set<DispatcherType> dispatcherTypes = new HashSet<>(
Arrays.asList(DispatcherType.ASYNC, DispatcherType.ERROR, DispatcherType.REQUEST));`
Arrays.asList(DispatcherType.ASYNC DispatcherType.ERROR DispatcherType.REQUEST));`
注意: 这里需要了解一下`DelegatingFilterProxyRegistrationBean`以及spring如何整合filter和mvc的.security核心就是filter
注意: 这里需要了解一下`DelegatingFilterProxyRegistrationBean`以及 spring 如何整合 filter 和 mvc 的。 springSecurity 核心就是 filter
![img.png](../../images/SpringSecurity/img-2023-6-7_0.png)image.png
`DelegatingFilterProxyRegistrationBean`和`FilterRegistrationBean`都是继承的`RegistrationBean`,而`RegistrationBean`
又是`ServletContextInitializer`的实现类.其中`void onStartup(ServletContext servletContext)`方法是关键. 在`javax.servlet`
,存在这样一个类
`DelegatingFilterProxyRegistrationBean`和`FilterRegistrationBean`都是继承的`RegistrationBean`而`RegistrationBean`
又是`ServletContextInitializer`的实现类。其中`void onStartup(ServletContext servletContext)`方法是关键。 在`javax.servlet`
存在这样一个类
```java
public interface ServletContainerInitializer {
@ -233,32 +236,32 @@ public interface ServletContainerInitializer {
void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException;
```
springboot中的`TomcatStarter`继承了这个类,而这个类又是spring启动的核心`AbstractApplicationContext#refresh()`
中的`onRefresh();`方法.
springboot 中的`TomcatStarter`继承了这个类,而这个类又是 spring 启动的核心`AbstractApplicationContext#refresh()`
中的`onRefresh();`方法
找到实现类`ServletWebServerApplicationContext`的`onRefresh()`方法.里面有`createWebServer();`方法.
在里面有创建`webServer`的方法.`this.webServer = factory.getWebServer(getSelfInitializer());`这个就是创建`tomcat`的工厂.
找到实现类`ServletWebServerApplicationContext`的`onRefresh()`方法。里面有`createWebServer();`方法。
在里面有创建`webServer`的方法。`this.webServer = factory.getWebServer(getSelfInitializer());`这个就是创建`tomcat`的工厂。
`TomcatServletWebServerFactory#getWebServer(ServletContextInitializer... initializers)`
.里面就是创建tomcat,并启动`TomcatWebServer#initialize()`(这就是springboot不用tamcat的原因)
。里面就是创建 tomcat并启动`TomcatWebServer#initialize()`(这就是 springboot 不用 tamcat 的原因)
而把 filter 注入 servlet 中的是`TomcatServletWebServerFactory#prepareContext(Host host, ServletContextInitializer[] initializers)`
中的`TomcatServletWebServerFactory#configureContext(context, initializersToUse)`
方法,在里面创建了一个`TomcatStarter starter = new TomcatStarter(initializers);`.而`TomcatStarter`
继承了`ServletContainerInitializer`类.调用`ServletContainerInitializer#onStartup(ServletContext servletContext)`
时候会进入到`RegistrationBean`中.
方法,在里面创建了一个`TomcatStarter starter = new TomcatStarter(initializers);`。而`TomcatStarter`
继承了`ServletContainerInitializer`类调用`ServletContainerInitializer#onStartup(ServletContext servletContext)`
时候会进入到`RegistrationBean`中
然后`AbstractFilterRegistrationBean#addRegistration`里面添加 filter
`return servletContext.addFilter(getOrDeduceName(filter), filter);`这样每次请求servlet,tomcat就会先使用filter过滤器进行拦截
`return servletContext.addFilter(getOrDeduceName(filter), filter);`这样每次请求 servlettomcat 就会先使用 filter 过滤器进行拦截
简单来说就是`TomcatStarter`继承了`ServletContainerInitializer`.tomcat会调用`onStartup`
方法,在这个方法里面会调用`ServletContextInitializer#onStartup`.在这个里面有`filter`和其余需要整合`ServletContext`的方法
简单来说就是`TomcatStarter`继承了`ServletContainerInitializer`。tomcat 会调用`onStartup`
方法,在这个方法里面会调用`ServletContextInitializer#onStartup`。在这个里面有`filter`和其余需要整合`ServletContext`的方法
比如`springSecurityFilterChain`使用的是`DelegatingFilterProxyRegistrationBean`,需要使用bean去获取`getFilter`
.而`ErrorPageFilter`使用的是`FilterRegistrationBean`.直接就可以注入
比如`springSecurityFilterChain`使用的是`DelegatingFilterProxyRegistrationBean`,需要使用 bean 去获取`getFilter`
。而`ErrorPageFilter`使用的是`FilterRegistrationBean`。直接就可以注入
## @EnableWebSecurity
这个就是security的核心注解
需要注意,`@EnableWebMvcSecurity`已经弃用,请使用`@EnableWebSecurity`
这个就是 springSecurity 的核心注解
需要注意`@EnableWebMvcSecurity`已经弃用,请使用`@EnableWebSecurity`
```java
@ -291,15 +294,16 @@ public @interface EnableWebSecurity {
#### setFilterChainProxySecurityConfigurer
逐行解释:
> this.webSecurity = objectPostProcessor.postProcess(new WebSecurity(objectPostProcessor));
`ObjectPostProcessor`也就是`AutowireBeanFactoryObjectPostProcessor`.在`AuthenticationConfiguration`
`ObjectPostProcessor`也就是`AutowireBeanFactoryObjectPostProcessor`在`AuthenticationConfiguration`
类上`@Import(ObjectPostProcessorConfiguration.class)`.
`AutowireBeanFactoryObjectPostProcessor`类里面创建`webSecurity`
`AutowireBeanFactoryObjectPostProcessor.postProcess(new WebSecurity(objectPostProcessor));`
使用`AutowireCapableBeanFactory`创建出`WebSecurity`
`AutowireBeanFactoryObjectPostProcessor`把`SmartInitializingSingleton`和`DisposableBean`拿出来,使用自己的`destroy()`
`AutowireBeanFactoryObjectPostProcessor`把`SmartInitializingSingleton`和`DisposableBean`拿出来使用自己的`destroy()`
和`afterSingletonsInstantiated()`执行
> List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new
@ -312,18 +316,18 @@ public @interface EnableWebSecurity {
先了解一下结构
![img_1.png](../../images/SpringSecurity/img-2023-6-7_1.png)
![img_2.png](../../images/SpringSecurity/img-2023-6-7-_2.png)
使用`WebSecurity`聚合了`private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<>();` 也就是`WebSecurityConfigurerAdapter`(当然还有别的,这里主要讲`WebSecurityConfigurerAdapter`)
使用`WebSecurity`
聚合了`private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<>();`
也就是`WebSecurityConfigurerAdapter`(当然还有别的,这里主要讲`WebSecurityConfigurerAdapter`)
`WebSecurityConfigurerAdapter`也可以认为就是`SecurityConfigurer`
`WebSecurity`也就是`SecurityBuilder`
然后在`SecurityBuilder`的实现类`AbstractConfiguredSecurityBuilder`的`doBuild()`方法进行很多别的操作.
然后在`SecurityBuilder`的实现类`AbstractConfiguredSecurityBuilder`的`doBuild()`方法进行很多别的操作。
```java
protected final O doBuild()throws Exception{
synchronized (this.configurers){
@ -341,13 +345,17 @@ protected final O doBuild() throws Exception {
}
```
回到原来地方,返回的`webSecurityConfigurers`,里面的
回到原来地方,返回的`webSecurityConfigurers`,里面的
```java
for(SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer:webSecurityConfigurers){
this.webSecurity.apply(webSecurityConfigurer);
}
```
然后就到了`AbstractConfiguredSecurityBuilder#apply`方法,里面调用了`add(configurer);` 也就是把`SecurityConfigurer`放入了`AbstractConfiguredSecurityBuilder#configurers`的一个map中,这样就使用`SecurityBuilder`聚合了`SecurityConfigurer`. 在构建的时候可以做一些事情
然后就到了`AbstractConfiguredSecurityBuilder#apply`方法,里面调用了`add(configurer);` 也就是把`SecurityConfigurer`
放入了`AbstractConfiguredSecurityBuilder#configurers`的一个 map 中,这样就使用`SecurityBuilder`聚合了`SecurityConfigurer`。
在构建的时候可以做一些事情
也就是说使用`WebSecurity`聚合了`SecurityConfigurer`(包括`WebSecurityConfigurerAdapter`)
@ -359,9 +367,10 @@ for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurity
获取所有`webSecurityCustomizers`
#### public Filter springSecurityFilterChain()
这个里面最关键的也就是这个了.
这个里面最关键的也就是这个了。
```java
for(SecurityFilterChain securityFilterChain:this.securityFilterChains){
this.webSecurity.addSecurityFilterChainBuilder(()->securityFilterChain);
@ -380,12 +389,15 @@ for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurity
首先使用根据获取到的`securityFilterChains`set 入`WebSecurity#securityFilterChainBuilders`的 List 属性
这里有个需要注意的地方,如果你继承了`WebSecurityConfigurerAdapter`.`this.securityFilterChains` 就是一个空的.
这里有个需要注意的地方,如果你继承了`WebSecurityConfigurerAdapter`。`this.securityFilterChains` 就是一个空的。
而且会由`WebSecurityConfigurerAdapter#getHttp()`进行创建`WebSecurity`.这就跟spring的order有关了. `@Order(SecurityProperties.BASIC_AUTH_ORDER)`
而且会由`WebSecurityConfigurerAdapter#getHttp()`进行创建`WebSecurity`
。这就跟 spring 的 order 有关了。 `@Order(SecurityProperties.BASIC_AUTH_ORDER)`
其中`SpringBootWebSecurityConfiguration#SecurityFilterChainConfiguration`有一个注解`@ConditionalOnDefaultWebSecurity`
```java
@Configuration(proxyBeanMethods = false)
@ConditionalOnDefaultWebSecurity
static class SecurityFilterChainConfiguration {
@ -401,25 +413,30 @@ for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurity
}
```
这里会创建`SecurityFilterChain`. 还会有一个`HttpSecurity`的注入
继续回到上面,
这里会创建`SecurityFilterChain`。 还会有一个`HttpSecurity`的注入
继续回到上面,
```java
if(filter instanceof FilterSecurityInterceptor){
this.webSecurity.securityInterceptor((FilterSecurityInterceptor)filter);
break;
}
```
`FilterSecurityInterceptor`也在这里进行处理,也就是`SecurityMetadataSource`元数据
然后自定义的`WebSecurityCustomizer`也在这里.可以自行改变`webSecurity`
`FilterSecurityInterceptor`也在这里进行处理,也就是`SecurityMetadataSource`元数据
然后自定义的`WebSecurityCustomizer`也在这里。可以自行改变`webSecurity`
```java
for(WebSecurityCustomizer customizer:this.webSecurityCustomizers){
customizer.customize(this.webSecurity);
}
```
接下来就是构建了,来到`AbstractConfiguredSecurityBuilder#doBuild()`
接下来就是构建了,来到`AbstractConfiguredSecurityBuilder#doBuild()`
```java
protected final O doBuild()throws Exception{
synchronized (this.configurers){
@ -436,10 +453,11 @@ protected final O doBuild() throws Exception {
}
}
```
> init();
如果继承了`WebSecurityConfigurerAdapter`,就会在这里创建`HttpSecurity`
> 如果继承了`WebSecurityConfigurerAdapter`就会在这里创建`HttpSecurity`
注意: 这里个`buildState`,用来控制当前状态的
注意: 这里个`buildState`用来控制当前状态的
> beforeConfigure();
@ -451,34 +469,38 @@ protected final O doBuild() throws Exception {
> O result = performBuild();
然后就到了`WebSecurity#performBuild()`.
然后就到了`WebSecurity#performBuild()`
1. 首先排除忽略的`RequestMatcher`
2. 添加入`securityFilterChain` 和`requestMatcherPrivilegeEvaluatorsEntries`
3. 创建出`FilterChainProxy`bean 的名称为`springSecurityFilterChain` (重点)
剩下的都是一些创建一些bean了.
`SecurityExpressionHandler`: 默认为`DefaultWebSecurityExpressionHandler`类(Facade将Spring Security评估安全表达式的要求与基础表达式对象的实现隔离)
剩下的都是一些创建一些 bean 了。
`WebInvocationPrivilegeEvaluator`: 为 `WebSecurity#performBuild()`中创建的 `requestMatcherPrivilegeEvaluatorsEntries` 使用`RequestMatcherDelegatingWebInvocationPrivilegeEvaluator`包装.(允许用户确定他们是否具有给定web URI的特权。)
`SecurityExpressionHandler`: 默认为`DefaultWebSecurityExpressionHandler`类(Facade 将 springSecurity 评估安全表达式的要求与基础表达式对象的实现隔离)
这俩个类都是很重要的. 一个是解析器,一个是判断uri是否合格的类. 后面单独讲
`WebInvocationPrivilegeEvaluator`: 为 `WebSecurity#performBuild()`中创建的 `requestMatcherPrivilegeEvaluatorsEntries`
使用`RequestMatcherDelegatingWebInvocationPrivilegeEvaluator`包装。(允许用户确定他们是否具有给定 web URI 的特权。)
这俩个类都是很重要的。 一个是解析器,一个是判断 uri 是否合格的类。 后面单独讲
### HttpSecurityConfiguration
接下来到了`HttpSecurityConfiguration`
根据上面`WebSecurityConfiguration`,可以得出.`WebSecurityConfiguration`创建`WebSecurity`,`WebSecurity`创建了`FilterChainProxy`的bean.
根据上面`WebSecurityConfiguration`,可以得出。`WebSecurityConfiguration`创建`WebSecurity``WebSecurity`
创建了`FilterChainProxy`的 bean。
`HttpSecurityConfiguration`创建`HttpSecurity`. 而在`SecurityFilterChainConfiguration`类中,使用`HttpSecurity`创建了`SecurityFilterChain`.这也就是我们使用了`WebSecurityConfigurerAdapter`.为什么会存在`SecurityFilterChain`类的原因.是`SecurityFilterChainConfiguration#defaultSecurityFilterChain`创建了一个`SecurityFilterChain`.
`HttpSecurityConfiguration`创建`HttpSecurity`。 而在`SecurityFilterChainConfiguration`类中,使用`HttpSecurity`
创建了`SecurityFilterChain`。这也就是我们使用了`WebSecurityConfigurerAdapter`。为什么会存在`SecurityFilterChain`
类的原因。是`SecurityFilterChainConfiguration#defaultSecurityFilterChain`创建了一个`SecurityFilterChain`。
得出结论,`FilterChainProxy`持有`SecurityFilterChain`.而`DelegatingFilterProxyRegistrationBean`又持有`FilterChainProxy`
得出结论`FilterChainProxy`持有`SecurityFilterChain`。而`DelegatingFilterProxyRegistrationBean`又持有`FilterChainProxy`
`DelegatingFilterProxyRegistrationBean`->`FilterChainProxy`->`SecurityFilterChain`
其实到了这一步。后面的就是我们自己编写的代码了比如
其实到了这一步.后面的就是我们自己编写的代码了比如
```java
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http)throws Exception{
@ -501,9 +523,10 @@ protected final O doBuild() throws Exception {
}
```
或者继承WebSecurityConfigurerAdapter的类了.
或者继承 WebSecurityConfigurerAdapter 的类了。
这里我们用 springSecurity 默认的`SpringBootWebSecurityConfiguration`来举例
这里我们用security默认的`SpringBootWebSecurityConfiguration`来举例
```java
@Bean
@Order(SecurityProperties.BASIC_AUTH_ORDER)
@ -515,21 +538,25 @@ protected final O doBuild() throws Exception {
}
```
首先获取`HttpSecurity http`(这里的`HttpSecurity`是从`HttpSecurityConfiguration`里面创建的,如果是自定义的`SecurityFilterChain`就是从自己写的里面来的)
首先获取`HttpSecurity http`(这里的`HttpSecurity`是从`HttpSecurityConfiguration`
里面创建的,如果是自定义的`SecurityFilterChain`就是从自己写的里面来的)
> 我们来到`HttpSecurityConfiguration#httpSecurity()`
先创建一个默认的密码管理器,
> 先创建一个默认的密码管理器,
接下来进入`authenticationBuilder.parentAuthenticationManager(authenticationManager());`,这里就是`AuthenticationConfiguration`里面的处理.这个类后面和springaop加载我们写的注释单独在`@EnableGlobalAuthentication`注解类说
接下来进入`authenticationBuilder.parentAuthenticationManager(authenticationManager());`
,这里就是`AuthenticationConfiguration`
里面的处理。这个类后面和 springaop 加载我们写的注释单独在`@EnableGlobalAuthentication`注解类说
接着创建` HttpSecurity http = new HttpSecurity(this.objectPostProcessor, authenticationBuilder, createSharedObjects());`,
> .csrf(withDefaults())
启用CSRF保护,创建`CsrfConfigurer`类,并添加入添加入`AbstractConfiguredSecurityBuilder#configurers`.
> 启用 CSRF 保护,创建`CsrfConfigurer`类,并添加入添加入`AbstractConfiguredSecurityBuilder#configurers`
> .addFilter(new WebAsyncManagerIntegrationFilter())
这个有一个很有意思的类`FilterOrderRegistration`. 前面问的根据filter是如何包装顺序的.就在这个类里面
这个有一个很有意思的类`FilterOrderRegistration`。 前面问的根据 filter 是如何包装顺序的。就在这个类里面
```java
FilterOrderRegistration(){
Step order=new Step(INITIAL_ORDER,ORDER_STEP);
@ -546,7 +573,9 @@ FilterOrderRegistration() {
put(LogoutFilter.class,order.next());
// .....
```
security事先使用这个类把预加载的类全部排序好,然后每次add一个新的filter就会使用这个里面的序号.如果我们有自定义的类,也要提前加载到里面去,不然就会
springSecurity 事先使用这个类把预加载的类全部排序好,然后每次 add 一个新的 filter 就会使用这个里面的序号。如果我们有自定义的类,也要提前加载到里面去,不然就会
```java
throw new IllegalArgumentException("The Filter class "+filter.getClass().getName()
+" does not have a registered order and cannot be added without a specified order. Consider using addFilterBefore or addFilterAfter instead.");
@ -558,23 +587,30 @@ security事先使用这个类把预加载的类全部排序好,然后每次add
这个是异常的处理
提示: 这个里面每次添加一个类,如果在`HttpSecurity`中调用`getOrApply`.比如这个代码调用的是`exceptionHandlingCustomizer.customize(getOrApply(new ExceptionHandlingConfigurer<>()));`. 打开`ExceptionHandlingConfigurer`类,发现是一个`HttpSecurityBuilder`, 这样只需要看`configure`方法大概就能明白这个是一个什么类. 这个就是在filter中添加了一个`ExceptionTranslationFilter`filter.主要就是`SecurityConfigurer`的俩个方法.先调用`init(B builder)`,然后`configure(B builder)`
提示: 这个里面每次添加一个类,如果在`HttpSecurity`中调用`getOrApply`
。比如这个代码调用的是`exceptionHandlingCustomizer.customize(getOrApply(new ExceptionHandlingConfigurer<>()));`。
打开`ExceptionHandlingConfigurer`类,发现是一个`HttpSecurityBuilder` 这样只需要看`configure`方法大概就能明白这个是一个什么类。
这个就是在 filter 中添加了一个`ExceptionTranslationFilter`filter.主要就是`SecurityConfigurer`
的俩个方法。先调用`init(B builder)`,然后`configure(B builder)`
后面都是一样,就跳过了
后面都是一样就跳过了
> applyDefaultConfigurers(http);
这里的这一句,就是从"META-INF/spring.factories" 中加载并实例化给定类型的工厂实现
这里的这一句就是从"META-INF/spring.factories" 中加载并实例化给定类型的工厂实现
`SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader)`
然后调用`http.apply(configurer);` 添加到`configurers`里面
接下来回到`SecurityFilterChainConfiguration`类
> http.authorizeRequests().anyRequest().authenticated();
首先添加了`http.authorizeRequests()` 然后调用 `return getOrApply(new ExpressionUrlAuthorizationConfigurer<>(context)).getRegistry();`.
先把`ExpressionUrlAuthorizationConfigurer`放入config中,返回一个调用了`getRegistry()`. 也就是`ExpressionInterceptUrlRegistry`类.
首先添加了`http.authorizeRequests()`
然后调用 `return getOrApply(new ExpressionUrlAuthorizationConfigurer<>(context)).getRegistry();`
先把`ExpressionUrlAuthorizationConfigurer`放入 config 中,返回一个调用了`getRegistry()`。
也就是`ExpressionInterceptUrlRegistry`类。
后面调用的`.anyRequest()`,也就是`AbstractRequestMatcherRegistry#anyRequest()`.先了解一下结构图
后面调用的`.anyRequest()`,也就是`AbstractRequestMatcherRegistry#anyRequest()`。先了解一下结构图
![img_3.png](../../images/SpringSecurity/img-2023-6-7-_3.png)
@ -582,13 +618,16 @@ security事先使用这个类把预加载的类全部排序好,然后每次add
`http.authorizeRequests().anyRequest().authenticated();`是需要所有请求登录后才能访问
1. `authorizeRequests`是创建了一个`ExpressionUrlAuthorizationConfigurer`并添加入configurer中.
2. `anyRequest`是创建了一个`new AuthorizedUrl(requestMatchers)`,其中 `requestMatchers`是`AnyRequestMatcher.INSTANCE;`也就是`AnyRequestMatcher`对象.里面`matches(HttpServletRequest request)`直接返回ture
3. `authenticated()`也就是授权,`ExpressionInterceptUrlRegistry#addMapping`.里面放入了一个`UrlMapping`,`UrlMapping`的俩个属性,一个是`AnyRequestMatcher`(所有请求),`configAttrs`表示`SecurityConfig`.`SecurityConfig`的值为`private static final String authenticated = "authenticated"`
1. `authorizeRequests`是创建了一个`ExpressionUrlAuthorizationConfigurer`并添加入 configurer 中。
2. `anyRequest`是创建了一个`new AuthorizedUrl(requestMatchers)`,其中 `requestMatchers`
是`AnyRequestMatcher.INSTANCE;`也就是`AnyRequestMatcher`对象。里面`matches(HttpServletRequest request)`直接返回 ture
3. `authenticated()`也就是授权,`ExpressionInterceptUrlRegistry#addMapping`。里面放入了一个`UrlMapping``UrlMapping`
的俩个属性,一个是`AnyRequestMatcher`(所有请求)`configAttrs`表示`SecurityConfig`。`SecurityConfig`
的值为`private static final String authenticated = "authenticated"`
> http.formLogin();
创建了一个`FormLoginConfigurer`,也就是`SecurityConfigurer`.关注`init`和`configure`方法.后面统一讲解
创建了一个`FormLoginConfigurer`,也就是`SecurityConfigurer`。关注`init`和`configure`方法。后面统一讲解
> http.httpBasic();
@ -596,12 +635,13 @@ security事先使用这个类把预加载的类全部排序好,然后每次add
> http.build()
进行构建,这个就是非常重要的一个方法,build对象,老规矩.进入`AbstractConfiguredSecurityBuilder#doBuild()`方法
进行构建这个就是非常重要的一个方法build 对象,老规矩。进入`AbstractConfiguredSecurityBuilder#doBuild()`方法
`beforeInit();`: 还是没有什么
`init()`: 调用里面所有的`configurers`里面的`init方法`,后面`HttpSecurity#doBuild`统一讲解,先把流程捋一遍
`init()`: 调用里面所有的`configurers`里面的`init方法`,后面`HttpSecurity#doBuild`统一讲解,先把流程捋一遍
接下来`SecurityFilterChain`就已经创建好了,看一下里面的方法
接下来`SecurityFilterChain`就已经创建好了,看一下里面的方法
```java
/**
* Defines a filter chain which is capable of being matched against an
@ -619,18 +659,22 @@ public interface SecurityFilterChain {
List<Filter> getFilters();
}
```
肯定是先匹配,如果成功了,就返回里面所有的filter进行过滤,比如刚刚设置的所有请求需要登录,也还有我们需要排除的请求
`SecurityAutoConfiguration`类就已经大致讲完了,
肯定是先匹配,如果成功了,就返回里面所有的 filter 进行过滤,比如刚刚设置的所有请求需要登录,也还有我们需要排除的请求
`SecurityAutoConfiguration`类就已经大致讲完了,
### @EnableGlobalAuthentication
当前注解在`@EnableSecurity`中会自动加上
#### @Import(AuthenticationConfiguration.class)
`AuthenticationConfiguration`上面`@Import(ObjectPostProcessorConfiguration.class)`. 以前使用的`ObjectPostProcessor`就是在这里注入的,注入`AutowireBeanFactoryObjectPostProcessor`对象
`AuthenticationConfiguration`上面`@Import(ObjectPostProcessorConfiguration.class)`。 以前使用的`ObjectPostProcessor`
就是在这里注入的,注入`AutowireBeanFactoryObjectPostProcessor`对象
#### AuthenticationManagerBuilder
```java
@Bean
public AuthenticationManagerBuilder authenticationManagerBuilder(ObjectPostProcessor<Object> objectPostProcessor,
@ -645,19 +689,23 @@ public interface SecurityFilterChain {
return result;
}
```
这里面返回了一个`AuthenticationManagerBuilder`的bean,也就是上面``HttpSecurityConfiguration#httpSecurity()`的时候需要的类,这个类也是一个`SecurityBuilder`.
这里面返回了一个`AuthenticationManagerBuilder`的 bean也就是上面``
HttpSecurityConfiguration#httpSecurity()`的时候需要的类,这个类也是一个`SecurityBuilder`。
> LazyPasswordEncoder defaultPasswordEncoder = new LazyPasswordEncoder(context);
首先创建了一个`LazyPasswordEncoder`,就是`PasswordEncoder`,用来管理密码的
首先创建了一个`LazyPasswordEncoder`,就是`PasswordEncoder`用来管理密码的
> AuthenticationEventPublisher authenticationEventPublisher = getAuthenticationEventPublisher(context);
这个就是在`SecurityAutoConfiguration`中创建的security的发布订阅,用来订阅事件
这个就是在`SecurityAutoConfiguration`中创建的 springSecurity 的发布订阅,用来订阅事件
> DefaultPasswordEncoderAuthenticationManagerBuilder result = new DefaultPasswordEncoderAuthenticationManagerBuilder(
> objectPostProcessor, defaultPasswordEncoder);
> DefaultPasswordEncoderAuthenticationManagerBuilder result = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, defaultPasswordEncoder);
就是`AuthenticationManagerBuilder`的真正实现了。接下来回到`getAuthenticationManager()`方法
就是`AuthenticationManagerBuilder`的真正实现了.接下来回到`getAuthenticationManager()`方法
```java
public AuthenticationManager getAuthenticationManager()throws Exception{
if(this.authenticationManagerInitialized){
@ -678,16 +726,19 @@ public AuthenticationManager getAuthenticationManager() throws Exception {
return this.authenticationManager;
}
```
> AuthenticationManagerBuilder authBuilder = this.applicationContext.getBean(AuthenticationManagerBuilder.class);
获取到`DefaultPasswordEncoderAuthenticationManagerBuilder`.
获取到`DefaultPasswordEncoderAuthenticationManagerBuilder`
```java
for(GlobalAuthenticationConfigurerAdapter config:this.globalAuthConfigurers){
authBuilder.apply(config);
}
```
需要注意的是,`this.globalAuthConfigurers`就是上面三个类,
需要注意的是,`this.globalAuthConfigurers`就是上面三个类,
```java
@Bean
public static GlobalAuthenticationConfigurerAdapter enableGlobalAuthenticationAutowiredConfigurer(
@ -707,9 +758,10 @@ public AuthenticationManager getAuthenticationManager() throws Exception {
return new InitializeAuthenticationProviderBeanManagerConfigurer(context);
}
```
调用了`apply`也就是`add`方法.添加到`configurers`中
然后调用`build`并返回. 又是到了 `doBuild()` 这里
调用了`apply`也就是`add`方法。添加到`configurers`中
然后调用`build`并返回。 又是到了 `doBuild()` 这里
> beforeInit();
@ -718,9 +770,14 @@ public AuthenticationManager getAuthenticationManager() throws Exception {
> init();
上面三个类的`init`方法
1. `EnableGlobalAuthenticationAutowiredConfigurer#init`
2. `InitializeUserDetailsBeanManagerConfigurer#init` 调用了`auth.apply(new InitializeUserDetailsManagerConfigurer());` 这个类比上面类名字少了一个bean,并且没有后init方法 只有`configure`方法. 里面创建的`DaoAuthenticationProvider`,里面默认有一个`passwordEncoder`,在无参构造方法里面.而`UserDetailsService`和`DaoAuthenticationProvider`是同一个,也就是在`UserDetailsServiceAutoConfiguration#inMemoryUserDetailsManager`这里创建的.里面继承了,所以是同一个
3. `InitializeAuthenticationProviderBeanManagerConfigurer#init`跟2一样,apply了一个`InitializeAuthenticationProviderManagerConfigurer`
2. `InitializeUserDetailsBeanManagerConfigurer#init` 调用了`auth.apply(new InitializeUserDetailsManagerConfigurer());`
这个类比上面类名字少了一个 bean并且没有后 init 方法 只有`configure`方法。 里面创建的`DaoAuthenticationProvider`
,里面默认有一个`passwordEncoder`,在无参构造方法里面。而`UserDetailsService`和`DaoAuthenticationProvider`
是同一个,也就是在`UserDetailsServiceAutoConfiguration#inMemoryUserDetailsManager`这里创建的。里面继承了,所以是同一个
3. `InitializeAuthenticationProviderBeanManagerConfigurer#init`
跟 2 一样apply 了一个`InitializeAuthenticationProviderManagerConfigurer`
> beforeConfigure();
@ -756,10 +813,12 @@ public AuthenticationManager getAuthenticationManager() throws Exception {
auth.authenticationProvider(provider);
}
```
获取所有`UserDetailsService`和`PasswordEncoder`和`UserDetailsPasswordService`,使用`DaoAuthenticationProvider`进行管理,然后添加到`AuthenticationManagerBuilder#authenticationProviders`中
获取所有`UserDetailsService`和`PasswordEncoder`和`UserDetailsPasswordService`,使用`DaoAuthenticationProvider`
进行管理,然后添加到`AuthenticationManagerBuilder#authenticationProviders`中
3: `InitializeAuthenticationProviderManagerConfigurer#configure`方法,把spring中的所有`AuthenticationProvider`添加到`AuthenticationManagerBuilder#authenticationProviders`中
3: `InitializeAuthenticationProviderManagerConfigurer#configure`方法,把 spring 中的所有`AuthenticationProvider`
添加到`AuthenticationManagerBuilder#authenticationProviders`中
然后又到了熟悉的`AuthenticationManagerBuilder#performBuild`
@ -775,29 +834,28 @@ ProviderManager providerManager = new ProviderManager(this.authenticationProvide
providerManager=postProcess(providerManager);
return providerManager;
```
首先使用`ProviderManager`管理`authenticationProviders`和`parentAuthenticationManager`,这里的`eraseCredentials`和`CredentialsContainer`类有关,也就是敏感数据.接着的`eventPublisher`就是发布订阅了,默认会创建的
然后`providerManager = postProcess(providerManager);`就是注入spring容器中,接着返回
,这里返回的其实是`ProviderManager`对象了,接着就是到了`HttpSecurity`的创建了,后面`HttpSecurity#doBuild()`时候再讲`HttpSecurity`的构建
这里面的`LazyPasswordEncoder`这个类也很有意思,手动制造一个懒加载类
首先使用`ProviderManager`管理`authenticationProviders`和`parentAuthenticationManager`,这里的`eraseCredentials`
和`CredentialsContainer`类有关,也就是敏感数据。接着的`eventPublisher`就是发布订阅了,默认会创建的
然后`providerManager = postProcess(providerManager);`就是注入 spring 容器中,接着返回
,这里返回的其实是`ProviderManager`对象了,接着就是到了`HttpSecurity`的创建了,后面`HttpSecurity#doBuild()`
时候再讲`HttpSecurity`的构建
这里面的`LazyPasswordEncoder`这个类也很有意思,手动制造一个懒加载类
## @EnableGlobalMethodSecurity
这里有个很坑的地方,里面的`prePostEnabled`,`securedEnabled`这些属性,不是直接在`GlobalMethodSecuritySelector`中进行处理的,放在了`GlobalMethodSecuritySelector#methodSecurityMetadataSource`这个bean里面进行处理,然后开启`prePostEnabled`之后,就会加载`PrePostAnnotationSecurityMetadataSource`类. 这个我找了半天,后面无意中才发现
这里有个很坑的地方,里面的`prePostEnabled``securedEnabled`这些属性,不是直接在`GlobalMethodSecuritySelector`
中进行处理的,放在了`GlobalMethodSecuritySelector#methodSecurityMetadataSource`
这个 bean 里面进行处理,然后开启`prePostEnabled`之后,就会加载`PrePostAnnotationSecurityMetadataSource`类。 这个我找了半天,后面无意中才发现
这个注解也添加了`@EnableGlobalAuthentication`注解
主要是看`GlobalMethodSecuritySelector`类
里面加载了`AutoProxyRegistrar`,这个就是springaop的类,创建代理对象的一个类.会创建`InfrastructureAdvisorAutoProxyCreator`类来创建代理对象.关键是`GlobalMethodSecurityConfiguration`和`MethodSecurityMetadataSourceAdvisorRegistrar`这俩个类
里面加载了`AutoProxyRegistrar`,这个就是 springaop 的类,创建代理对象的一个类。会创建`InfrastructureAdvisorAutoProxyCreator`
类来创建代理对象。关键是`GlobalMethodSecurityConfiguration`和`MethodSecurityMetadataSourceAdvisorRegistrar`这俩个类
`MethodSecurityMetadataSourceAdvisorRegistrar`类里面都用到了`GlobalMethodSecurityConfiguration`.我就放在一起了
`MethodSecurityMetadataSourceAdvisorRegistrar`类里面都用到了`GlobalMethodSecurityConfiguration`我就放在一起了
### MethodSecurityMetadataSourceAdvisorRegistrar
@ -805,9 +863,11 @@ ProviderManager providerManager = new ProviderManager(this.authenticationProvide
#### MethodSecurityMetadataSourceAdvisor
这个类就是`PointcutAdvisor`,使用`AbstractAutoProxyCreator`创建代理对象的是,会获取`Pointcut`来判断是否需要代理对象,然后使用`Advice`来进行其余操作.这是springaop的内容就不过多讲解了
这个类就是`PointcutAdvisor`,使用`AbstractAutoProxyCreator`创建代理对象的是,会获取`Pointcut`
来判断是否需要代理对象,然后使用`Advice`来进行其余操作。这是 springaop 的内容就不过多讲解了
aop 首先获取`pointcut`,进行匹配,当前的为
aop首先获取`pointcut`,进行匹配,当前的为
```java
class MethodSecurityMetadataSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
@ -819,24 +879,29 @@ class MethodSecurityMetadataSourcePointcut extends StaticMethodMatcherPointcut i
}
```
也就是`StaticMethodMatcherPointcut`,`ClassFilter`默认都是true,方法匹配为`MethodSecurityMetadataSourceAdvisor#attributeSource`进行匹配.而`methodSecurityMetadataSource`是在`GlobalMethodSecurityConfiguration#methodSecurityMetadataSource`里面进行创建的.这俩个类后面讲.只要匹配成功就和aop一样流程了
这里的`Advice`就是`MethodSecurityInterceptor`类.在`GlobalMethodSecurityConfiguration#methodSecurityInterceptor`中创建
也就是`StaticMethodMatcherPointcut``ClassFilter`
默认都是 true方法匹配为`MethodSecurityMetadataSourceAdvisor#attributeSource`进行匹配。而`methodSecurityMetadataSource`
是在`GlobalMethodSecurityConfiguration#methodSecurityMetadataSource`里面进行创建的。这俩个类后面讲。只要匹配成功就和 aop 一样流程了
这里的`Advice`就是`MethodSecurityInterceptor`类。在`GlobalMethodSecurityConfiguration#methodSecurityInterceptor`中创建
##### MethodSecurityInterceptor
> isPrePostEnabled
添加`PrePostAnnotationSecurityMetadataSource`类,主要关注`getAttributes`方法.后面会讲. 这里面就是我们常用的注解了,然后构建成`ConfigAttribute`并返回.里面的构建主要用的是`PrePostInvocationAttributeFactory`的实现,只有一个实现
添加`PrePostAnnotationSecurityMetadataSource`类,主要关注`getAttributes`方法后面会讲。
这里面就是我们常用的注解了,然后构建成`ConfigAttribute`并返回。里面的构建主要用的是`PrePostInvocationAttributeFactory`
的实现,只有一个实现
> isSecuredEnabled
这个就是`@Secured`注解的处理. 逻辑基本和上面一样
最后返回一个`DelegatingMethodSecurityMetadataSource`对象,就是`MethodSecurityInterceptor`中用到的对象
这个就是`@Secured`注解的处理。 逻辑基本和上面一样
最后返回一个`DelegatingMethodSecurityMetadataSource`对象,就是`MethodSecurityInterceptor`中用到的对象
匹配成功的 aop 都会进入`MethodSecurityInterceptor#invoke`
```java
@Override
public Object invoke(MethodInvocation mi)throws Throwable{
@ -851,15 +916,17 @@ class MethodSecurityMetadataSourcePointcut extends StaticMethodMatcherPointcut i
return super.afterInvocation(token,result);
}
```
这个一看就是标准 aop
###### super.beforeInvocation(mi)
这个里面就有授权了,`Authorization`和`authentication`不一样,一个是认证一个是授权.这个是授权,简单说就是角色
这个里面就有授权了`Authorization`和`authentication`不一样,一个是认证一个是授权。这个是授权,简单说就是角色
> Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object);
接着来到了`DelegatingMethodSecurityMetadataSource#getAttributes`
```java
for(MethodSecurityMetadataSource s:this.methodSecurityMetadataSources){
attributes=s.getAttributes(method,targetClass);
@ -868,8 +935,10 @@ class MethodSecurityMetadataSourcePointcut extends StaticMethodMatcherPointcut i
}
}
```
`this.methodSecurityMetadataSources`里面的值,就是`GlobalMethodSecurityConfiguration#methodSecurityMetadataSource`里面的`sources`.
构建出来返回`attributes`.
`this.methodSecurityMetadataSources`里面的值,就是`GlobalMethodSecurityConfiguration#methodSecurityMetadataSource`
里面的`sources`.
构建出来返回`attributes`。
> Authentication authenticated = authenticateIfRequired();
@ -877,10 +946,10 @@ class MethodSecurityMetadataSourcePointcut extends StaticMethodMatcherPointcut i
> attemptAuthorization(object, attributes, authenticated);
使用`accessDecisionManager`进行授权.放到`MethodInterceptor`中进行讲解
使用`accessDecisionManager`进行授权放到`MethodInterceptor`中进行讲解
里面还有授权失败发布事件`publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated, ex));`
接着就是授权成功发送事件,接着就是返回一个`InterceptorStatusToken`对象
接着就是授权成功发送事件接着就是返回一个`InterceptorStatusToken`对象
###### result = mi.proceed();
@ -888,12 +957,11 @@ class MethodSecurityMetadataSourcePointcut extends StaticMethodMatcherPointcut i
###### super.finallyInvocation(token);
是否刷新`InterceptorStatusToken`,前面传参是false
是否刷新`InterceptorStatusToken`,前面传参是 false
###### return super.afterInvocation(token, result);
后处理器,与前处理器基本一样.剩下的`MethodInterceptor`中进行讲解
后处理器,与前处理器基本一样。剩下的`MethodInterceptor`中进行讲解
#### MethodInterceptor
@ -905,7 +973,7 @@ class MethodSecurityMetadataSourcePointcut extends StaticMethodMatcherPointcut i
##### accessDecisionManager()
这个就是`AbstractSecurityInterceptor#attemptAuthorization`的授权方法.
这个就是`AbstractSecurityInterceptor#attemptAuthorization`的授权方法
```java
protected AccessDecisionManager accessDecisionManager(){
@ -928,13 +996,14 @@ class MethodSecurityMetadataSourcePointcut extends StaticMethodMatcherPointcut i
return new AffirmativeBased(decisionVoters);
}
```
前提条件必须开启`prePostEnabled`
这里面返回的也是`AffirmativeBased`.有时候我们自定义也会使用这个.只要有一个`AccessDecisionVoter`通过就认为是有权限的.这里就不过多讲解了
这里面返回的也是`AffirmativeBased`,有时候我们自定义也会使用这个,只要有一个`AccessDecisionVoter`通过就认为是有权限的,这里就不过多讲解了
里面的`GrantedAuthorityDefaults`对象,也可以是我们自定义的一个前缀.默认前缀为`ROLE_`
里面的`GrantedAuthorityDefaults`对象,也可以是我们自定义的一个前缀,默认前缀为`ROLE_`
我们一般自定义的,会使用`.accessDecisionManager(accessDecisionManager())`.在`HttpSecurity#doBuild()`中进行讲解
我们一般自定义的,会使用`.accessDecisionManager(accessDecisionManager())`在`HttpSecurity#doBuild()`中进行讲解
##### afterInvocationManager
@ -944,20 +1013,19 @@ class MethodSecurityMetadataSourcePointcut extends StaticMethodMatcherPointcut i
这个就是`MethodSecurityMetadataSource`对象了
总结一下这里,就是实现springaop的`AbstractPointcutAdvisor`对象`MethodSecurityMetadataSourceAdvisor`. 进行aop加载,处理
总结一下这里,就是实现 springaop 的`AbstractPointcutAdvisor`对象`MethodSecurityMetadataSourceAdvisor` 进行 aop 加载,处理
## EnableMethodSecurity
这个注解没有`@EnableGlobalMethodSecurity`这么强大.代码基本跟`@EnableGlobalMethodSecurity`一样
这个注解没有`@EnableGlobalMethodSecurity`这么强大代码基本跟`@EnableGlobalMethodSecurity`一样
## HttpSecurity#doBuild()
接下来是最后一块内容了,主要是看里面初始化,构建了哪些类
接下来是最后一块内容了,主要是看里面初始化,构建了哪些类
又是熟悉的`AbstractConfiguredSecurityBuilder#doBuild()`
首先看看我们一开始创建`HttpSecurity`的时候添加了哪些类. `HttpSecurityConfiguration#httpSecurity()`
首先看看我们一开始创建`HttpSecurity`的时候添加了哪些类 `HttpSecurityConfiguration#httpSecurity()`
```java
@Bean(HTTPSECURITY_BEAN_NAME)
@ -989,21 +1057,19 @@ class MethodSecurityMetadataSourcePointcut extends StaticMethodMatcherPointcut i
}
```
`CsrfConfigurer`,`ExceptionHandlingConfigurer`,`HeadersConfigurer`,`SessionManagementConfigurer`,`SecurityContextConfigurer`,`RequestCacheConfigurer`,`AnonymousConfigurer`,`ServletApiConfigurer`,`DefaultLoginPageConfigurer`,`LogoutConfigurer`
`CsrfConfigurer``ExceptionHandlingConfigurer``HeadersConfigurer``SessionManagementConfigurer``SecurityContextConfigurer``RequestCacheConfigurer``AnonymousConfigurer``ServletApiConfigurer``DefaultLoginPageConfigurer``LogoutConfigurer`
还有我们添加到`META-INF/spring.factories`中的`AbstractHttpConfigurer.class`类
接着回到这里. 我们是自定义了一个`SecurityFilterChain`.所以在这里面进行构建
接着回到这里, 我们是自定义了一个`SecurityFilterChain`,所以在这里面进行构建
首先` http.formLogin();`添加了`FormLoginConfigurer`
`http.authorizeRequests()` 添加了`ExpressionUrlAuthorizationConfigurer`.这个只有`configure` 没有`init`
`http.authorizeRequests()` 添加了`ExpressionUrlAuthorizationConfigurer`这个只有`configure` 没有`init`
` http.csrf()` 添加了`CsrfConfigurer`
`http.userDetailsService(userDetailsService())` 添加了一个自定义的`UserDetailsService`
### FormLoginConfigurer
也是一个`SecurityConfigurer`
@ -1017,6 +1083,7 @@ this.authenticationEntryPoint = new LoginUrlAuthenticationEntryPoint(loginPage);
```
接着来到`init`
```java
@Override
public void init(B http)throws Exception{
@ -1048,27 +1115,29 @@ this.authenticationEntryPoint = new LoginUrlAuthenticationEntryPoint(loginPage);
```
`loginProcessingUrl(this.loginPage);`
1. 设置登录页面
2. `this.authFilter`也就是`UsernamePasswordAuthenticationFilter`的`RequestMatcher`设置为`new AntPathRequestMatcher(loginProcessingUrl, "POST")`
1. 设置登录页面
2. `this.authFilter`也就是`UsernamePasswordAuthenticationFilter`的`RequestMatcher`
设置为`new AntPathRequestMatcher(loginProcessingUrl, "POST")`
`failureUrl(this.loginPage + "?error");`
1. 设置失败页面
2. `this.failureHandler`设置为`new SimpleUrlAuthenticationFailureHandler(authenticationFailureUrl)`里面的`authenticationFailureUrl`是`/login + "?error"`
`getBuilder().getConfigurer(LogoutConfigurer.class);` 就是前面加入的那一堆`Configurer`中的一个.这个默认就是当前设置的值,不用理会
1. 设置失败页面
2. `this.failureHandler`设置为`new SimpleUrlAuthenticationFailureHandler(authenticationFailureUrl)`
里面的`authenticationFailureUrl`是`/login + "?error"`
`getBuilder().getConfigurer(LogoutConfigurer.class);` 就是前面加入的那一堆`Configurer`中的一个.这个默认就是当前设置的值,不用理会
> updateAccessDefaults(http);
里面默认为false.
里面默认为 false
> registerDefaultAuthenticationEntryPoint(http);
获取上面`Configurer`里面的`ExceptionHandlingConfigurer`.
获取上面`Configurer`里面的`ExceptionHandlingConfigurer`
在`ExceptionHandlingConfigurer`中有俩个属性. `defaultEntryPointMappings`和`defaultDeniedHandlerMappings`. 基本看注释就能知道是做什么的 .这个注释是map的value类上的注释
在`ExceptionHandlingConfigurer`中有俩个属性, `defaultEntryPointMappings`和`defaultDeniedHandlerMappings` 基本看注释就能知道是做什么的
,这个注释是 map 的 value 类上的注释
```java
/**
@ -1084,18 +1153,23 @@ this.authenticationEntryPoint = new LoginUrlAuthenticationEntryPoint(loginPage);
private LinkedHashMap<RequestMatcher, AccessDeniedHandler> defaultDeniedHandlerMappings=new LinkedHashMap<>();
```
我们这里的是添加`defaultDeniedHandlerMappings`. `key`是`RequestMatcher`.是否匹配.`value`是匹配成功就执行 这个类里面也是只有`configure()`,没有`init()`. 后面讲解
我们这里的是添加`defaultDeniedHandlerMappings` `key`是`RequestMatcher`,是否匹配。 `value`是匹配成功就执行
这个类里面也是只有`configure()`,没有`init()` 后面讲解
先说里面的`value`,就是当前类的`this.authenticationEntryPoint`.也就是创建类时候的`LoginUrlAuthenticationEntryPoint`
先说里面的`value`,就是当前类的`this.authenticationEntryPoint` 也就是创建类时候的`LoginUrlAuthenticationEntryPoint`
`key`就是`AndRequestMatcher`.但是里面聚合了俩个`RequestMatcher`. 一个是`MediaTypeRequestMatcher`,还有一个是`NegatedRequestMatcher`
`key`就是`AndRequestMatcher`,但是里面聚合了俩个`RequestMatcher` 一个是`MediaTypeRequestMatcher`
,还有一个是`NegatedRequestMatcher`
其实到了这一步,我们只需要了解其中一个,剩下的都大同小异了.
其实到了这一步,我们只需要了解其中一个,剩下的都大同小异了
举例: `ExceptionHandlingConfigurer`
`ExceptionHandlingConfigurer`这个类就是刚刚在`FormLoginConfigurer`中处理的那个.往这个里面添加了`defaultEntryPointMappings`属性.
然后我们找到`ExceptionHandlingConfigurer`中的` configure(H http) `方法, 里面就是创建了一个`ExceptionTranslationFilter`过滤器,添加到了`http`中. 代码是这一段
`ExceptionHandlingConfigurer`这个类就是刚刚在`FormLoginConfigurer`
中处理的那个,往这个里面添加了`defaultEntryPointMappings`属性
然后我们找到`ExceptionHandlingConfigurer`中的`configure(H http)`方法, 里面就是创建了一个`ExceptionTranslationFilter`
过滤器,添加到了`http`中, 代码是这一段
```java
@Override
@ -1109,7 +1183,13 @@ this.authenticationEntryPoint = new LoginUrlAuthenticationEntryPoint(loginPage);
http.addFilter(exceptionTranslationFilter);
}
```
接着我们打开`ExceptionTranslationFilter`,这就是一个`Filter`,找到`doFilter(ServletRequest request, ServletResponse response, FilterChain chain)`方法,就是在处理`catch (Exception ex) {`的时候,做的一些事情.接着继续打开`handleSpringSecurityException(HttpServletRequest request, HttpServletResponse response,FilterChain chain, RuntimeException exception)`方法,
接着我们打开`ExceptionTranslationFilter`,这就是一个`Filter`
,找到`doFilter(ServletRequest request, ServletResponse response, FilterChain chain)`
方法,就是在处理`catch (Exception ex) {`
的时候,做的一些事情,接着继续打开`handleSpringSecurityException(HttpServletRequest request, HttpServletResponse response,FilterChain chain, RuntimeException exception)`
方法,
```java
private void handleSpringSecurityException(HttpServletRequest request,HttpServletResponse response,
FilterChain chain,RuntimeException exception)throws IOException,ServletException{
@ -1121,7 +1201,9 @@ this.authenticationEntryPoint = new LoginUrlAuthenticationEntryPoint(loginPage);
}
}
```
发现最后还是到了
```java
protected void sendStartAuthentication(HttpServletRequest request,HttpServletResponse response,FilterChain chain,
AuthenticationException reason)throws ServletException,IOException{
@ -1133,7 +1215,10 @@ this.authenticationEntryPoint = new LoginUrlAuthenticationEntryPoint(loginPage);
this.authenticationEntryPoint.commence(request,response,reason);
}
```
里面就有`this.authenticationEntryPoint.commence(request, response, reason);`这段代码. 创建的是`DelegatingAuthenticationEntryPoint`.
里面就有`this.authenticationEntryPoint.commence(request, response, reason);`这段代码,
创建的是`DelegatingAuthenticationEntryPoint`
```java
@Override
public void commence(HttpServletRequest request,HttpServletResponse response,
@ -1152,12 +1237,12 @@ this.authenticationEntryPoint = new LoginUrlAuthenticationEntryPoint(loginPage);
this.defaultEntryPoint.commence(request,response,authException);
}
```
基本上流程就完成了,请求先走过滤器.然后走不同的filter,报错就到了这一步.进行错误处理.其余都基本一致了.
基本上流程就完成了,请求先走过滤器,然后走不同的 filter报错就到了这一步进行错误处理其余都基本一致了
## 备注
剩下的一些处理基本和上面这个流程一致,还有几个注解需要注意下
剩下的一些处理基本和上面这个流程一致还有几个注解需要注意下
`@CsrfToken`
@ -1165,9 +1250,10 @@ this.authenticationEntryPoint = new LoginUrlAuthenticationEntryPoint(loginPage);
`@AuthenticationPrincipal`
这三个注解是在`WebMvcSecurityConfiguration`类进行处理的,只要启动了`@EnableWebSecurity`注解,就会启动
这三个注解是在`WebMvcSecurityConfiguration`类进行处理的,只要启动了`@EnableWebSecurity`注解,就会启动
```java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@ -1185,7 +1271,9 @@ public @interface EnableWebSecurity {
}
```
使用方法
```java
@GetMapping("/get")
// public Users getUser(String username,@CsrfToken CsrfToken token, @AuthenticationPrincipal Users customUser, @CurrentSecurityContext Authentication authentication) {
@ -1194,5 +1282,6 @@ public @interface EnableWebSecurity {
}
```
里面的`SpringWebMvcImportSelector`类注入了`WebMvcSecurityConfiguration`.这就是springmvc中`HandlerMethodArgumentResolver`的处理,也就是参数的处理,比如我们添加的`@PathVariable`,`@RequestBody`等,都是`HandlerMethodArgumentResolver`的实现类处理的,当然还有`HandlerMethodReturnValueHandler`.这些就是`DispatcherServlet`里面的处理了
里面的`SpringWebMvcImportSelector`类注入了`WebMvcSecurityConfiguration`,这就是 springmvc 中`HandlerMethodArgumentResolver`
的处理,也就是参数的处理,比如我们添加的`@PathVariable``@RequestBody`等,都是`HandlerMethodArgumentResolver`
的实现类处理的,当然还有`HandlerMethodReturnValueHandler`,这些就是`DispatcherServlet`里面的处理了

Loading…
Cancel
Save