初识SpringSecurity

本文章对应视频可在B站查看SpringSecurity6对应视频教程,记得三连哦,这对我很重要呢!
温馨提示:视频与文章相辅相成,结合学习效果更强哦!

系列文章链接

1、初识SpringSecurity,认识主流Java权限框架,SpringSecurity入门使用
2、SpringSecurity集成数据库,完成认证授权操作
3、SpringSecurity实现动态权限,OAuth2.0授权登录等

安全框架是什么

安全框架的本质就是一堆过滤器的组成,目的在于保护系统资源的访问是被允许的,所以在到达资源之前会做一系列的验证工作,这些验证工作通过一系列的过滤器完成。安全框架通常的功能有认证、授权、防止常见的网络攻击,以此为核心拓展其他功能。比如session管理,密码加密,权限管理等功能

Apache Shiro

img

起源

Shiro是 Apache 下的一个 开源安全框架,提供了身份验证、授权、 密码学 和会话管理等 关于安全的核心功能。它的前身是 JSecurity,2004 年,Les Hazlewood 和 Jeremy Haile 创办了 Jsecurity。当时他们找不到适用于应用程序级别的合适 Java 安全框架,同时又对 JAAS 非常失望,于是就搞了Shiro这个框架。

2004 年到 2008 年期间,JSecurity 托管在 SourceForge 上,贡献者包括 Peter Ledbrook、Alan Ditzel 和 Tim Veil。

2008 年,JSecurity 项目贡献给了 Apache 软件基金会( ASF ),并被接纳成为 Apache Incubator 项目,由导师管理,目标是成为一个顶级 Apache 项目。期间,Jsecurity 曾短暂更名为 Ki,随后因商标问题被社区更名为“Shiro”。随后项目持续在 Apache Incubator 中孵化,并增加了贡献者 KalleKorhonen。

2010 年 7 月,Shiro 社区发布了 1.0 版,随后社区创建了其项目管理委员会,并选举 Les Hazlewood 为主席。2010 年 9 月 22 日,Shrio 成为 Apache 软件基金会的顶级项目(TLP)。

功能

Shiro 干净利落地 处理身份认证,授权,会话管理和加密。 Apache Shiro 的首要目标是易于使用和理解。框架应该尽可能掩盖复杂的地方,暴露一个干净而直观的 API,来简化开发人员在应用程序安全上所花费的时间。

Apache Shiro 是一个拥有许多功能的综合性的安全框架,下图表展示了 Shiro 的核心功能:
在这里插入图片描述

Shiro 中有四大核心功能——身份验证,授权,会话管理和加密。

  1. Authentication :简称为“登录”,这是一个证明用户是谁的行为。
  2. Authorization :访问控制的过程,也就是决定“谁”可以去访问“什么”。
  3. Session Management : 管理用户特定的会话,即使在非 Web 或 EJB 应用程序。
  4. Cryptography :通过使用加密算法保持数据安全同时易于使用。

除此之外,Shiro 也提供了额外的功能,来解决在不同环境下所面临的安全问题,尤其是以下这些:

  1. Web Support: web 支持的 API 能够轻松地保护 Web 应用程序。
  2. Caching :缓存是用来确保操作的快速而又高效的。
  3. Concurrency :Apache Shiro 利用它的并发特性来支持多线程应用程序。
  4. Testing :测试功能来帮助编写单元测试和集成测试。
  5. Run As :一个允许用户 以 另一个用户身份(如果允许) 运行的功能,有时候在管理脚本时很有用。
  6. Remember Me :在会话中记住用户的身份,这样用户只需要在强制登录时登录。

特点

Shiro 框架具有 直观、易用 等特性,同时也能提供了 健壮的安全性 ,在常规的企业级应用中,其实也够用了。

SpringSecurity

img

Spring Security是一个 功能强大且高度可定制 的,主要负责为 Java 程序提供声明式的 身份验证和访问控制 的安全框架。其 前身是Acegi Security ,后来被收纳为 Spring 的一个子项目,并更名为了Spring Security。

Spring Security的 底层主要是 基于 Spring AOP 和 Servlet 过滤器 来实现安全控制 ,它提供了全面的安全解决方案,同时授权粒度可以在 Web请求级和方法调用级 来处理身份确认和授权。

