You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

50 KiB

springSecurity流程补充

注意:

  1. 基于spring-boot-dependencies:2.7.7
  2. 首先需要了解springboot2.7升级 Changes to Auto-configuration 以后使用autoconfigure进行自动注入
  3. 代码地址io.github.poo0054

启动

我们每次添加 <artifactId>spring-boot-starter-security</artifactId> ,启动的时候启动日志会有一条类似 `Using generated security password: 1db8eb87-e2ee-4c72-88e7-9b85268c4430

This generated password is for development use only. Your security configuration must be updated before running your application in production.`

的日志.找到UserDetailsServiceAutoConfiguration#InMemoryUserDetailsManager类,它是springboot自动装配的.

下面这些都是springboot自动装配类,在spring-boot-autoconfigure-2.7.7.jar>META-INF>spring> org.springframework.boot.autoconfigure.AutoConfiguration.imports中. 这些类就是security的全部了.

org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration
org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration
org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration
org.springframework.boot.autoconfigure.security.rsocket.RSocketSecurityAutoConfiguration
org.springframework.boot.autoconfigure.security.saml2.Saml2RelyingPartyAutoConfiguration
..........
org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration
org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration
org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration
org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration

SecurityAutoConfiguration


/**
 * {@code EnableAutoConfiguration} for Spring Security.
 *
 * @author Dave Syer
 * @author Andy Wilkinson
 * @author Madhura Bhave
 * @since 1.0.0
 */
@AutoConfiguration
@ConditionalOnClass(DefaultAuthenticationEventPublisher.class)
@EnableConfigurationProperties(SecurityProperties.class)
@Import({SpringBootWebSecurityConfiguration.class, SecurityDataConfiguration.class})
public class SecurityAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean(AuthenticationEventPublisher.class)
    public DefaultAuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher publisher) {
        return new DefaultAuthenticationEventPublisher(publisher);
    }

}

@EnableConfigurationProperties(SecurityProperties.class)

这个是security的核心配置类SecurityProperties,里面能配置 filter: 过滤,user : 用户信息

这个有个问题,filter是属于tomcat的,security中使用什么方式让filter变的有序的

@Import({ SpringBootWebSecurityConfiguration.class, SecurityDataConfiguration.class })

这里导入了2个类 SpringBootWebSecurityConfigurationSecurityDataConfiguration,SecurityDataConfiguration是Spring Security与Spring数据的集成,暂时不做讲解,重点是SpringBootWebSecurityConfiguration

SpringBootWebSecurityConfiguration

这个类就是一个Configuration类,条件必须为@ConditionalOnWebApplication(type = Type.SERVLET)才会注入

SecurityFilterChainConfiguration

其中第一个子类SecurityFilterChainConfiguration添加了@ConditionalOnDefaultWebSecurity,这个类有个注解 @Conditional(DefaultWebSecurityCondition.class),而DefaultWebSecurityCondition类继承了AllNestedConditions

所以下面代码就是判断该类是否生效,如果不存在SecurityFilterChainWebSecurityConfigurerAdapter 的bean,就生效.创建默认的SecurityFilterChain

/**
 * {@link Condition} for
 * {@link ConditionalOnDefaultWebSecurity @ConditionalOnDefaultWebSecurity}.
 *
 * @author Phillip Webb
 */
class DefaultWebSecurityCondition extends AllNestedConditions {

    DefaultWebSecurityCondition() {
        super(ConfigurationPhase.REGISTER_BEAN);
    }

    @ConditionalOnClass({SecurityFilterChain.class, HttpSecurity.class})
    static class Classes {

    }

    @ConditionalOnMissingBean({
            org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter.class,
            SecurityFilterChain.class})
    @SuppressWarnings("deprecation")
    static class Beans {

    }

}

SecurityFilterChain就是整个springsecurity的流程了,有俩个方法,一个是boolean matches(HttpServletRequest request); ,是否匹配这次请求,匹配成功就获取当前所有Filter进行处理

SecurityFilterChain类会放在最下面单独讲解

ErrorPageSecurityFilterConfiguration

这是第二个子类,主要就是通过FilterRegistrationBean注入了一个ErrorPageSecurityFilter. 用于拦截错误调度,以确保对错误页面的授权访问。

   /**
* Configures the {@link ErrorPageSecurityFilter}.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(WebInvocationPrivilegeEvaluator.class)
@ConditionalOnBean(WebInvocationPrivilegeEvaluator.class)
static class ErrorPageSecurityFilterConfiguration {

   @Bean
   FilterRegistrationBean<ErrorPageSecurityFilter> errorPageSecurityFilter(ApplicationContext context) {
       FilterRegistrationBean<ErrorPageSecurityFilter> registration = new FilterRegistrationBean<>(
               new ErrorPageSecurityFilter(context));
       registration.setDispatcherTypes(DispatcherType.ERROR);
       return registration;
   }

}
WebSecurityEnablerConfiguration

这个类主要就是添加了@EnableWebSecurity注解,这个注解也很重要,后面跟SecurityFilterChain一起讲解

DefaultAuthenticationEventPublisher

在类中还存在SecurityAutoConfigurationbean,这个是属于spring的发布订阅.改装一下,就是security的成功和失败事件,可以订阅失败后的一些处理,如日志打印等

/**
 * @author Luke Taylor
 * @since 3.0
 */
public interface AuthenticationEventPublisher {

    void publishAuthenticationSuccess(Authentication authentication);

    void publishAuthenticationFailure(AuthenticationException exception, Authentication authentication);

}

UserDetailsServiceAutoConfiguration

注入条件


@ConditionalOnBean(ObjectPostProcessor.class)
@ConditionalOnMissingBean(
        value = {AuthenticationManager.class, AuthenticationProvider.class, UserDetailsService.class,
                AuthenticationManagerResolver.class},
        type = {"org.springframework.security.oauth2.jwt.JwtDecoder",
                "org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector",
                "org.springframework.security.oauth2.client.registration.ClientRegistrationRepository",
                "org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository"})

