SpringSecurity

Spring Security

官方文档:https://docs.spring.io/spring-security/reference/index.html

功能

  • 身份认证(authentication)
  • 授权(authorization)
  • 防御常见攻击(protection against common attacks)

身份认证

官方代码示例:GitHub - spring-projects/spring-security-samples

  • 身份认证是验证谁正在访问系统资源,判断用户是否为合法用户。认证用户的常见方式是要求用户输入用户名和密码

授权

  • 用户进行身份认证后,系统会控制谁能访问哪些资源,这个过程叫做授权。用户无法访问没有权限的资源

防御常见攻击

  • CSRF
  • HTTP Headers
  • HTTP Requests

默认

  • 保护应用程序 URL,要求对应用程序的任何交互进行身份验证
  • 程序启动时生成一个默认用户 “user”
  • 生成一个默认的随机密码,并将此密码记录在控制台上
  • 生成默认的登录表单和注销页面
  • 提供基于表单的登录和注销流程
  • 对于Web 请求,重定向到登录页面
  • 对于服务请求,返回 401 未经授权
  • 处理跨站请求伪造(CSRF)攻击
  • 处理会话劫持攻击
  • 写入 Strict-Transport-Security 以确保 HTTPS
  • 写于 X-Content-Type-Options 以处理嗅探攻击
  • 写入 Cache Control 头来保护经过身份验证的资源
  • 写入 X-Frame-Options 以处理点击劫持攻击

底层原理

官方文档:https://docs.spring.io/spring-security/reference/servlet/architecture.html

Spring Security之所以默认帮助我们做了那么多事情,它的底层原理是传统的 Servlet 过滤器

  • Filter
  • DelegatingFilterProxy
  • FilterChainProxy
  • SecurityFilterChain
  • Multiple SecurityFilterChain

自定义配置

官方文档:Java自定义配置

基于内存的用户认证

1、创建自定义配置

**UserDetailsService **用来管理用户信息,**InMemoryUserDetailsManager **是 UserDetailsService 的一个实现,用来管理基于内存的用户信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Configuration
@EnableWebSecurity //Spring项目总需要添加此注解,SpringBoot项目中不需要
public class WebSecurityConfig {

@Bean
public UserDetailsService userDetailsService() {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser( //此行设置断点可以查看创建的user对象
User
.withDefaultPasswordEncoder()
.username("ren") //自定义用户名
.password("password") //自定义密码
.roles("USER") //自定义角色
.build()
);
return manager;
}
}

2、基于内存的用户认证流程

  • 程序启动时:
    • 创建 InMemoryUserDetailsManager 对象
    • 创建 User 对象,封装用户名密码
    • 使用 InMemoryUserDetailsManagerUser 存入内存
  • 校验用户时:
    • SpringSecurity自动使用 InMemoryUserDetailsManagerloadUserByUsername 方法从内存中获取 User 对象
    • UsernamePasswordAuthenticationFilter 过滤器中的 attemptAuthentication 方法中将用户输入的用户名密码和从内存中获取到的用户信息进行比较,进行用户认证

基于数据库的数据源

1、SQL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
-- 创建数据库
CREATE DATABASE `security-demo`;
USE `security-demo`;

-- 创建用户表
CREATE TABLE `user`(
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`username` VARCHAR(50) DEFAULT NULL ,
`password` VARCHAR(500) DEFAULT NULL,
`enabled` BOOLEAN NOT NULL
);
-- 唯一索引
CREATE UNIQUE INDEX `user_username_uindex` ON `user`(`username`);

-- 插入用户数据(密码是 "abc" )
INSERT INTO `user` (`username`, `password`, `enabled`) VALUES
('admin', '{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW', TRUE),
('Helen', '{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW', TRUE),
('Tom', '{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW', TRUE);

2、引入依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>

<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.4.1</version>
<exclusions>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
</exclusion>
</exclusions>
</dependency>

<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>3.0.3</version>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>

3、配置数据源

1
2
3
4
5
6
7
#MySQL数据源
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/security-demo
spring.datasource.username=root
spring.datasource.password=123456
#SQL日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

4、实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
@Data
public class User {

@TableId(value = "id", type = IdType.AUTO)
private Integer id;

private String username;

private String password;

private Boolean enabled;

}