SpringSecurity是由Spring提供的一个安全框架,依赖于Spring Freamwork的基础功能,也可以将Bean交由Spring管理,充分利用Spring的IOC和AOP,为系统提供安全服务,如果项目使用Spring为基础使用SpringSecurity整合再合适不过。如果你的项目不是用Spring开发的就不要考虑此技术了

功能

Spring Security 的核心功能主要包括如下几个:

  • 认证: 解决 “你是谁” 的问题–>解决的是系统中是否有这个“用户”(用户/设备/系统)的问题,也就是我们常说的“登录”。
  • 授权: 权限控制/鉴别,解决的是系统中某个用户能够访问哪些资源,即“你能干什么”的问题。Spring Security 支持基于 URL 的请求授权、方法访问授权、对象访问授权。
  • 防护攻击: 防止身份伪造等各种攻击手段。
  • 加密功能: 对密码进行加密、匹配等。
  • 会话功能: 对 Session 进行管理。
  • RememberMe功能: 实现“记住我”功能,并可以实现token令牌持久化。

两者区别

  • SpringSecurity基于Spring开发,与SpringBoot、SpringCloud更容易集成
  • SpringSecurity拥有更多功能,如安全防护,对OAuth授权登录的支持
  • SpringSecurity拥有良好的扩展性,更容易自定义实现一些定制需求
  • SpringSecurity的社区资源比Shiro更丰富
  • Shiro相较于SpringSecurity更轻便,简单,使用流程更清晰,上手容易,反观SpringSecurity属于重量级,学习难度比Shiro高
  • Shiro不依赖于其他框架可独立运行,而SpringSecurity需要依赖于Spring容器运行

Sa-Token

img

是一款国产安全框架,使用简单,轻便。文档清晰详细,内置多种功能,喜欢的同学可以了解一下

SpringSecurity课程使用技术版本

  • SpringBoot:3.1.5
  • SpringSecurity:6.1.5
  • JDK17:长期支持版本
  • MySQL:8.0
  • Mybatis-Plus:3.5.x
  • JWT
  • Redis

Spring Security 6.1新特性

  1. 依赖项和配置的简化:Spring Security 6.1 简化了项目的依赖项和配置,通过使用 Spring Boot 2.3 作为默认的启动器,简化了自动配置和依赖项的处理
  2. 改进的身份验证和授权:Spring Security 6.1 对身份验证和授权进行了改进,增加了新的策略如默认的UserDetailsService策略,更简单的角色和权限管理,以及更灵活的异常处理
  3. 密码存储和加密的增强:Spring Security 6.1 提供了更强大的密码存储和加密功能,支持新的密码加密策略,用户可以自定义密码加密方式
  4. 支持 JDK 16:Spring Security 6.1 支持 JDK 16,可以更好地利用 Java 的新特性和 API 进行开发
  5. 提高了默认配置的易用性:Spring Security 6.1 增强了默认配置的易用性,例如默认的认证管理器,默认的安全过滤器链等,使得开发者可以更快速地启动项目
  6. 改进的测试支持:Spring Security 6.1 对测试提供了更好的支持,包括对测试的简化和性能优化
  7. 对 WebFlux 的支持:Spring Security 6.1 支持 Spring WebFlux,可以更好地适用于非阻塞式的 Web 应用
  8. 对 Spring Cloud 的集成:Spring Security 6.1 与 Spring Cloud 集成更好,可以更好地支持微服务架构的应用
  9. 安全性增强:Spring Security 6.1 对安全性进行了增强,包括对 CSRF 和 CSP 等攻击的防御,以及新的安全特性如安全HEADER等的支持

SpringSecurity6.1废弃了很多老的接口,已经不再是过时警告而是直接剔除。在配置上写法也发生变化,后边细说。

创建SpringSecurity项目

SpringSecurity焕发第二春主要是SpringBoot的兴起,让SpringSecurity配置更方便,使用更简单,在使用SpringSecurity时最好与SpringBoot搭配使用,此处以SpringSecurity6.1.5版本为例,首先创建一个基本的Maven工程,此处使用JDK17+SpringBoot3.1.5版本

直接创建SpringBoot项目

在工程中引入springboot和springsecurity依赖

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>3.1.5</version>
  <relativePath/>
</parent>

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

引入SpringSecurity之后不同

控制台输出密码,并提示加载了部分过滤器

在这里插入图片描述