InMemoryUserDetailsManager

UserDetailsManager的非持久化实现支持内存映射。 主要用于测试和演示目的,其中不需要完整的持久系统

SecurityFilterAutoConfiguration

Spring Security的过滤器 自动配置。与SpringBootWebSecurityConfiguration分开配置以确保在存在用户提供的WebSecurityConfiguration时过滤器的顺序仍然被配置。

DelegatingFilterProxyRegistrationBean

这个类是继承了AbstractFilterRegistrationBean,FilterRegistrationBean也是继承的AbstractFilterRegistrationBean .但是DelegatingFilterProxyRegistrationBean是使用的一个targetBeanName 找到bean.进行注入.其中private static final String DEFAULT_FILTER_NAME = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME; 也就是springSecurityFilterChain

其中有俩个属性, Order和DispatcherTypes

  • Order默认为-100

  • DispatcherType就是DispatcherType

    private Set<DispatcherType> dispatcherTypes = new HashSet<>( Arrays.asList(DispatcherType.ASYNC, DispatcherType.ERROR, DispatcherType.REQUEST));

注意: 这里需要了解一下DelegatingFilterProxyRegistrationBean以及spring如何整合filter和mvc的.security核心就是filter

img.pngimage.png DelegatingFilterProxyRegistrationBeanFilterRegistrationBean都是继承的RegistrationBean,而RegistrationBean 又是ServletContextInitializer的实现类.其中void onStartup(ServletContext servletContext)方法是关键. 在javax.servlet 中,存在这样一个类

public interface ServletContainerInitializer {

    /**
     * Receives notification during startup of a web application of the classes
     * within the web application that matched the criteria defined via the
     * {@link javax.servlet.annotation.HandlesTypes} annotation.
     *
     * @param c     The (possibly null) set of classes that met the specified
     *              criteria
     * @param ctx   The ServletContext of the web application in which the
     *              classes were discovered
     *
     * @throws ServletException If an error occurs
     */
    void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException;

springboot中的TomcatStarter继承了这个类,而这个类又是spring启动的核心AbstractApplicationContext#refresh() 中的onRefresh();方法.

找到实现类ServletWebServerApplicationContextonRefresh()方法.里面有createWebServer();方法. 在里面有创建webServer的方法.this.webServer = factory.getWebServer(getSelfInitializer());这个就是创建tomcat的工厂. TomcatServletWebServerFactory#getWebServer(ServletContextInitializer... initializers) .里面就是创建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中. 然后AbstractFilterRegistrationBean#addRegistration里面添加filter return servletContext.addFilter(getOrDeduceName(filter), filter);这样每次请求servlet,tomcat就会先使用filter过滤器进行拦截

简单来说就是TomcatStarter继承了ServletContainerInitializer.tomcat会调用onStartup 方法,在这个方法里面会调用ServletContextInitializer#onStartup.在这个里面有filter和其余需要整合ServletContext的方法

比如springSecurityFilterChain使用的是DelegatingFilterProxyRegistrationBean,需要使用bean去获取getFilter .而ErrorPageFilter使用的是FilterRegistrationBean.直接就可以注入

@EnableWebSecurity

这个就是security的核心注解 需要注意,@EnableWebMvcSecurity已经弃用,请使用@EnableWebSecurity


@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import({WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class,
        HttpSecurityConfiguration.class})
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {

    /**
     * Controls debugging support for Spring Security. Default is false.
     * @return if true, enables debug support with Spring Security
     */
    boolean debug() default false;

}

WebSecurityConfiguration

首先先看类注释(以后都默认翻译成简体中文.ali翻译):

使用WebSecurity创建执行Spring安全web安全的FilterChainProxy。然后导出必要的bean。 可以通过实现WebSecurityConfigurer WebSecurityConfigurer并将其公开为Configuration或公开WebSecurityCustomizer bean来进行自定义。 使用EnableWebSecurity时会导入该配置。

setFilterChainProxySecurityConfigurer

逐行解释:

this.webSecurity = objectPostProcessor.postProcess(new WebSecurity(objectPostProcessor));

ObjectPostProcessor也就是AutowireBeanFactoryObjectPostProcessor.在AuthenticationConfiguration 类上@Import(ObjectPostProcessorConfiguration.class).

AutowireBeanFactoryObjectPostProcessor类里面创建webSecurity AutowireBeanFactoryObjectPostProcessor.postProcess(new WebSecurity(objectPostProcessor)); 使用AutowireCapableBeanFactory创建出WebSecurity AutowireBeanFactoryObjectPostProcessorSmartInitializingSingletonDisposableBean拿出来,使用自己的destroy()afterSingletonsInstantiated()执行

List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new AutowiredWebSecurityConfigurersIgnoreParents(beanFactory).getWebSecurityConfigurers();

AutowiredWebSecurityConfigurersIgnoreParents也就是获取所有的WebSecurityConfigurerAdapter

这里有几个类需要了解SecurityConfigurerSecurityBuilder

先了解一下结构 img_1.png

img_2.png

使用WebSecurity聚合了private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<>(); 也就是WebSecurityConfigurerAdapter(当然还有别的,这里主要讲WebSecurityConfigurerAdapter)

WebSecurityConfigurerAdapter也可以认为就是SecurityConfigurer

WebSecurity也就是SecurityBuilder

然后在SecurityBuilder的实现类AbstractConfiguredSecurityBuilderdoBuild()方法进行很多别的操作.

protected final O doBuild() throws Exception {
		synchronized (this.configurers) {
			this.buildState = BuildState.INITIALIZING;
			beforeInit();
			init();
			this.buildState = BuildState.CONFIGURING;
			beforeConfigure();
			configure();
			this.buildState = BuildState.BUILDING;
			O result = performBuild();
			this.buildState = BuildState.BUILT;
			return result;
		}
	}

回到原来地方,返回的webSecurityConfigurers,里面的

for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
			this.webSecurity.apply(webSecurityConfigurer);
		}

