# SpringSecurity从入门到精通
**Repository Path**: multiline/spring-security
## Basic Information
- **Project Name**: SpringSecurity从入门到精通
- **Description**: SpringSecurity基础学习
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 38
- **Created**: 2024-06-14
- **Last Updated**: 2024-06-14
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# SpringSecurity从入门到精通
## 1.简介
[Spring Security 中文文档 ](https://springdoc.cn/spring-security/)
Spring Security是一个Java框架,用于保护应用程序的安全性。它提供了一套全面的安全解决方案,包括身份验证、授权、防止攻击等功能。Spring Security基于过滤器链的概念,可以轻松地集成到任何基于Spring的应用程序中。它支持多种身份验证选项和授权策略,开发人员可以根据需要选择适合的方式。此外,Spring Security还提供了一些附加功能,如集成第三方身份验证提供商和单点登录,以及会话管理和密码编码等。总之,Spring Security是一个强大且易于使用的框架,可以帮助开发人员提高应用程序的安全性和可靠性。
一般Web应用的需要进行**认证**和**授权**。
**认证:验证当前访问系统的是不是本系统的用户,并且要确认具体是哪个用户**
**授权:经过认证后判断当前用户是否有权限进行某个操作**
而认证和授权也是SpringSecurity作为安全框架的核心功能
## 2.快速入门
你可以通过添加 `spring-boot-starter-security` 来为你的Spring Boot项目添加Spring Security。
1. **添加依赖**
```xml
org.springframework.boot
spring-boot-starter-security
org.springframework.boot
spring-boot-starter-web
```
2. **启动类**
创建SpringBoot项目时自动创建好了 此处我做了更改只是为了后期Debug
```java
@SpringBootApplication
public class SanGengSecurityApplication {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(SanGengSecurityApplication.class, args);
System.out.println("args = " + args);
}
}
```
3. **创建Controller**
```java
@RestController
@RequestMapping("/hello")
public class HelloController {
@GetMapping
public String hello(){
return "hello";
}
}
```
4. **启动工程**
你可以在 `localhost:8080/` 访问该应用程序,这将使浏览器重定向到默认的登录页面。你可以提供 `user` 的默认用户名和随机生成的密码,这些密码会被记录到控制台中。然后浏览器会被带到最初请求的页面。
要注销,你可以访问 `localhost:8080/logout`,然后确认你想注销。
> 输入默认账号: user 密码在控制台打印了
## 3.认证
### 3.1登录校验流程
### 3.2原理初探
`SpringSecurity`的原理其实就是一个过滤器链,内部包含了提供各种功能的过滤器。这里我们可以看看
入门案例中的过滤器。

图中只展示了核心过滤器,其它的非核心过滤器并没有在图中展示。
**UsernamePasswordAuthenticationFilter**:负责处理我们在登陆页面填写了用户名密码后的登陆请求。入门案例的认证工作主要有它负责。
**ExceptionTranslationFilter**:处理过滤器链中抛出的任何AccessDeniedException和AuthenticationException 。
**FilterSecurityInterceptor**:负责权限校验的过滤器。
我们可以通过Debug查看当前系统中SpringSecurity过滤器链中有哪些过滤器及它们的顺序。
输入 `run.getBean(DefaultSecurityFilterChain.class)` 求值
### 3.3认证详流程详解

概念速查:
`Authentication`接口: 它的实现类,表示当前访问系统的用户,封装了用户相关信息。
`AuthenticationManager`接口:定义了认证Authentication的方法
`UserDetailsService`接口:加载用户特定数据的核心接口。里面定义了一个根据用户名查询用户信息的
方法。
`UserDetails`接口:提供核心用户信息。通过UserDetailsService根据用户名获取处理的用户信息要封装
成UserDetails对象返回。然后将这些信息封装到Authentication对象中
## 4.案例实战
### 4.1 思路分析
1. **自定义登录接口**
调用`ProviderManager`的方法进行认证 如果认证通过生成jwt
把用户信息存入redis中
> `SpringSecurity`在默认的认证过程中如果账号密码校验成功会返回Authentication对象之后`UsernamePasswordAuthenticationFilter`会将用户信息`Authentication`存入`SecurityContextHolder`中
>
> 但是我们在实际运用场景中认证通过后还需要向前端返回一个JSON格式的数据里面包括了JWT
>
> 所以此时我们需要写一个自定义登录接口
2. **自定义UserDetailsService接口**
在这个实现类中去查询数据库
校验:
定义Jwt认证过滤器
获取token
解析token获取其中的userid
从redis中获取用户信息
存入SecurityContextHolder
> `SpringSecurity` 默认是在内存中查找对应的用户名密码然后封装成`UserDetai`l对象交给`DaoAuthenticationProcider`校验
>
> 但是我们在实际运用场景中是从数据库中查找用户信息
>
> 所以此时我们需要写一个`UserDetailsService`的实现类用来在数据库中查询用户信息并且封装到`UserDetai`l对象中
### 4.2准备工作
1.**添加依赖**(pom.xml)
```xml
org.springframework.boot
spring-boot-starter-security
org.springframework.boot
spring-boot-starter-web
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-test
test
org.springframework.security
spring-security-test
test
org.springframework.boot
spring-boot-starter-data-redis
com.alibaba
fastjson
1.2.33
io.jsonwebtoken
jjwt
0.9.0
javax.xml.bind
jaxb-api
2.3.1
com.baomidou
mybatis-plus-spring-boot3-starter
3.5.5
mysql
mysql-connector-java
8.0.29
org.springframework.boot
spring-boot-starter-test
```
2.**添加Redis相关配置**(com.sangeng.utils | com.sangeng.config)
```java
public class FastJsonRedisSerializer implements RedisSerializer {
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private Class clazz;
static {
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
}
public FastJsonRedisSerializer(Class clazz) {
super();
this.clazz = clazz;
}
@Override
public byte[] serialize(T t) throws SerializationException {
if (t == null) {
return new byte[0];
}
return JSON.toJSONString(t,
SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
}
@Override
public T deserialize(byte[] bytes) throws SerializationException {
if (bytes == null || bytes.length <= 0) {
return null;
}
String str = new String(bytes, DEFAULT_CHARSET);
return JSON.parseObject(str, clazz);
}
protected JavaType getJavaType(Class> clazz) {
return TypeFactory.defaultInstance().constructType(clazz);
}
}
```
```java
@Configuration
public class RedisConfig {
@Bean
@SuppressWarnings(value = {"unchecked", "rawtypes"})
public RedisTemplate