之所以想写这一系列,是因为之前工作过程中使用Spring Security,但当时基于spring-boot 2.3.x,其默认的Spring Security是5.3.x。之后新项目升级到了spring-boot 3.3.0,结果一看Spring Security也升级为6.3.0,关键是其风格和内部一些关键Filter大改,导致在配置同样功能时,多费了些手脚,因此花费了些时间研究新版本的底层原理,这里将一些学习经验分享给大家。
注意:由于框架不同版本改造会有些使用的不同,因此本次系列中使用基本框架是 spring-boo-3.3.0(默认引入的Spring Security是6.3.0),JDK版本使用的是19,所有代码都在spring-security-study项目上:https://github.com/forever1986/spring-security-study.git
目录
- 1 Spring Security
- 1.1 身份认证
- 1.2 访问控制(授权)
- 1.3 其它框架
- 2 第一个入门示例
- 3 默认配置的原理
- 4 通过yaml配置自定义账号密码
1 Spring Security
什么是Spring Security,以下是官方地址截图说明
这里是翻译工具翻译结果:
Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架。它是保护基于spring的应用程序的事实上的标准。
Spring Security是一个框架,专注于为Java应用程序提供身份验证和授权。与所有Spring项目一样,Spring Security的真正强大之处在于它可以轻松地扩展以满足自定义需求
我们可以从描述中把握几个关键词:身份认证、访问控制(授权)、标准、自定义需求。
说明Spring Security是一个具备身份认证和访问控制的框架,其流程认证和授权流程具备一定行业标准,同时还能通过其扩展点自定义符合自己业务的需求。下面我们就身份认证和授权2个功能以及其它类似Spring Security框架做一下详细说明
1.1 身份认证
我们在做业务系统的时候,对于某些界面、接口都需要做登录认证,其实就是身份认证。只有通过身份认证的请求才能访问程序,不然就会返回http401状态。Spring Security就默认内置身份认证功能,让用户无需太多代码,即可实现一个简单用户密码登录认证。
1.2 访问控制(授权)
访问控制或者授权,其目的是对资源进行细粒度的控制,比如某个页面或者接口,只有具备什么权限的用户才能够访问。我们如果做过权限系统,应该了解ACLs、RBAC、ABAC等权限模式,而Spring Security就内置了一种标准的权限模式,可通过配置或者注解方式实现类似ACLs权限模式。
1.3 其它框架
除了Spring Security之外,是否有类似的其它安全框架
Apache Shrio:Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。相对于Spring Security来说,是一款轻量级的框架
Sa-Token:Sa-Token 是一个轻量级 Java 权限认证框架,主要解决:登录认证、权限认证、Session会话、单点登录、OAuth2.0、微服务网关鉴权 等一系列权限相关问题,并且它是国内的开源项目。github上这么写:当你受够 Shiro、SpringSecurity 等框架的三拜九叩之后,你就会明白,相对于这些传统老牌框架,Sa-Token 的 API 设计是多么的简单、优雅!
CAS (Central Authentication Service):CAS是一个独立的企业级单点登录解决方案,为Web应用程序提供了一个集中的认证机制。它允许用户在多个应用程序中使用相同的凭证进行登录。
2 第一个入门示例
Spring Security其实是一个入门门槛较高的框架,但是自从和spring-boot集成之后,其使用就简便很多。话不多说,我们开始体验一把。
新建一个父模块:spring-security-study,该模块只作为统一pom引入作用,没有其它功能或代码
入门示例代码参考lesson01子模块
1)新建一个父模块spring-security-study,其pom文件引入如下:(这里只是声明版本,有些引入只是为了后面使用提前引入而已)
<?xml version="1.0" encoding="UTF-8"?>
<properties><maven.compiler.source>19</maven.compiler.source><maven.compiler.target>19</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><spring-boot.version>3.3.0</spring-boot.version><commons-pool2.version>2.12.0</commons-pool2.version><jaxb-api.version>2.3.1</jaxb-api.version><jjwt.version>0.9.0</jjwt.version><lombok.version>1.18.36</lombok.version><mybatis-plus.version>3.5.5</mybatis-plus.version><mysql.version>8.0.19</mysql.version><druid.version>1.2.9</druid.version><fastjson.version>1.2.83</fastjson.version></properties><dependencyManagement><dependencies><!-- 引入spring boot依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency><!-- lombok依赖,用于get/set的简便--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>${lombok.version}</version></dependency><!-- mysql依赖,用于连接mysql数据库--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>${mysql.version}</version></dependency><!-- mybatis-plus依赖,用于使用mybatis-plus--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-spring-boot3-starter</artifactId><version>${mybatis-plus.version}</version><type>jar</type></dependency><!-- jjwt依赖,用于使用JWT--><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>${jjwt.version}</version></dependency><!-- jaxb-api依赖,在java17之后,被移除了,但是在做JWT解析时需要使用--><dependency><groupId>javax.xml.bind</groupId><artifactId>jaxb-api</artifactId><version>${jaxb-api.version}</version></dependency><!-- pool2和druid依赖,用于mysql连接池--><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId><version>${commons-pool2.version}</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>${druid.version}</version></dependency><!-- fastjson依赖,用于json转换--><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>${fastjson.version}</version></dependency></dependencies></dependencyManagement><!-- maven仓库 --><repositories><repository><id>central</id><url>https://maven.aliyun.com/repository/central</url><releases><enabled>true</enabled></releases><snapshots><enabled>true</enabled></snapshots></repository></repositories>
2)新建子模块lesson01,其pom文件引入如下:
<dependencies><!--Spring Boot 的 web --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--Spring Boot 提供的 Security 启动器 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency>
</dependencies>
3)在子模块lesson01,新建一个DemoController,里面有一个demo接口,返回简单字符串
@RestController
public class DemoController {@GetMapping("/demo")public String demo() {return"demo";}
}
4)设置启动类SecurityLesson01Application,并启动项目
5)访问:http://127.0.0.1:8080/demo 你会得到以下的界面
是的,你被强制需要登录才能访问demo接口
6)这时候输入用户名:user,密码(在控制台有一串自动生成的密码,如下图)。之后你就能访问demo页面
3 默认配置的原理
我们看到只需要引入一个spring-boot-starter-security的依赖,整个项目就会被Spring Security给管理起来。那么spring-boot-starter-security到底做了什么。
熟悉spring-boot的朋友都知道,约定大于配置。在spring-boot中就是默认对Spring Security进行了配置。
1)我们可以从spring-boot的autoconfigure中找到SecurityAutoConfiguration的自动化配置
2)进入SecurityAutoConfiguration这个配置类,该类没什么内容,但是注解中有2个类SpringBootWebSecurityConfiguration.class和SecurityProperties.class比较重要
3)SpringBootWebSecurityConfiguration类中的defaultSecurityFilterChain就是默认配置。这里简单说明一下每一步骤的作用。我们先知道Spring Security是基于一个Filter过滤器的链路方式进行一系列操作,后续会着重讲解其不同过滤器的作用。
这里说明一下,Spring Security的认证方式有多种,里面包括formLogin方式、HTTP Basic等,甚至可以自定义。如果你不使用默认HTTP认证,而是配置HTTP Basic。那么认证就不是通过一个登录页面,而是会弹出一个HTTP Basic对话框(如下图),有兴趣的朋友可以去了解一下HTTP Basic authentication(就是在Postman工具有一项Authorization配置),这里就不细说。
4)SecurityProperties类就是定义登录的用户名和密码,里面头部的properties注解是读取yaml配置,如果没有yaml配置,则给出一个默认用户user以及随机生成一串密码。这就解释了为什么我们什么都没配置,Spring Security就开始工作,还给我们生成了用户名密码
4 通过yaml配置自定义账号密码
我们可以通过配置application.yml文件,为Spring Security配置默认用户名和密码,这样就不会使用默认user和随机密码
server:port: 8080
spring:application:name: lesson01security:user:name: moopassword: 1234
结语:问题来了,即使通过这种配置自定义账号密码,在业务上面来说,也不会有人将用户名和密码放到配置文件中,一般都会放到数据库中。那么Spring Security也考虑到这种情况,下一章我们来讲解:Spring Security基于数据库的用户认证和认证原理