然后就到了AbstractConfiguredSecurityBuilder#apply方法,里面调用了add(configurer); 也就是把SecurityConfigurer放入了AbstractConfiguredSecurityBuilder#configurers的一个map中,这样就使用SecurityBuilder聚合了SecurityConfigurer. 在构建的时候可以做一些事情

也就是说使用WebSecurity聚合了SecurityConfigurer(包括WebSecurityConfigurerAdapter)

this.securityFilterChains = securityFilterChains;

获取所有的securityFilterChains

this.webSecurityCustomizers = webSecurityCustomizers;

获取所有webSecurityCustomizers

public Filter springSecurityFilterChain()

这个里面最关键的也就是这个了.

		for (SecurityFilterChain securityFilterChain : this.securityFilterChains) {
			this.webSecurity.addSecurityFilterChainBuilder(() -> securityFilterChain);
			for (Filter filter : securityFilterChain.getFilters()) {
				if (filter instanceof FilterSecurityInterceptor) {
					this.webSecurity.securityInterceptor((FilterSecurityInterceptor) filter);
					break;
				}
			}
		}
		for (WebSecurityCustomizer customizer : this.webSecurityCustomizers) {
			customizer.customize(this.webSecurity);
		}
		return this.webSecurity.build();

首先使用根据获取到的securityFilterChainsset入WebSecurity#securityFilterChainBuilders的List属性

这里有个需要注意的地方,如果你继承了WebSecurityConfigurerAdapter.this.securityFilterChains 就是一个空的.

而且会由WebSecurityConfigurerAdapter#getHttp()进行创建WebSecurity.这就跟spring的order有关了. @Order(SecurityProperties.BASIC_AUTH_ORDER)

其中SpringBootWebSecurityConfiguration#SecurityFilterChainConfiguration有一个注解@ConditionalOnDefaultWebSecurity

@Configuration(proxyBeanMethods = false)
	@ConditionalOnDefaultWebSecurity
	static class SecurityFilterChainConfiguration {

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

	}

这里会创建SecurityFilterChain. 还会有一个HttpSecurity的注入

继续回到上面,

if (filter instanceof FilterSecurityInterceptor) {
					this.webSecurity.securityInterceptor((FilterSecurityInterceptor) filter);
					break;
				}

FilterSecurityInterceptor也在这里进行处理,也就是SecurityMetadataSource元数据

然后自定义的WebSecurityCustomizer也在这里.可以自行改变webSecurity

for (WebSecurityCustomizer customizer : this.webSecurityCustomizers) {
			customizer.customize(this.webSecurity);
		}

接下来就是构建了,来到AbstractConfiguredSecurityBuilder#doBuild()

protected final O doBuild() throws Exception {
		synchronized (this.configurers) {
			this.buildState = BuildState.INITIALIZING;
			beforeInit();
			init();
			this.buildState = BuildState.CONFIGURING;
			beforeConfigure();
			configure();
			this.buildState = BuildState.BUILDING;
			O result = performBuild();
			this.buildState = BuildState.BUILT;
			return result;
		}
	}

init(); 如果继承了WebSecurityConfigurerAdapter,就会在这里创建HttpSecurity

注意: 这里个buildState,用来控制当前状态的

beforeConfigure();

在当前是没有什么处理

configure();

这行代码就是我们每次继承WebSecurityConfigurerAdapter的处理了

O result = performBuild();

然后就到了WebSecurity#performBuild().

  1. 首先排除忽略的RequestMatcher
  2. 添加入securityFilterChainrequestMatcherPrivilegeEvaluatorsEntries
  3. 创建出FilterChainProxybean的名称为springSecurityFilterChain (重点)

剩下的都是一些创建一些bean了.

SecurityExpressionHandler: 默认为DefaultWebSecurityExpressionHandler类(Facade将Spring Security评估安全表达式的要求与基础表达式对象的实现隔离)

WebInvocationPrivilegeEvaluator: 为 WebSecurity#performBuild()中创建的 requestMatcherPrivilegeEvaluatorsEntries 使用RequestMatcherDelegatingWebInvocationPrivilegeEvaluator包装.(允许用户确定他们是否具有给定web URI的特权。)

这俩个类都是很重要的. 一个是解析器,一个是判断uri是否合格的类. 后面单独讲

HttpSecurityConfiguration

接下来到了HttpSecurityConfiguration

根据上面WebSecurityConfiguration,可以得出.WebSecurityConfiguration创建WebSecurity,WebSecurity创建了FilterChainProxy的bean.

HttpSecurityConfiguration创建HttpSecurity. 而在SecurityFilterChainConfiguration类中,使用HttpSecurity创建了SecurityFilterChain.这也就是我们使用了WebSecurityConfigurerAdapter.为什么会存在SecurityFilterChain类的原因.是SecurityFilterChainConfiguration#defaultSecurityFilterChain创建了一个SecurityFilterChain.

得出结论,FilterChainProxy持有SecurityFilterChain.而DelegatingFilterProxyRegistrationBean又持有FilterChainProxy

DelegatingFilterProxyRegistrationBean->FilterChainProxy->SecurityFilterChain

其实到了这一步.后面的就是我们自己编写的代码了比如

 @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        // 配置认证
        http.formLogin();

        // 设置URL的授权问题
        // 多个条件取交集
        http.authorizeRequests()
                // 匹配 / 控制器  permitAll() 不需要被认证就可以访问
                .antMatchers("/login").permitAll()
                .antMatchers("/error").permitAll()
                .antMatchers("/fail").permitAll()
                // anyRequest() 所有请求   authenticated() 必须被认证
                .anyRequest().authenticated();
