• SpringSecurity6从入门到上天系列第六篇:解决这个问题为什么在引入SpringSecurity之后所有的请求都需要先做登录认证才可以进行访问呢


    文章目录

    问题引入

    1:问题阐述

    2:问题分析

    一:从SpringBoot的自动装配

    1:@SpringBootApplication介绍

    2:自动装配的核心方法

    3:核心方法的调用路径

    4:SpringSecurity核心配置

    5:SpringBoot...Configuration详解

    6:总结一下


    大神链接:作者有幸结识技术大神孙哥为好友,获益匪浅。现在把孙哥视频分享给大家。

    孙哥链接:孙哥个人主页
    作者简介:一个颜值99分,只比孙哥差一点的程序员
    本专栏简介:话不多说,让我们一起干翻SpringSecurity6

    本文章简介:话不多说,让我们讲清楚SpringSecurity6中为什么在引入SpringSecurity之后所有的请求都需要先做登录认证才可以进行访问呢

    问题引入

    1:问题阐述

            为什么在引入SpringSecurity之后所有的请求都需要先做登录认证才可以进行访问呢?

    2:问题分析

            分析清楚这个问题之前,我们先从自动装配开始研究。

    一:从SpringBoot的自动装配

    1:@SpringBootApplication介绍

            这个注解的作用就是标志这个类是SpringBootApplication的启动类。

    1. @SpringBootApplication
    2. public class BigtreeApplication {
    3. public static void main(String[] args) {
    4. SpringApplication.run(BigtreeApplication.class, args);
    5. }
    6. }
    1. @Target({ElementType.TYPE})
    2. @Retention(RetentionPolicy.RUNTIME)
    3. @Documented
    4. @Inherited
    5. @SpringBootConfiguration
    6. @EnableAutoConfiguration
    7. @ComponentScan(
    8. excludeFilters = {@Filter(
    9. type = FilterType.CUSTOM,
    10. classes = {TypeExcludeFilter.class}
    11. ), @Filter(
    12. type = FilterType.CUSTOM,
    13. classes = {AutoConfigurationExcludeFilter.class}
    14. )}
    15. )
    16. public @interface SpringBootApplication {
    17. @AliasFor(annotation = EnableAutoConfiguration.class)
    18. Class[] exclude() default {};
    19. @AliasFor(annotation = EnableAutoConfiguration.class)
    20. String[] excludeName() default {};
    21. @AliasFor(annotation = ComponentScan.class,attribute = "basePackages")
    22. String[] scanBasePackages() default {};
    23. @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
    24. Class[] scanBasePackageClasses() default {};
    25. @AliasFor(annotation = ComponentScan.class,attribute = "nameGenerator")
    26. Classextends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
    27. @AliasFor( annotation = Configuration.class)
    28. boolean proxyBeanMethods() default true;
    29. }

           

            这个注解是一个复合注解@SpringBootConfiguration这个注解的作用带表了这当前这个类是一个SpringBoot配置类,自动交给SpringIOC容器进行管理。

            第二个注解是ComponentScan这个注解的作用是定义Spring的扫描路径的。通畅对应我们自己定义的组件例如:Controller,Service,Dao这些组件。

            第三个注解是:@EnableAutoConfiguration这个注解是SpringBoot自动装配的关键注解,这个注解包含两个核心注解

            第一个注解是:@Import({AutoConfigurationImportSelector.class})这个注解的作用就是在导入当前类的同时顺便导入AutoConfigurationImportSelector这个类也加载进来。

    2:自动装配的核心方法

    1. org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getCandidateConfigurations
    2. protected List getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    3. List configurations = ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader())
    4. .getCandidates();
    5. Assert.notEmpty(configurations,
    6. "No auto configuration classes found in "
    7. + "META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you "
    8. + "are using a custom packaging, make sure that file is correct.");
    9. return configurations;
    10. }
    1. org.springframework.boot.context.annotation.ImportCandidates
    2. public static ImportCandidates load(Class annotation, ClassLoader classLoader) {
    3. Assert.notNull(annotation, "'annotation' must not be null");
    4. ClassLoader classLoaderToUse = decideClassloader(classLoader);
    5. String location = String.format(LOCATION, annotation.getName());
    6. Enumeration urls = findUrlsInClasspath(classLoaderToUse, location);
    7. List importCandidates = new ArrayList<>();
    8. while (urls.hasMoreElements()) {
    9. URL url = urls.nextElement();
    10. importCandidates.addAll(readCandidateConfigurations(url));
    11. }
    12. return new ImportCandidates(importCandidates);
    13. }

            此方法执行完毕的返回值:

            这个配置在:spring-boot-autoconfigure-3.0.12.jar包下!\META-INF\spring\包下的

            org.springframework.boot.autoconfigure.AutoConfiguration.imports文件中,这里边就是Spring中的各种需要自动装配的组件。其中就有很多的配置组件。

    3:核心方法的调用路径

    1. "main@1" prio=5 tid=0x1 nid=NA runnable
    2. java.lang.Thread.State: RUNNABLE
    3. at org.springframework.boot.context.annotation.ImportCandidates.load(ImportCandidates.java:90)
    4. at org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.getCandidateConfigurations(AutoConfigurationImportSelector.java:180)
    5. at org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.getAutoConfigurationEntry(AutoConfigurationImportSelector.java:126)
    6. at org.springframework.boot.autoconfigure.AutoConfigurationImportSelector$AutoConfigurationGroup.process(AutoConfigurationImportSelector.java:430)
    7. at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorGrouping.getImports(ConfigurationClassParser.java:796)
    8. at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorGroupingHandler.processGroupImports(ConfigurationClassParser.java:726)
    9. at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorHandler.process(ConfigurationClassParser.java:697)
    10. at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:182)
    11. at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:415)
    12. at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:287)
    13. at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:344)
    14. at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:115)
    15. at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:779)
    16. at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:597)
    17. - locked <0x12a7> (a java.lang.Object)
    18. at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)
    19. at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:733)
    20. at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:435)
    21. at org.springframework.boot.SpringApplication.run(SpringApplication.java:311)
    22. at org.springframework.boot.SpringApplication.run(SpringApplication.java:1301)
    23. at org.springframework.boot.SpringApplication.run(SpringApplication.java:1290)
    24. at com.dashu.AlibabaApplication.main(AlibabaApplication.java:10)

            这个调用路径是怎么获得的?方法很简单,只需要在这个核心方法的中间打上一个断点。

            然后我们启动main方法,等线程执行过这个方法的断点。然后我们在idea上玩一个骚操作就可以了。

            然后,我们在debug区域,找到最上层一个方法,然后我们右键Exports Thread即可。 

    4:SpringSecurity核心配置

    org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
    1. @AutoConfiguration
    2. @ConditionalOnClass(DefaultAuthenticationEventPublisher.class)
    3. @EnableConfigurationProperties(SecurityProperties.class)
    4. @Import({ SpringBootWebSecurityConfiguration.class, SecurityDataConfiguration.class })
    5. public class SecurityAutoConfiguration {
    6. @Bean
    7. @ConditionalOnMissingBean(AuthenticationEventPublisher.class)
    8. public DefaultAuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher publisher) {
    9. return new DefaultAuthenticationEventPublisher(publisher);
    10. }
    11. }

            然后,就会加载这两个组件:SpringBootWebSecurityConfiguration.class, SecurityDataConfiguration.class 尤其是第一个。

    5:SpringBoot...Configuration详解

            这里边只有一个@Bean注解,最终会创建一个对象:SecurityFilterChain,为什么最终引入了SpringSecurity依赖之后就会所有的请求都会被拦截答案就在这个方法里边。

    1. @Bean
    2. @Order(SecurityProperties.BASIC_AUTH_ORDER)
    3. SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
    4. //任意Http请求都会被认证拦截
    5. http.authorizeHttpRequests().anyRequest().authenticated();
    6. //认证的时候支持form表单认证
    7. http.formLogin();
    8. //http的basic认证
    9. http.httpBasic();
    10. return http.build();
    11. }
    1. @Configuration(proxyBeanMethods = false)
    2. @ConditionalOnWebApplication(type = Type.SERVLET)
    3. class SpringBootWebSecurityConfiguration {
    4. @Configuration(proxyBeanMethods = false)
    5. @ConditionalOnDefaultWebSecurity
    6. static class SecurityFilterChainConfiguration {
    7. @Bean
    8. @Order(SecurityProperties.BASIC_AUTH_ORDER)
    9. SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
    10. http.authorizeHttpRequests().anyRequest().authenticated();
    11. http.formLogin();
    12. http.httpBasic();
    13. return http.build();
    14. }
    15. }
    16. @Configuration(proxyBeanMethods = false)
    17. @ConditionalOnMissingBean(name = BeanIds.SPRING_SECURITY_FILTER_CHAIN)
    18. @ConditionalOnClass(EnableWebSecurity.class)
    19. @EnableWebSecurity
    20. static class WebSecurityEnablerConfiguration {
    21. }
    22. }

    6:总结一下

            基于SpringBoot的自动装配,由于SpringSecurity的装配配置在SpringBoot配置环境中,所以它默认会被加载,加载完毕之后defaultSecurityFilterChain被调用,SecurityFilterChain对象被创创建。所有的方法都会被被鉴权。

            具体的方法调用路径或者叫配置路径是这样的:首先是三个核心的注解:

    @SpringBootApplication-> @EnableAutoConfiguration>@Import(AutoConfigurationImportSelector)

           这样的代码就会基于下面这个调用路径:

    1. "main@1" prio=5 tid=0x1 nid=NA runnable
    2. java.lang.Thread.State: RUNNABLE
    3. at org.springframework.boot.context.annotation.ImportCandidates.load(ImportCandidates.java:90)
    4. at org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.getCandidateConfigurations(AutoConfigurationImportSelector.java:180)
    5. at org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.getAutoConfigurationEntry(AutoConfigurationImportSelector.java:126)
    6. at org.springframework.boot.autoconfigure.AutoConfigurationImportSelector$AutoConfigurationGroup.process(AutoConfigurationImportSelector.java:430)
    7. at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorGrouping.getImports(ConfigurationClassParser.java:796)
    8. at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorGroupingHandler.processGroupImports(ConfigurationClassParser.java:726)
    9. at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorHandler.process(ConfigurationClassParser.java:697)
    10. at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:182)
    11. at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:415)
    12. at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:287)
    13. at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:344)
    14. at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:115)
    15. at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:779)
    16. at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:597)
    17. - locked <0x12a7> (a java.lang.Object)
    18. at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)
    19. at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:733)
    20. at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:435)
    21. at org.springframework.boot.SpringApplication.run(SpringApplication.java:311)
    22. at org.springframework.boot.SpringApplication.run(SpringApplication.java:1301)
    23. at org.springframework.boot.SpringApplication.run(SpringApplication.java:1290)
    24. at com.dashu.AlibabaApplication.main(AlibabaApplication.java:10)

            调用到这个方法里边:AutoConfigurationImportSelector#getCandidateConfigurations最后查到核心组件的配置文件。这样加载到SpringSecurity的核心文件。最终调用到上边的方法,导致所有的方法都得进行登录认证。

    二:默认认证方式的条件限制

    1:@ConditionalOnDefaultWebSecurity

            此注解显示了要想使用下面SpringSecurity的默认认证方式是有条件的。

            也就是说,并不是所有的Http请求都必须走默认认证。进入这个注解:

    1. @Target({ ElementType.TYPE, ElementType.METHOD })
    2. @Retention(RetentionPolicy.RUNTIME)
    3. @Documented
    4. @Conditional(DefaultWebSecurityCondition.class)
    5. public @interface ConditionalOnDefaultWebSecurity {
    6. }

            这个注解上边还有一个注解:@Conditional(DefaultWebSecurityCondition.class)

    1. class DefaultWebSecurityCondition extends AllNestedConditions {
    2. DefaultWebSecurityCondition() {
    3. super(ConfigurationPhase.REGISTER_BEAN);
    4. }
    5. @ConditionalOnClass({ SecurityFilterChain.class, HttpSecurity.class })
    6. static class Classes {
    7. }
    8. @ConditionalOnMissingBean({ SecurityFilterChain.class })
    9. static class Beans {
    10. }
    11. }

            在这个类当中我们定义了两种鉴权规则。第一种是基于Class,他是基于类作用,也就是说当前在classpath下如果有上述两个class的话,就可以走默认的认证方式。在SpringSecurity中肯定是有的,这也就是在引入SpringSecurity依赖之后就会走默认的配置。

            第二种是基于丢失Bean的情况,如果丢失了,那么我们可以走默认的认证规则了。也就是说,如果没有了SecurityFilterChain这个对象的话,那么就不在使用默认的认证规则了。

  • 相关阅读:
    8.字符串转换整数(atoi)
    [iOS开发]frame和bounds
    数据库实践 Hw07
    spring boot利用redis作为缓存
    linux系统优化措施汇总-持续更新
    如何做校园圈子小程序,需要哪些功能?APP小程序H5公众号功能齐全,PHP书写,uniAPP。源码交付,支持二开!
    E : DS堆栈--逆序输出(STL栈使用)
    map中插入数据
    为了给你省1千8,给你推荐这款免费的DTS工具
    【无标题】ssm
  • 原文地址:https://blog.csdn.net/Facial_Mask/article/details/134429503