初识spring security (一),一文弄懂默认配置

一、简单导入依赖

        1、导入pom

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.2</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>springsecurity-simple-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <java.version>8</java.version>
    </properties>


    <dependencies>
        <!-- web 支持 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- SpringSecurity依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
    </dependencies>


    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

2、创建controller测试

@SpringBootApplication
public class SpringSecuritySimpApplication {


    public static void main(String[] args) {
        SpringApplication.run(SpringSecuritySimpApplication.class,args);
    }
}

@RestController
public class HelloController {



    @RequestMapping("hello")
    public String hello() {
        return "hello world";
    }


}

3、简单导入后,接口资源就受到保护了,是什么原理,以下引申出三个问题

1如何在只是导入了一个springsecurity-statar 所有的接口都进行保护了;

2、登录页面的这个html 是从何而来,我们自己项目也没有写html怎么就出来了;

3、登录页的这个user 以及默认的用户名和密码是怎么来的;

二、问题1:只是导入了一个springsecurity-statar 依赖,为何 所有的接口都进行保护了

2.1  DelegatingFilterProxy  

  DelegatingFilterProxy  作用是打通servlet 和spring 通讯的桥梁,介入servilet 容器和spirng 应用;我们在DelegatingFilterProxy  基础上使用spring提供的过滤器都被该过滤器所代理;

2.2 FilterChainProxy 、

FilterChainProxy 为过滤器链,允许委托其他多个过滤器,底层实现,框架做好了,不需要我们处理;

2.3  SecurityFilterChain 

SecurityFilterChain 我们自己配置使用的过滤器链,提供我们自定义配置;该类是我们接触到的,最终也是要配置该过滤器来实现一系列的验证

2.4  SpringBootWebSecurityConfiguration 

SpringBootWebSecurityConfiguration 每一个starter都有一个autoconfig入口配置,该类是入口,从该类开始出发,追求答案

2.4.1  注解 @ConditionalOnDefaultWebSecurity

进入后,导入了一个 DefaultWebSecurityCondition 的类

2.4.1.1 源码

2.4.2 @Conditional(DefaultWebSecurityCondition.class)

   里面有个conditon注解@Conditional(DefaultWebSecurityCondition.class),DefaultWebSecurityCondition进去后看到里面有两个方法;
条件1:@ConditionalOnClass({ SecurityFilterChain.class, HttpSecurity.class }) 判断是否有class文件,SecurityFilterChain 或者HttpSecurity,只要导入了,肯定会有这两个class的文件;即使我们不配置;
条件2:@ConditionalOnMissingBean({ WebSecurityConfigurerAdapter.class, SecurityFilterChain.class })   找不到对应的bean,注意这个是bean,而不是class,因我们创建的时候,没有重写或者继承WebSecurityConfigurerAdapter 所以是找不到的;至此该注解条件生效;

2.4.2.1 源码截图

2.4.3  WebSecurityConfigurerAdapter  

该类作用:后续我们可以继承该类,用于重写里面方法,配置自己的验证规则;

跟随代码  @ConditionalOnMissingBean({ WebSecurityConfigurerAdapter.class) 进到该类后,找到 configure(HttpSecurity http)  方法,是不是一下子豁然开朗了;看到了我们熟悉的配置;

  这个配置是不是很熟悉了: http.authorizeRequests((requests) -> requests.anyRequest().authenticated());

所有的请求都需要认证;至此,导入依赖自动加了认证;spring官方也推荐我们重写该方法配置;

三、问题2:登录页面的这个html 是从何而来

是不是有疑问,这个页面我自己项目也没写,怎么就到这个form表单页面了;

3.1  FormLoginConfigurer 

FormLoginConfigurer中的init() 可以得知,初始化会调用 ,该方法做了什么,是不是初始化了一个DefaultLoginPageGeneratingFilter 的过来器

3.1.1 源码截图

3.2  流程分析

1. 请求/hello 接口,在引入 spring security 之后会先经过一些列过滤器

2. 在请求到达 FilterSecuritylnterceptor时,发现请求并未认证。请求拦截下来,并抛出

AccessDeniedException 异常。

抛出AccessDeniedExceptsn 的异常会被 ExceptionTranslationFilter 捕获,这个Filter 中

会调用 LoginUrlAuthenticationEntryPoint#commence 方法给客户端返回 302,要求客户

端进行重定向到 /login 页面。

4. 客户端发送/login 请求。

5. /login 请求会再次被拦截器中 DefaultLoginPageGeneratingFilter 拦截到,并在拦截器中返回生成登录页面。

3.3  DefaultLoginPageGeneratingFilter

DefaultLoginPageGeneratingFilter 默认的redirect页面,至此自动生成的登录页面是通过浏览器生成一个表单写出的;

3.3.1 源码截图

四、问题3:默认的用户名和密码是怎么来的

是不是疑问为什么控制台能打印出密码呢,怎么实现的;

4.1 http.formLogin()方法

用来初始化表单一些配置,如用户名 密码 等;

4.1.1 源码

4.2 FormLoginConfigurer 新建

4.3 UsernamePasswordAuthenticationFilter 

UsernamePasswordAuthenticationFilter 通过构造方法里面new了一个token过滤器,查看attemptAuthentication 方法 

4.4  断点调试 ProviderManager

通过  Authentication 参数可以知道传过来的是 一个UsernamePasswordAuthenticationToken 对象;

4.5 DaoAuthenticationProvider 最终实现类

4.5.1  InMemoryUserDetailsManager

   跟踪后得知,UserDetailsService的实现类是InMemoryUserDetailsManager,最终从内存中获取到用户信息,然后得到密码;

4.6 InMemoryUserDetailsManager 基于内存的用户会生效

我们也没有配置这个基于内存的用户信息,为什么自动生成用户和密码了

4.6.1  UserDetailsServiceAutoConfiguration

一般默认配置都是有一个starter 启动器

4.6.2  @ConditionalOnClass(AuthenticationManager.class)

由注解得知,@ConditionalOnClass(AuthenticationManager.class)
当类中有AuthenticationManager,导入依赖后,肯定会有;
@ConditionalOnMissingBean(
        value = { AuthenticationManager.class, AuthenticationProvider.class, UserDetailsService.class,
                AuthenticationManagerResolver.class };

当 bean中没有 AuthenticationManager并且UserDetailsService并且AuthenticationProvider 时候,该自动配置InMemoryUserDetailsManager 类就生效,并且注入为bean;

所以说,只要我们重新定义了这三个其中一个设置为bean,那么基于InMemoryUserDetailsManager 这个就不生效了,这就是为什么我们实现了UserDetailsService后,就没有内存认证的这个类了;

4.6.3  SecurityProperties

我们看到 inMemoryUserDetailsManager中传入了一个 SecurityProperties 该类就是一个

@ConfigurationProperties 标记的注解,也就是说 我们在配置文件写上 
 spring.security. username 是不是就可以映射到属性里面了

通过该类  User user = properties.getUser(); 调用了该方法,可以看到 User对象里面这个name就是叫user,密码为随机生成的uuid,这就是为什么默认的登录名为user

4.6.4 自定义登录时候的用户名和密码

当我们在配置文件中自定义了用户名密码后,随机密码和用户名就为自定义的了,至此 user 和密码整个流程就出来了

4.6.5  如果我们文件自定义了,该标志位就是false了,就不会打印控制台的密码了

  • 6
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

星空寻流年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值