//				.accessDecisionManager(accessDecisionManager());
        // 关闭csrf
        http.csrf().disable();
        return http.build();
    }

或者继承WebSecurityConfigurerAdapter的类了.

这里我们用security默认的SpringBootWebSecurityConfiguration来举例

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

首先获取HttpSecurity http(这里的HttpSecurity是从HttpSecurityConfiguration里面创建的,如果是自定义的SecurityFilterChain就是从自己写的里面来的)

我们来到HttpSecurityConfiguration#httpSecurity() 先创建一个默认的密码管理器,

接下来进入authenticationBuilder.parentAuthenticationManager(authenticationManager());,这里就是AuthenticationConfiguration里面的处理.这个类后面和springaop加载我们写的注释单独在@EnableGlobalAuthentication注解类说

接着创建 HttpSecurity http = new HttpSecurity(this.objectPostProcessor, authenticationBuilder, createSharedObjects());,

.csrf(withDefaults()) 启用CSRF保护,创建CsrfConfigurer类,并添加入添加入AbstractConfiguredSecurityBuilder#configurers.

.addFilter(new WebAsyncManagerIntegrationFilter())

这个有一个很有意思的类FilterOrderRegistration. 前面问的根据filter是如何包装顺序的.就在这个类里面

FilterOrderRegistration() {
		Step order = new Step(INITIAL_ORDER, ORDER_STEP);
		put(DisableEncodeUrlFilter.class, order.next());
		put(ForceEagerSessionCreationFilter.class, order.next());
		put(ChannelProcessingFilter.class, order.next());
		order.next(); // gh-8105
		put(WebAsyncManagerIntegrationFilter.class, order.next());
		put(SecurityContextHolderFilter.class, order.next());
		put(SecurityContextPersistenceFilter.class, order.next());
		put(HeaderWriterFilter.class, order.next());
		put(CorsFilter.class, order.next());
		put(CsrfFilter.class, order.next());
		put(LogoutFilter.class, order.next());
        // .....

security事先使用这个类把预加载的类全部排序好,然后每次add一个新的filter就会使用这个里面的序号.如果我们有自定义的类,也要提前加载到里面去,不然就会

	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.");

WebAsyncManagerIntegrationFilter: 与处理异步web请求相关的实用程序方法

.exceptionHandling(withDefaults())

这个是异常的处理

提示: 这个里面每次添加一个类,如果在HttpSecurity中调用getOrApply.比如这个代码调用的是exceptionHandlingCustomizer.customize(getOrApply(new ExceptionHandlingConfigurer<>()));. 打开ExceptionHandlingConfigurer类,发现是一个HttpSecurityBuilder, 这样只需要看configure方法大概就能明白这个是一个什么类. 这个就是在filter中添加了一个ExceptionTranslationFilterfilter.主要就是SecurityConfigurer的俩个方法.先调用init(B builder),然后configure(B builder)

后面都是一样,就跳过了

applyDefaultConfigurers(http);

这里的这一句,就是从"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类.

后面调用的.anyRequest(),也就是AbstractRequestMatcherRegistry#anyRequest().先了解一下结构图

img_3.png

完整调用链就是AbstractRequestMatcherRegistry#anyRequest() -> AbstractRequestMatcherRegistry#requestMatchers(RequestMatcher... requestMatchers) -> AbstractConfigAttributeRequestMatcherRegistry#chainRequestMatchers(List<RequestMatcher> requestMatchers) -> ExpressionUrlAuthorizationConfigurer#chainRequestMatchersInternal(List<RequestMatcher> requestMatchers) -> return new AuthorizedUrl(requestMatchers);

http.authorizeRequests().anyRequest().authenticated();是需要所有请求登录后才能访问

  1. authorizeRequests是创建了一个ExpressionUrlAuthorizationConfigurer并添加入configurer中.
  2. anyRequest是创建了一个new AuthorizedUrl(requestMatchers),其中 requestMatchersAnyRequestMatcher.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.关注initconfigure方法.后面统一讲解

http.httpBasic();

HttpBasicConfigurer

http.build()

进行构建,这个就是非常重要的一个方法,build对象,老规矩.进入AbstractConfiguredSecurityBuilder#doBuild()方法 beforeInit();: 还是没有什么

init(): 调用里面所有的configurers里面的init方法,后面HttpSecurity#doBuild统一讲解,先把流程捋一遍

接下来SecurityFilterChain就已经创建好了,看一下里面的方法

/**
 * Defines a filter chain which is capable of being matched against an
 * {@code HttpServletRequest}. in order to decide whether it applies to that request.
 * <p>
 * Used to configure a {@code FilterChainProxy}.
 *
 * @author Luke Taylor
 * @since 3.1
 */
public interface SecurityFilterChain {

	boolean matches(HttpServletRequest request);

	List<Filter> getFilters();
}

肯定是先匹配,如果成功了,就返回里面所有的filter进行过滤,比如刚刚设置的所有请求需要登录,也还有我们需要排除的请求

SecurityAutoConfiguration类就已经大致讲完了,

@EnableGlobalAuthentication

当前注解在@EnableSecurity中会自动加上

@Import(AuthenticationConfiguration.class)

AuthenticationConfiguration上面@Import(ObjectPostProcessorConfiguration.class). 以前使用的ObjectPostProcessor就是在这里注入的,注入AutowireBeanFactoryObjectPostProcessor对象

AuthenticationManagerBuilder

@Bean
	public AuthenticationManagerBuilder authenticationManagerBuilder(ObjectPostProcessor<Object> objectPostProcessor,
			ApplicationContext context) {
		LazyPasswordEncoder defaultPasswordEncoder = new LazyPasswordEncoder(context);
		AuthenticationEventPublisher authenticationEventPublisher = getAuthenticationEventPublisher(context);
		DefaultPasswordEncoderAuthenticationManagerBuilder result = new DefaultPasswordEncoderAuthenticationManagerBuilder(
				objectPostProcessor, defaultPasswordEncoder);
		if (authenticationEventPublisher != null) {
			result.authenticationEventPublisher(authenticationEventPublisher);
		}
		return result;
	}

这里面返回了一个AuthenticationManagerBuilder的bean,也就是上面``HttpSecurityConfiguration#httpSecurity()的时候需要的类,这个类也是一个SecurityBuilder`.

LazyPasswordEncoder defaultPasswordEncoder = new LazyPasswordEncoder(context);

首先创建了一个LazyPasswordEncoder,就是PasswordEncoder,用来管理密码的

AuthenticationEventPublisher authenticationEventPublisher = getAuthenticationEventPublisher(context);

这个就是在SecurityAutoConfiguration中创建的security的发布订阅,用来订阅事件

DefaultPasswordEncoderAuthenticationManagerBuilder result = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, defaultPasswordEncoder);

