SpringBoot 3 进阶教程之三整合 Spring Security

大纲

前言

概念介绍

安全架构

系统的安全架构有以下三大方面:

  • 认证 (Authentication): 登录系统,用户系统(解决你是谁的问题)
  • 授权 (Authorization): 权限管理,用户授权(解决你能干什么的问题)
  • 攻击防护
    • XSS (Cross-site scripting)
    • CSRF (Cross-site request forgery)
    • CORS (Cross-Origin Resource Sharing)
    • SQL 注入

权限模型

ACL 模型

  • ACL (Access Controll List)
    • 用户(t_user)
    • 用户_权限 (t_user_perm)
      • N 对 N 关系,需要有中间表
    • 权限(t_permission)

RBAC 模型

  • RBAC (Role Based Access Controll)
    • 用户 (t_user)
    • 用户_角色 (t_user_role)
      • N 对 N 关系,需要有中间表
    • 角色 (t_role)
    • 角色_权限 (t_role_perm)
      • N 对 N 关系,需要有中间表
    • 权限 (t_permission)

Spring Security

工作原理

过滤器链架构

Spring Security 的底层利用 FilterChainProxy 封装了一系列过滤器链,并实现了各种安全过滤功能。

Servlet 三大组件

Servlet 的三大组件分别是:Servlet、Filter (过滤器)、Listener (监听器)。

FilterChainProxy

SecurityFilterChain

使用示例

核心内容

  • WebSecurityConfigurerAdapter
  • @EnableGlobalMethodSecurity:开启全局方法安全配置
    • @Secured
    • @PreAuthorize
    • @PostAuthorize
  • UserDetailService:获取用户详细信息(用户基本信息、用户角色、用户权限)的 Service 类

方法安全

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@SpringBootApplication
@EnableGlobalMethodSecurity(securedEnabled = true)
public class SampleSecureApplication {

}

@Service
public class MyService {

@Secured("ROLE_USER")
public String secure() {
return "Hello Security";
}

}

HTTP 安全

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Configuration
@Order(SecurityProperties.BASIC_AUTH_ORDER - 10)
public class ApplicationConfigurerAdapter extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/match1/**")
.authorizeRequests()
.antMatchers("/match1/user").hasRole("USER")
.antMatchers("/match1/spam").hasRole("SPAM")
.anyRequest().isAuthenticated();
}

}

整合案例

本章节完整的案例代码可以直接从 GitHub 下载对应章节 spring-boot3-16

准备工作

引入依赖项
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
<!-- Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Thymeleaf -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity6</artifactId>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
创建首页的页面
1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Welcome Page</title>
</head>
<body>
<h3>Welcome to here.</h3>
<a th:href="@{/hello}">点击跳转 Hello 页面</a>
</body>
</html>
创建登录的页面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Login page</title>
</head>
<body>
<form th:action="@{/login}" method="post">
<div>
<label> User Name : <input type="text" name="username" /> </label>
</div>
<div>
<label> Password: <input type="password" name="password" /> </label>
</div>
<div><input type="submit" value="登录" /></div>
</form>
</body>
</html>
创建 Hello 页面
1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Hello Page</title>
</head>
<body>
<h3>Hello Spring Security.</h3>
</body>
</html>

核心代码

主启动类

提示

在主启动类上添加 @EnableMethodSecurity 注解,即开启方法级别的角色权限控制,这样就可以在方法上使用 @Secured@PreAuthorize@PostAuthorize 等注解了。

1
2
3
4
5
6
7
8
9
@EnableMethodSecurity
@SpringBootApplication
public class MainApplication {

public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}

}
控制器类
  • 登录控制器类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Controller
public class LoginController {

/**
* 跳转登录页面
*
* @return
*/
@GetMapping("/login")
public String login() {
return "/login";
}

}
  • Hello 控制器类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Controller
public class HelloController {

/**
* 跳转Hello页面
*
* @return
*/
@GetMapping("/hello")
@PreAuthorize("hasAuthority('file_read')")
public String hello() {
return "/hello";
}

}
安全配置类
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
@Configuration
public class WebSecurityConfiguration {

/**
* 请求认证
*/
@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
// 请求认证
http.authorizeHttpRequests(registry -> {
registry
.requestMatchers(("/")).permitAll() // 首页支持所有人访问
.anyRequest().authenticated(); // 其他任意请求都需要认证登录
});

// 用户登录
http.formLogin(formLoginConfigurer -> {
formLoginConfigurer.loginPage("/login").permitAll(); // 自定义登录页面,且所有人都可以访问登录页面
});

// 退出登录
http.logout(logoutConfigurer -> {
logoutConfigurer.permitAll(); // 退出登录接口允许所有人访问
});

return http.build();
}

/**
* 自定义用户信息
*/
@Bean
public UserDetailsService userDetailsService(PasswordEncoder passwordEncoder) {
UserDetails user = User.withUsername("admin")
.password(passwordEncoder.encode("123456"))
.roles("admin", "hr")
.authorities("file_read", "file_write")
.build();
return new InMemoryUserDetailsManager(user);
}

/**
* 密码加密器
*/
@Bean
public PasswordEncoder passwordEncoder () {
return new BCryptPasswordEncoder();
}

}

提示

除了可以使用上述的 UserDetailsService 类自定义用户信息(账号、密码、角色等),还可以直接在 YML / Properties 配置文件中指定,如下所示:

1
2
3
spring.security.user.name=admin
spring.security.user.password=123456
spring.security.user.roles=admin,hr

自动配置原理

  • Spring Security 的自动配置类:SecurityAutoConfigurationSpringBootWebSecurityConfigurationSecurityDataConfigurationSecurityFilterAutoConfiguration
  • Spring Security 的所有配置都在 SecurityProperties,以 spring.security 为配置前缀
  • 导入默认的 SecurityFilterChain 组件
    • 所有请求都需要认证(登录)
    • 支持使用 HTTP Basic 方式登录
    • 默认开启表单登录: Spring Security 提供一个默认登录页面,未经登录的所有请求都需要登录
  • @EnableWebSecurity 生效
    • WebSecurityConfiguration 生效,Web 安全配置
    • HttpSecurityConfiguration 生效,HTTP 安全配置
    • @EnableGlobalAuthentication生效:全局安全认证生效
      • AuthenticationConfiguration:认证配置