访问接口首先进入到登录页面,用户名默认是user,密码是控制台生成的

在这里插入图片描述

修改默认用户和密码

可以在项目的application.yml文件中配置自定义用户名和密码

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

配置之后重启项目,控制台就不会输出密码了

默认用户名和密码从哪来

在SpringSecurity源码中的SecurityProperties类中做了关于用户、密码,角色的配置,该类中有一个User内部类。定义了默认用户名密码以及权限
在这里插入图片描述

所以当我们配置name和password时就会使用自定的名字和密码了,当然也可以配置角色roles,当然他是一个集合

SpringSecurity配置类

自定义用户名和密码

通过配置类的方式实现基于内存的用户名和密码,角色的定义,后边切换成数据库,目的在于搞明白SpringSecurity认证流程

package com.stt.springsecuritydemo2.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.authentication.PasswordEncoderParser;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

/**
 * 使用的是 SpringSecurity6.1.5,配置类有一些变化
 * 1、该类不再需要继承其他的Security定义的类
 * 2、需要使用 @Configuration 才会被Spring容器加载
 * 3、废弃了很多方法,比如and()方法,建议使用Lambda表示实现
 */
@Configuration
// 标记为一个Security类,启用SpringSecurity的自定义配置
@EnableWebSecurity
public class SecurityConfig {

    // 自定义用户名和密码
    // UserDetailsService:根据用户名加载用户,找到的话返回用户信息【UserDetails类型】
    // UserDetails:存储了用户的信息
    @Bean
    public UserDetailsService userDetailsService() {
        // 定义用户信息
        // 构建管理员
        UserDetails adminUser = User.withUsername("xiaozhang")
                .password("$2a$10$csvUZnj/VG6wBkooT/mewO.WbJesVCiHqEoWTyQrOYTKJvk3xpQb6")
                .roles("admin", "user")
                .build();
        // 构建普通用户
        UserDetails vipUser = User.withUsername("user")
                .password("$2a$10$csvUZnj/VG6wBkooT/mewO.WbJesVCiHqEoWTyQrOYTKJvk3xpQb6")
                .roles("user")
                .build();
        // 将用户存储到SpringSecurity中
        InMemoryUserDetailsManager userDetailsManager = new InMemoryUserDetailsManager();
        // 创建两个用户,SpringSecurity在运行时就会知道有两个用户
        userDetailsManager.createUser(adminUser);
        userDetailsManager.createUser(vipUser);
        return userDetailsManager;
    }
}

UserDetailsService:提供查询用户功能,如根据用户名查询用户,并返回UserDetails

UserDetails:记录用户信息,如用户名,密码,权限等

密码问题

SpringSecurity提供密码加密工具:PasswordEncoder,具体实现又很多,此处使用BCryptPasswordEncoder

@Bean
public PasswordEncoder passwordEncoder() {
    // 构建密码编码器
    return new BCryptPasswordEncoder();
}

密码加密:

String pass = "123456";
String result = passwordEncoder.encode(pass);

密码匹配:

// 匹配密码
boolean isTrue = passwordEncoder.matches("111111",result);
System.out.println("isTrue===>" + isTrue);

自定义登录页面

不使用前后端分离,涉及跨域问题,这里将会使用Thymeleaf实现。学习更多SpringSecurity配置

定义登录页面

引入Thymeleaf的依赖

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

定义登陆页面

SpringBoot要求登录页面写在resources/templates目录下
在这里插入图片描述

配置

配置跳转登陆页面的请求不需要拦截

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    // 配置关闭csrf机制,
    http.csrf(csrf -> csrf.disable());
    // 配置请求拦截方式
    // requestMatchers() : 匹配资源路径
    // permitAll() :随意访问
    // anyRequest():其他任意请求
    // authenticated() : 需要认证之后
    http.authorizeHttpRequests(auth -> auth.requestMatchers("/to_login").permitAll().anyRequest().authenticated());
    return http.build();
}

登陆实现

需求1:系统中有资源,没有登陆时,访问自动跳转到登录页面,登陆成功则可以正常访问

需求2:登陆成功之后自动跳转到首页