就是AuthenticationManagerBuilder的真正实现了.接下来回到getAuthenticationManager()方法

public AuthenticationManager getAuthenticationManager() throws Exception {
		if (this.authenticationManagerInitialized) {
			return this.authenticationManager;
		}
		AuthenticationManagerBuilder authBuilder = this.applicationContext.getBean(AuthenticationManagerBuilder.class);
		if (this.buildingAuthenticationManager.getAndSet(true)) {
			return new AuthenticationManagerDelegator(authBuilder);
		}
		for (GlobalAuthenticationConfigurerAdapter config : this.globalAuthConfigurers) {
			authBuilder.apply(config);
		}
		this.authenticationManager = authBuilder.build();
		if (this.authenticationManager == null) {
			this.authenticationManager = getAuthenticationManagerBean();
		}
		this.authenticationManagerInitialized = true;
		return this.authenticationManager;
}

AuthenticationManagerBuilder authBuilder = this.applicationContext.getBean(AuthenticationManagerBuilder.class);

获取到DefaultPasswordEncoderAuthenticationManagerBuilder.

		for (GlobalAuthenticationConfigurerAdapter config : this.globalAuthConfigurers) {
			authBuilder.apply(config);
		}

需要注意的是,this.globalAuthConfigurers就是上面三个类,

    @Bean
	public static GlobalAuthenticationConfigurerAdapter enableGlobalAuthenticationAutowiredConfigurer(
			ApplicationContext context) {
		return new EnableGlobalAuthenticationAutowiredConfigurer(context);
	}

	@Bean
	public static InitializeUserDetailsBeanManagerConfigurer initializeUserDetailsBeanManagerConfigurer(
			ApplicationContext context) {
		return new InitializeUserDetailsBeanManagerConfigurer(context);
	}

	@Bean
	public static InitializeAuthenticationProviderBeanManagerConfigurer initializeAuthenticationProviderBeanManagerConfigurer(
			ApplicationContext context) {
		return new InitializeAuthenticationProviderBeanManagerConfigurer(context);
	}

调用了apply也就是add方法.添加到configurers

然后调用build并返回. 又是到了 doBuild() 这里

beforeInit();

没有

init();

上面三个类的init方法

  1. EnableGlobalAuthenticationAutowiredConfigurer#init
  2. InitializeUserDetailsBeanManagerConfigurer#init 调用了auth.apply(new InitializeUserDetailsManagerConfigurer()); 这个类比上面类名字少了一个bean,并且没有后init方法 只有configure方法. 里面创建的DaoAuthenticationProvider,里面默认有一个passwordEncoder,在无参构造方法里面.而UserDetailsServiceDaoAuthenticationProvider是同一个,也就是在UserDetailsServiceAutoConfiguration#inMemoryUserDetailsManager这里创建的.里面继承了,所以是同一个
  3. InitializeAuthenticationProviderBeanManagerConfigurer#init跟2一样,apply了一个InitializeAuthenticationProviderManagerConfigurer

beforeConfigure();

没有

configure();

调用里面的configure方法

2: InitializeUserDetailsManagerConfigurer#configure方法

		@Override
		public void configure(AuthenticationManagerBuilder auth) throws Exception {
			if (auth.isConfigured()) {
				return;
			}
			UserDetailsService userDetailsService = getBeanOrNull(UserDetailsService.class);
			if (userDetailsService == null) {
				return;
			}
			PasswordEncoder passwordEncoder = getBeanOrNull(PasswordEncoder.class);
			UserDetailsPasswordService passwordManager = getBeanOrNull(UserDetailsPasswordService.class);
			DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
			provider.setUserDetailsService(userDetailsService);
			if (passwordEncoder != null) {
				provider.setPasswordEncoder(passwordEncoder);
			}
			if (passwordManager != null) {
				provider.setUserDetailsPasswordService(passwordManager);
			}
			provider.afterPropertiesSet();
			auth.authenticationProvider(provider);
		}

获取所有UserDetailsServicePasswordEncoderUserDetailsPasswordService,使用DaoAuthenticationProvider进行管理,然后添加到AuthenticationManagerBuilder#authenticationProviders

3: InitializeAuthenticationProviderManagerConfigurer#configure方法,把spring中的所有AuthenticationProvider添加到AuthenticationManagerBuilder#authenticationProviders

然后又到了熟悉的AuthenticationManagerBuilder#performBuild

ProviderManager providerManager = new ProviderManager(this.authenticationProviders,
				this.parentAuthenticationManager);
		if (this.eraseCredentials != null) {
			providerManager.setEraseCredentialsAfterAuthentication(this.eraseCredentials);
		}
		if (this.eventPublisher != null) {
			providerManager.setAuthenticationEventPublisher(this.eventPublisher);
		}
		providerManager = postProcess(providerManager);
		return providerManager;

首先使用ProviderManager管理authenticationProvidersparentAuthenticationManager,这里的eraseCredentialsCredentialsContainer类有关,也就是敏感数据.接着的eventPublisher就是发布订阅了,默认会创建的 然后providerManager = postProcess(providerManager);就是注入spring容器中,接着返回 ,这里返回的其实是ProviderManager对象了,接着就是到了HttpSecurity的创建了,后面HttpSecurity#doBuild()时候再讲HttpSecurity的构建