5、Mapper

1
2
3
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
1
2
3
4
5
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.securitydemo.mapper.UserMapper">

</mapper>

6、Service

1
2
public interface UserService extends IService<User> {
}
1
2
3
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}

7、Controller

1
2
3
4
5
6
7
8
9
10
11
12
@RestController
@RequestMapping("/user")
public class UserController {

@Resource
public UserService userService;

@GetMapping("/list")
public List<User> getList(){
return userService.list();
}
}

基于数据库的用户认证

1、基于数据库的用户认证流程

  • 程序启动时:
    • 创建 DBUserDetailsManager 类,实现接口 UserDetailsManager, UserDetailsPasswordService
    • 在应用程序中初始化这个类的对象
  • 校验用户时:
    • SpringSecurity 自动使用 DBUserDetailsManagerloadUserByUsername 方法从数据库中获取 User 对象
    • UsernamePasswordAuthenticationFilter 过滤器中的 attemptAuthentication 方法中将用户输入的用户名密码和从数据库中获取到的用户信息进行比较,进行用户认证

2、定义 DBUserDetailsManager

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
public class DBUserDetailsManager implements UserDetailsManager, UserDetailsPasswordService {

@Resource
private UserMapper userMapper;

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username", username);
User user = userMapper.selectOne(queryWrapper);
if (user == null) {
throw new UsernameNotFoundException(username);
} else {
Collection<GrantedAuthority> authorities = new ArrayList<>();
return new org.springframework.security.core.userdetails.User(
user.getUsername(),
user.getPassword(),
user.getEnabled(),
true, //用户账号是否过期
true, //用户凭证是否过期
true, //用户是否未被锁定
authorities); //权限列表
}
}

@Override
public UserDetails updatePassword(UserDetails user, String newPassword) {
return null;
}

@Override
public void createUser(UserDetails user) {

}

@Override
public void updateUser(UserDetails user) {

}

@Override
public void deleteUser(String username) {

}

@Override
public void changePassword(String oldPassword, String newPassword) {

}

@Override
public boolean userExists(String username) {
return false;
}
}

3、初始化UserDetailsService

1
2
3
4
5
@Bean
public UserDetailsService userDetailsService() {
DBUserDetailsManager manager = new DBUserDetailsManager();
return manager;
}

SpringSecurity的默认配置

1
2
3
4
5
6
7
8
9
10
11
12
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
//authorizeRequests():开启授权保护
//anyRequest():对所有请求开启授权保护
//authenticated():已认证请求会自动被授权
http
.authorizeRequests(authorize -> authorize.anyRequest().authenticated())
.formLogin(withDefaults())//表单授权方式
.httpBasic(withDefaults());//基本授权方式

return http.build();
}

添加用户功能

1、Controller

1
2
3
4
@PostMapping("/add")
public void add(@RequestBody User user){
userService.saveUserDetails(user);
}

2、Service

1
void saveUserDetails(User user);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Resource
private DBUserDetailsManager dbUserDetailsManager;

@Override
public void saveUserDetails(User user) {

UserDetails userDetails = org.springframework.security.core.userdetails.User
.withDefaultPasswordEncoder()
.username(user.getUsername()) //自定义用户名
.password(user.getPassword()) //自定义密码
.build();
dbUserDetailsManager.createUser(userDetails);

}

3、修改配置

1
2
3
4
5
6
7
8
9
@Override
public void createUser(UserDetails userDetails) {

User user = new User();
user.setUsername(userDetails.getUsername());
user.setPassword(userDetails.getPassword());
user.setEnabled(true);
userMapper.insert(user);
}

4、使用 Swagger 测试

1
2
3
4
5
6
<!--swagger测试-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>4.1.0</version>
</dependency>

5、关闭 CSRF 攻击防御

filterChain 方法中添加如下代码,关闭 CSRF 攻击防御

1
2
3
4
//关闭csrf攻击防御
http.csrf((csrf) -> {
csrf.disable();
});

SpringSecurity
https://www.renkelin.vip/2024/01/30/SpringSecurity/
Author
Kolin
Posted on
January 30, 2024
Licensed under