学习介绍

网上关于SpringSecurity的教程大部分都停留在6以前的版本 但是,SpringSecurity6.x版本后的内容进行大量的整改,网上的教程已经不能够满足 最新的版本使用。这里我查看了很多教程 发现一个宝藏课程,并且博主也出了一个关于SpringSecurity的权限认证的项目,前后端都有。且免费

哔站最好的SpringSecurity6讲解课程 以及三更草堂的SpringSecurity讲解 只不过版本不同。按需观看

认证流程

登录流程

我们来回想一下开始学习开发的时候如何做到一个登录校验的过程,这边以图解形式展示。

SpringSecurity6 学习_SpringSecurity


那么,在一些权限管理系统中 使用这种认证操作就会比较难以实现功能,

比如说我此时会员用户和普通用户 需要有不同的功能权限,依靠一个拦截器的情况下很难说实现。

此时就需要使用到springSecurity进行校验,SpringSecurity其中是一个过滤器链 对角色和权限认证提供了很好的对应接口。

SpringSecurity认证流程

这个图片是 哔站三更草堂 的授课资料中的,如果想要学习SpringSecurity5版本的可以去看这个内容,这张图现在是看不明白的,先记一下下面的基础概念。这部分内容分别是什么。

SpringSecurity6 学习_SpringSecurity_02


概念速查:

Authentication接口: 它的实现类,表示当前访问系统的用户,封装了用户相关信息。

AuthenticationManager接口:定义了认证Authentication的方法

UserDetailsService接口:加载用户特定数据的核心接口。里面定义了一个根据用户名查询用户信息的

方法。

UserDetails接口:提供核心用户信息。通过UserDetailsService根据用户名获取处理的用户信息要封装

成UserDetails对象返回。然后将这些信息封装到Authentication对象中

引入SpringSecurity依赖

SpringSecurity也是Spring框架中的一种,所以他仍然需要依靠于Spring框架才能发挥作用
SpringSecurity maven坐标

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-security</artifactId>
</dependency>

此时我们使用浏览器访问一些自定义接口的时候会发现,多了一个校验内容,会跳转到一个登录界面,此时SpringSecurity就创建成功了,账号admin 密码会在控制台输出,现在我们可以进行下一步配置。

SpringSecurity6 学习_SpringSecurity_03

yml文件修改默认用户和密码

在Application.yml文件中初始化Security的账户和密码,配置重启项目 控制台就不会输出这个密码了

spring:
	security:
		user:
		name: admin
		password: 123456
		roles: ['admin','user']

其中这个配置文件生效的流程 主要是在SecurityProperties类中做了初始化的内容。

SpringSecurity配置类

这个配置类跟6之前版本的差别还是挺大的,6版本的配置类变成了依靠注解的形式进行定义。想要更改默认配置就自己重新注入对应的配置内容。这边我主要书写一下一部分配置的内容 标以注释的形式

@Configuration
// 这个注解的作用是标记一个Security类 启用SpringSecurity的自定义配置
@EnableWebSecurity
// 启用方法注解认证
@EnableMethodSecurity
public class SecurityConfig {
    /*
     * 基本配置讲解
     * */
// 自定义用户名和密码 初始
// 一般替换成 自定义userDetailsService
    @Bean
    public UserDetailsService userDetailsService() {
        // 密 码 : 1234
        UserDetails build = User.withUsername("zhangsan")
                .password("$2a$10$L5xB2VxwKM2kQL4SMTBdyej8e4VpfeMP3XF1660weV0n.WGRbsXdC")
//                .password("{noop}1234")
//                基础无加密的密码需要再前面跟上{noop}这个内容
//                角色的创建
                .roles("admin").build();

        UserDetails build1 = User.withUsername("user")
                .password("$2a$10$L5xB2VxwKM2kQL4SMTBdyej8e4VpfeMP3XF1660weV0n.WGRbsXdC")
                .roles("user").build();
                //这个是基于内容的储存校验
        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
        inMemoryUserDetailsManager.createUser(build);
        inMemoryUserDetailsManager.createUser(build1);
        return inMemoryUserDetailsManager;
    }
}
过滤器链

这个位置的书写 都写成lambda表达式了 和之前的不一致

@Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        // 关闭csrf机制
        http.csrf(csrf->csrf.disable());
        // 在user下面的所有路径下 都不进行验证 其他的页面进行权限认证
        http.authorizeHttpRequests(auth->auth.requestMatchers("/user/**")
                .permitAll().anyRequest().authenticated());
        return http.build();
    }
密码处理

一般以BCryptPasswordEncoder工具进行处理密码的内容,但是需要注意此时的时候你的初始密码必须是BCryptPasswordEncoder加密的内容

@Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
授权

用户认证之后,会去存储用户对应的权限,并且给资源设置对应的权限,SpringSecurity支持两种粒度
的权限
基于请求的:在配置文件中配置路径,可以使用**的通配符
基于方法的:在方法上使用注解实现
动态权限:用户权限被修改之后,不需要用户退出,会自动刷新,也不需要修改代码

UserDetails build = User.withUsername("zhangsan")
	.password("$2a$10$L5xB2VxwKM2kQL4SMTBdyej8e4VpfeMP3XF1660weV0n.WGRbsXdC")
//	.password("{noop}1234")
//	基础无加密的密码需要再前面跟上{noop}这个内容
//	角色的创建
	.roles("admin")
	.authorities("test1:show")
	.build();

进行授权authorities后需要加入A:B的这种形式

对请求中进行鉴权过滤
@Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
//      配置关闭csrf机制,
        http.csrf(csrf -> csrf.disable());
//      配置请求拦截方式
//      requestMatchers() : 匹配资源路径
//      permitAll() :随意访问
//      anyRequest():其他任意请求
//      authenticated() : 需要认证之后
        http.authorizeHttpRequests(auth ->
//      auth.requestMatchers("/test1").hasRole("admin")
//      auth.requestMatchers("/test1").hasAnyRole("admin", "user")
//      auth.requestMatchers("/test1").hasAuthority("test1:show")
//		这个位置进行的路径鉴权 - 那一部分路径使用权限认证 那一部分不使用
                auth.requestMatchers("/test1").hasAnyAuthority("test1:show", "user:show")
                        .requestMatchers("/to_login").permitAll()
                        .anyRequest().authenticated());
        );
        return http.build();
    }