这里面的LazyPasswordEncoder这个类也很有意思,手动制造一个懒加载类

@EnableGlobalMethodSecurity

这里有个很坑的地方,里面的prePostEnabled,securedEnabled这些属性,不是直接在GlobalMethodSecuritySelector中进行处理的,放在了GlobalMethodSecuritySelector#methodSecurityMetadataSource这个bean里面进行处理,然后开启prePostEnabled之后,就会加载PrePostAnnotationSecurityMetadataSource类. 这个我找了半天,后面无意中才发现

这个注解也添加了@EnableGlobalAuthentication注解

主要是看GlobalMethodSecuritySelector类 里面加载了AutoProxyRegistrar,这个就是springaop的类,创建代理对象的一个类.会创建InfrastructureAdvisorAutoProxyCreator类来创建代理对象.关键是GlobalMethodSecurityConfigurationMethodSecurityMetadataSourceAdvisorRegistrar这俩个类

MethodSecurityMetadataSourceAdvisorRegistrar类里面都用到了GlobalMethodSecurityConfiguration.我就放在一起了

MethodSecurityMetadataSourceAdvisorRegistrar

这个类里面就是往spring中注册了一个MethodSecurityMetadataSourceAdvisor对象

MethodSecurityMetadataSourceAdvisor

这个类就是PointcutAdvisor,使用AbstractAutoProxyCreator创建代理对象的是,会获取Pointcut来判断是否需要代理对象,然后使用Advice来进行其余操作.这是springaop的内容就不过多讲解了

aop首先获取pointcut,进行匹配,当前的为

class MethodSecurityMetadataSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {

		@Override
		public boolean matches(Method m, Class<?> targetClass) {
			MethodSecurityMetadataSource source = MethodSecurityMetadataSourceAdvisor.this.attributeSource;
			return !CollectionUtils.isEmpty(source.getAttributes(m, targetClass));
		}

	}

也就是StaticMethodMatcherPointcut,ClassFilter默认都是true,方法匹配为MethodSecurityMetadataSourceAdvisor#attributeSource进行匹配.而methodSecurityMetadataSource是在GlobalMethodSecurityConfiguration#methodSecurityMetadataSource里面进行创建的.这俩个类后面讲.只要匹配成功就和aop一样流程了

这里的Advice就是MethodSecurityInterceptor类.在GlobalMethodSecurityConfiguration#methodSecurityInterceptor中创建

MethodSecurityInterceptor

isPrePostEnabled

添加PrePostAnnotationSecurityMetadataSource类,主要关注getAttributes方法.后面会讲. 这里面就是我们常用的注解了,然后构建成ConfigAttribute并返回.里面的构建主要用的是PrePostInvocationAttributeFactory的实现,只有一个实现

isSecuredEnabled

这个就是@Secured注解的处理. 逻辑基本和上面一样

最后返回一个DelegatingMethodSecurityMetadataSource对象,就是MethodSecurityInterceptor中用到的对象

匹配成功的aop都会进入MethodSecurityInterceptor#invoke

@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		InterceptorStatusToken token = super.beforeInvocation(mi);
		Object result;
		try {
			result = mi.proceed();
		}
		finally {
			super.finallyInvocation(token);
		}
		return super.afterInvocation(token, result);
	}

这个一看就是标准aop

super.beforeInvocation(mi)

这个里面就有授权了,Authorizationauthentication不一样,一个是认证一个是授权.这个是授权,简单说就是角色

Collection attributes = this.obtainSecurityMetadataSource().getAttributes(object);

接着来到了DelegatingMethodSecurityMetadataSource#getAttributes

        for (MethodSecurityMetadataSource s : this.methodSecurityMetadataSources) {
				attributes = s.getAttributes(method, targetClass);
				if (attributes != null && !attributes.isEmpty()) {
					break;
				}
			}

this.methodSecurityMetadataSources里面的值,就是GlobalMethodSecurityConfiguration#methodSecurityMetadataSource里面的sources. 构建出来返回attributes.

Authentication authenticated = authenticateIfRequired();

这个就是获取当前认证信息

attemptAuthorization(object, attributes, authenticated);

使用accessDecisionManager进行授权.放到MethodInterceptor中进行讲解 里面还有授权失败发布事件publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated, ex));

接着就是授权成功发送事件,接着就是返回一个InterceptorStatusToken对象

result = mi.proceed();

执行业务

super.finallyInvocation(token);

是否刷新InterceptorStatusToken,前面传参是false

return super.afterInvocation(token, result);

后处理器,与前处理器基本一样.剩下的MethodInterceptor中进行讲解

MethodInterceptor

创建出MethodSecurityInterceptor对象给MethodInterceptor用也就是securityMetadataSource属性

this.methodSecurityInterceptor = isAspectJ() ? new AspectJMethodSecurityInterceptor(): new MethodSecurityInterceptor();

一般都是MethodSecurityInterceptor

accessDecisionManager()

这个就是AbstractSecurityInterceptor#attemptAuthorization的授权方法.

	protected AccessDecisionManager accessDecisionManager() {
		List<AccessDecisionVoter<?>> decisionVoters = new ArrayList<>();
		if (prePostEnabled()) {
			ExpressionBasedPreInvocationAdvice expressionAdvice = new ExpressionBasedPreInvocationAdvice();
			expressionAdvice.setExpressionHandler(getExpressionHandler());
			decisionVoters.add(new PreInvocationAuthorizationAdviceVoter(expressionAdvice));
		}
		if (jsr250Enabled()) {
			decisionVoters.add(new Jsr250Voter());
		}
		RoleVoter roleVoter = new RoleVoter();
		GrantedAuthorityDefaults grantedAuthorityDefaults = getSingleBeanOrNull(GrantedAuthorityDefaults.class);
		if (grantedAuthorityDefaults != null) {
			roleVoter.setRolePrefix(grantedAuthorityDefaults.getRolePrefix());
		}
		decisionVoters.add(roleVoter);
		decisionVoters.add(new AuthenticatedVoter());
		return new AffirmativeBased(decisionVoters);
	}