package com.stt.springsecuritydemo3.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    // 配置权限相关的配置
    // 安全框架本质都是一堆的过滤器,称之为过滤器链,每一个过滤器功能都不同
    // to_login不要拦截
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        // 配置关闭csrf机制,
        http.csrf(csrf -> csrf.disable());
        // 配置请求拦截方式
        // requestMatchers() : 匹配资源路径
        // permitAll() :随意访问
        // anyRequest():其他任意请求
        // authenticated() : 需要认证之后
        http.authorizeHttpRequests(auth -> auth.requestMatchers("/to_login").permitAll().anyRequest().authenticated());
        // 表单、Basic等等
        http.formLogin(form -> form.loginPage("/to_login") // 跳转到自定的登录页面
                .loginProcessingUrl("/doLogin") // 处理前端的请求,与form表单的action一致
                .usernameParameter("username") // 用户名
                .passwordParameter("password") // 密码
                .defaultSuccessUrl("/index") // 请求到index接口上
        );
        return http.build();
    }
}

授权

用户认证之后,会去存储用户对应的权限,并且给资源设置对应的权限,SpringSecurity支持两种粒度的权限

  • 基于请求的:在配置文件中配置路径,可以使用**的通配符
  • 基于方法的:在方法上使用注解实现
  • 动态权限:用户权限被修改之后,不需要用户退出,会自动刷新,也不需要修改代码

角色配置

@Bean
public UserDetailsService userDetailsService() {
    // 定义用户信息
    // 构建管理员
    UserDetails adminUser = User.withUsername("xiaozhang")
        .password("$2a$10$csvUZnj/VG6wBkooT/mewO.WbJesVCiHqEoWTyQrOYTKJvk3xpQb6")
        .roles("admin") // 角色
        .authorities("test1:show") // 权限
        .build();
    // 构建普通用户
    UserDetails vipUser = User.withUsername("user")
        .password("$2a$10$csvUZnj/VG6wBkooT/mewO.WbJesVCiHqEoWTyQrOYTKJvk3xpQb6")
        .roles("user")
        .authorities("user:show","goods:show")
        .build();
    // 将用户存储到SpringSecurity中
    InMemoryUserDetailsManager userDetailsManager = new InMemoryUserDetailsManager();
    // 创建两个用户,SpringSecurity在运行时就会知道有两个用户
    userDetailsManager.createUser(adminUser);
    userDetailsManager.createUser(vipUser);
    return userDetailsManager;
}

基于请求鉴权

@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());
    // 表单、Basic等等
    http.formLogin(form -> form.loginPage("/to_login") // 跳转到自定的登录页面
                   .loginProcessingUrl("/doLogin") // 处理前端的请求,与form表单的action一致
                   .usernameParameter("username") // 用户名
                   .passwordParameter("password") // 密码
                   .defaultSuccessUrl("/index") // 请求到index接口上
                  );
    return http.build();
}

在这里插入图片描述

如果没有权限则返回403

基于方法鉴权

在SpringSecurity6版本中@EnableGlobalMethodSecurity被弃用,取而代之的是@EnableMethodSecurity。默认情况下,会激活pre-post注解,并在内部使用AuthorizationManager。

新老API区别

此@EnableMethodSecurity替代了@EnableGlobalMethodSecurity。提供了以下改进:

  1. 使用简化的AuthorizationManager。
  2. 支持直接基于bean的配置,而不需要扩展GlobalMethodSecurityConfiguration
  3. 使用Spring AOP构建,删除抽象并允许您使用Spring AOP构建块进行自定义
  4. 检查是否存在冲突的注释,以确保明确的安全配置
  5. 符合JSR-250
  6. 默认情况下启用@PreAuthorize、@PostAuthorize、@PreFilter和@PostFilter
    在这里插入图片描述

请求级别和方法级别对比

请求级别方法级别
授权类型粗粒度细粒度
配置位置在配置类中配置在方法上配置
配置样式DSL注解
授权定义编程式SpEL表达式

主要的权衡似乎是您希望您的授权规则位于何处。重要的是要记住,当您使用基于注释的方法安全性时,未注释的方法是不安全的。为了防止这种情况,请在HttpSecurity实例中声明一个兜底授权规则。

如果方法上也定义了权限,则会覆盖类上的权限

注意:使用注解的方式实现,如果接口的权限发生变化,需要修改代码了。后期会学习动态权限,无需修改代码就可以实现接口权限的修改

  • 10
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

石添的编程哲学

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

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

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

打赏作者

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

抵扣说明:

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

余额充值