前提条件必须开启prePostEnabled

这里面返回的也是AffirmativeBased.有时候我们自定义也会使用这个.只要有一个AccessDecisionVoter通过就认为是有权限的.这里就不过多讲解了

里面的GrantedAuthorityDefaults对象,也可以是我们自定义的一个前缀.默认前缀为ROLE_

我们一般自定义的,会使用.accessDecisionManager(accessDecisionManager()).在HttpSecurity#doBuild()中进行讲解

afterInvocationManager

与上面前处理一样

methodSecurityMetadataSource

这个就是MethodSecurityMetadataSource对象了

总结一下这里,就是实现springaop的AbstractPointcutAdvisor对象MethodSecurityMetadataSourceAdvisor. 进行aop加载,处理

EnableMethodSecurity

这个注解没有@EnableGlobalMethodSecurity这么强大.代码基本跟@EnableGlobalMethodSecurity一样

HttpSecurity#doBuild()

接下来是最后一块内容了,主要是看里面初始化,构建了哪些类

又是熟悉的AbstractConfiguredSecurityBuilder#doBuild()

首先看看我们一开始创建HttpSecurity的时候添加了哪些类. HttpSecurityConfiguration#httpSecurity()

	@Bean(HTTPSECURITY_BEAN_NAME)
	@Scope("prototype")
	HttpSecurity httpSecurity() throws Exception {
		WebSecurityConfigurerAdapter.LazyPasswordEncoder passwordEncoder = new WebSecurityConfigurerAdapter.LazyPasswordEncoder(
				this.context);
		AuthenticationManagerBuilder authenticationBuilder = new WebSecurityConfigurerAdapter.DefaultPasswordEncoderAuthenticationManagerBuilder(
				this.objectPostProcessor, passwordEncoder);
		authenticationBuilder.parentAuthenticationManager(authenticationManager());
		authenticationBuilder.authenticationEventPublisher(getAuthenticationEventPublisher());
		HttpSecurity http = new HttpSecurity(this.objectPostProcessor, authenticationBuilder, createSharedObjects());
		// @formatter:off
		http
			.csrf(withDefaults())
			.addFilter(new WebAsyncManagerIntegrationFilter())
			.exceptionHandling(withDefaults())
			.headers(withDefaults())
			.sessionManagement(withDefaults())
			.securityContext(withDefaults())
			.requestCache(withDefaults())
			.anonymous(withDefaults())
			.servletApi(withDefaults())
			.apply(new DefaultLoginPageConfigurer<>());
		http.logout(withDefaults());
		// @formatter:on
		applyDefaultConfigurers(http);
		return http;
	}

CsrfConfigurer,ExceptionHandlingConfigurer,HeadersConfigurer,SessionManagementConfigurer,SecurityContextConfigurer,RequestCacheConfigurer,AnonymousConfigurer,ServletApiConfigurer,DefaultLoginPageConfigurer,LogoutConfigurer 还有我们添加到META-INF/spring.factories中的AbstractHttpConfigurer.class

接着回到这里. 我们是自定义了一个SecurityFilterChain.所以在这里面进行构建

首先 http.formLogin();添加了FormLoginConfigurer

http.authorizeRequests() 添加了ExpressionUrlAuthorizationConfigurer.这个只有configure 没有init

http.csrf() 添加了CsrfConfigurer

http.userDetailsService(userDetailsService()) 添加了一个自定义的UserDetailsService

FormLoginConfigurer

也是一个SecurityConfigurer

首先创建对象给属性赋值

authFilter = UsernamePasswordAuthenticationFilter()
loginPage = "/login"
this.authenticationEntryPoint = new LoginUrlAuthenticationEntryPoint(loginPage);

接着来到init

	@Override
	public void init(B http) throws Exception {
		updateAuthenticationDefaults();
		updateAccessDefaults(http);
		registerDefaultAuthenticationEntryPoint(http);
	}

updateAuthenticationDefaults();

	/**
	 * Updates the default values for authentication.
	 * @throws Exception
	 */
	protected final void updateAuthenticationDefaults() {
		if (this.loginProcessingUrl == null) {
			loginProcessingUrl(this.loginPage);
		}
		if (this.failureHandler == null) {
			failureUrl(this.loginPage + "?error");
		}
		LogoutConfigurer<B> logoutConfigurer = getBuilder().getConfigurer(LogoutConfigurer.class);
		if (logoutConfigurer != null && !logoutConfigurer.isCustomLogoutSuccess()) {
			logoutConfigurer.logoutSuccessUrl(this.loginPage + "?logout");
		}
	}

loginProcessingUrl(this.loginPage);

  1. 设置登录页面
  2. this.authFilter也就是UsernamePasswordAuthenticationFilterRequestMatcher设置为new AntPathRequestMatcher(loginProcessingUrl, "POST")

failureUrl(this.loginPage + "?error");

  1. 设置失败页面
  2. this.failureHandler设置为new SimpleUrlAuthenticationFailureHandler(authenticationFailureUrl)里面的authenticationFailureUrl/login + "?error"

getBuilder().getConfigurer(LogoutConfigurer.class); 就是前面加入的那一堆Configurer中的一个.这个默认就是当前设置的值,不用理会

updateAccessDefaults(http);

里面默认为false.

registerDefaultAuthenticationEntryPoint(http);

获取上面Configurer里面的ExceptionHandlingConfigurer.

ExceptionHandlingConfigurer中有俩个属性. defaultEntryPointMappingsdefaultDeniedHandlerMappings. 基本看注释就能知道是做什么的 .这个注释是map的value类上的注释

/**
 * 开始一个身份验证方案。
在调用该方法之前, ExceptionTranslationFilter将使用请求的目标URL填充HttpSession属性abstractathenticationprocessingfilter.SPRING_SECURITY_SAVED_REQUEST _key。
实现应根据需要修改ServletResponse上的标头以开始身份验证过程。
 */
	private LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> defaultEntryPointMappings = new LinkedHashMap<>();

/**
 * 处理拒绝访问失败。
 */
	private LinkedHashMap<RequestMatcher, AccessDeniedHandler> defaultDeniedHandlerMappings = new LinkedHashMap<>();

我们这里的是添加defaultDeniedHandlerMappings. keyRequestMatcher.是否匹配.value是匹配成功就执行 这个类里面也是只有configure(),没有init(). 后面讲解

先说里面的value,就是当前类的this.authenticationEntryPoint.也就是创建类时候的LoginUrlAuthenticationEntryPoint

key就是AndRequestMatcher.但是里面聚合了俩个RequestMatcher. 一个是MediaTypeRequestMatcher,还有一个是NegatedRequestMatcher

其实到了这一步,我们只需要了解其中一个,剩下的都大同小异了.

举例: ExceptionHandlingConfigurer

ExceptionHandlingConfigurer这个类就是刚刚在FormLoginConfigurer中处理的那个.往这个里面添加了defaultEntryPointMappings属性. 然后我们找到ExceptionHandlingConfigurer中的configure(H http)方法, 里面就是创建了一个ExceptionTranslationFilter过滤器,添加到了http中. 代码是这一段

	@Override
	public void configure(H http) {
		AuthenticationEntryPoint entryPoint = getAuthenticationEntryPoint(http);
		ExceptionTranslationFilter exceptionTranslationFilter = new ExceptionTranslationFilter(entryPoint,
				getRequestCache(http));
		AccessDeniedHandler deniedHandler = getAccessDeniedHandler(http);
		exceptionTranslationFilter.setAccessDeniedHandler(deniedHandler);
		exceptionTranslationFilter = postProcess(exceptionTranslationFilter);
		http.addFilter(exceptionTranslationFilter);
	}

接着我们打开ExceptionTranslationFilter,这就是一个Filter,找到doFilter(ServletRequest request, ServletResponse response, FilterChain chain)方法,就是在处理catch (Exception ex) {的时候,做的一些事情.接着继续打开handleSpringSecurityException(HttpServletRequest request, HttpServletResponse response,FilterChain chain, RuntimeException exception)方法,

	private void handleSpringSecurityException(HttpServletRequest request, HttpServletResponse response,
			FilterChain chain, RuntimeException exception) throws IOException, ServletException {
		if (exception instanceof AuthenticationException) {
			handleAuthenticationException(request, response, chain, (AuthenticationException) exception);
		}
		else if (exception instanceof AccessDeniedException) {
			handleAccessDeniedException(request, response, chain, (AccessDeniedException) exception);
		}
	}

发现最后还是到了

	protected void sendStartAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
			AuthenticationException reason) throws ServletException, IOException {
		// SEC-112: Clear the SecurityContextHolder's Authentication, as the
		// existing Authentication is no longer considered valid
		SecurityContext context = SecurityContextHolder.createEmptyContext();
		SecurityContextHolder.setContext(context);
		this.requestCache.saveRequest(request, response);
		this.authenticationEntryPoint.commence(request, response, reason);
	}

里面就有this.authenticationEntryPoint.commence(request, response, reason);这段代码. 创建的是DelegatingAuthenticationEntryPoint.

@Override
	public void commence(HttpServletRequest request, HttpServletResponse response,
			AuthenticationException authException) throws IOException, ServletException {
		for (RequestMatcher requestMatcher : this.entryPoints.keySet()) {
			logger.debug(LogMessage.format("Trying to match using %s", requestMatcher));
			if (requestMatcher.matches(request)) {
				AuthenticationEntryPoint entryPoint = this.entryPoints.get(requestMatcher);
				logger.debug(LogMessage.format("Match found! Executing %s", entryPoint));
				entryPoint.commence(request, response, authException);
				return;
			}
		}
		logger.debug(LogMessage.format("No match found. Using default entry point %s", this.defaultEntryPoint));
		// No EntryPoint matched, use defaultEntryPoint
		this.defaultEntryPoint.commence(request, response, authException);
	}

基本上流程就完成了,请求先走过滤器.然后走不同的filter,报错就到了这一步.进行错误处理.其余都基本一致了.

备注

剩下的一些处理基本和上面这个流程一致,还有几个注解需要注意下

@CsrfToken

@CurrentSecurityContext

@AuthenticationPrincipal

这三个注解是在WebMvcSecurityConfiguration类进行处理的,只要启动了@EnableWebSecurity注解,就会启动

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import({ WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class,
		HttpSecurityConfiguration.class })
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {

	/**
	 * Controls debugging support for Spring Security. Default is false.
	 * @return if true, enables debug support with Spring Security
	 */
	boolean debug() default false;

}

使用方法

	@GetMapping("/get")
//	public Users getUser(String username,@CsrfToken CsrfToken token, @AuthenticationPrincipal Users customUser, @CurrentSecurityContext Authentication authentication) {
	public Users getUser(String username, @AuthenticationPrincipal Users customUser, @CurrentSecurityContext SecurityContext securityContext) {
		return userInfoService.getUsers(username);
	}

里面的SpringWebMvcImportSelector类注入了WebMvcSecurityConfiguration.这就是springmvc中HandlerMethodArgumentResolver的处理,也就是参数的处理,比如我们添加的@PathVariable,@RequestBody等,都是HandlerMethodArgumentResolver的实现类处理的,当然还有HandlerMethodReturnValueHandler.这些就是DispatcherServlet里面的处理了