Spring Security Reactive
- RBAC权限模型
- WebFlux配置:@EnableWebFluxSecurity、@EnableReactiveMethodSecurity
- SecurityFilterChain 组件
- AuthenticationManager 组件
- UserDetailsService 组件
- 基于注解的方法级别授权
 整合
| 12
 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
 
 | <dependencies>
 <dependency>
 <groupId>io.asyncer</groupId>
 <artifactId>r2dbc-mysql</artifactId>
 <version>1.0.5</version>
 </dependency>
 
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-data-r2dbc</artifactId>
 </dependency>
 
 
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-webflux</artifactId>
 </dependency>
 
 
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-test</artifactId>
 <scope>test</scope>
 </dependency>
 
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-security</artifactId>
 </dependency>
 
 <dependency>
 <groupId>org.projectlombok</groupId>
 <artifactId>lombok</artifactId>
 </dependency>
 </dependencies>
 
 | 
 开发
 应用安全
- 防止攻击: 
- 控制权限 
- 登录的用户能干什么。
- 用户登录系统以后要控制住用户的所有行为,防止越权;
 
- 传输加密 
- 认证: 
 RBAC权限模型
Role Based Access Controll: 基于角色的访问控制
一个网站有很多用户,每个用户可以有很多角色,一个角色可以关联很多权限。一个人到底能干什么?
权限控制:
- 找到这个人,看他有哪些角色,每个角色能拥有哪些权限。 这个人就拥有一堆的 角色 或者 权限
- 这个人执行方法的时候,我们给方法规定好权限,由权限框架负责判断,这个人是否有指定的权限
所有权限框架:
- 让用户登录进来: 认证(authenticate):用账号密码、各种其他方式,先让用户进来
- 查询用户拥有的所有角色和权限: 授权(authorize): 每个方法执行的时候,匹配角色或者权限来判定用户是否可以执行这个方法
 认证
登录行为
 静态资源放行
 其他请求需要登录
| 12
 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
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 
 | package com.yuanyuan.security.config;
 import blog.yuanyuan.security.component.AppReactiveUserDetailsService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.autoconfigure.security.reactive.PathRequest;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Primary;
 import org.springframework.security.authentication.UserDetailsRepositoryReactiveAuthenticationManager;
 import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity;
 import org.springframework.security.config.web.server.ServerHttpSecurity;
 import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 import org.springframework.security.crypto.factory.PasswordEncoderFactories;
 import org.springframework.security.crypto.password.PasswordEncoder;
 import org.springframework.security.web.SecurityFilterChain;
 import org.springframework.security.web.server.SecurityWebFilterChain;
 
 @Configuration
 @EnableReactiveMethodSecurity
 public class AppSecurityConfiguration {
 
 
 @Autowired
 ReactiveUserDetailsService appReactiveUserDetailsService;
 
 @Bean
 SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
 
 http.authorizeExchange(authorize -> {
 
 authorize.matchers(PathRequest.toStaticResources()
 .atCommonLocations()).permitAll();
 
 
 
 authorize.anyExchange().authenticated();
 });
 
 
 http.formLogin(formLoginSpec -> {
 
 });
 
 
 http.csrf(csrfSpec -> {
 csrfSpec.disable();
 });
 
 
 
 
 
 
 
 
 
 
 http.authenticationManager(
 new UserDetailsRepositoryReactiveAuthenticationManager(
 appReactiveUserDetailsService)
 );
 
 
 return http.build();
 }
 
 
 @Primary
 @Bean
 PasswordEncoder passwordEncoder(){
 
 PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
 return encoder;
 }
 }
 
 | 

这个界面点击登录,最终Spring Security 框架会使用 ReactiveUserDetailsService 组件,按照 表单提交的用户名 去数据库查询这个用户详情(基本信息[账号、密码],角色,权限)。把数据库中返回的 用户详情 中的密码 和 表单提交的密码进行比对。比对成功则登录成功。
| 12
 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
 57
 58
 59
 60
 
 | package blog.yuanyuan.security.component;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.r2dbc.core.DatabaseClient;
 import org.springframework.security.core.authority.SimpleGrantedAuthority;
 import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
 import org.springframework.security.core.userdetails.User;
 import org.springframework.security.core.userdetails.UserDetails;
 import org.springframework.security.crypto.factory.PasswordEncoderFactories;
 import org.springframework.security.crypto.password.PasswordEncoder;
 import org.springframework.stereotype.Component;
 import reactor.core.publisher.Mono;
 
 @Component
 public class AppReactiveUserDetailsService implements ReactiveUserDetailsService {
 
 
 @Autowired
 DatabaseClient databaseClient;
 
 
 
 @Autowired
 PasswordEncoder passwordEncoder;
 @Override
 public Mono<UserDetails> findByUsername(String username) {
 
 
 
 
 Mono<UserDetails> userDetailsMono = databaseClient.sql("select u.*,r.id rid,r.name,r.value,pm.id pid,pm.value pvalue,pm.description " +
 "from t_user u " +
 "left join t_user_role ur on ur.user_id=u.id " +
 "left join t_roles r on r.id = ur.role_id " +
 "left join t_role_perm rp on rp.role_id=r.id " +
 "left join t_perm pm on rp.perm_id=pm.id " +
 "where u.username = ? limit 1")
 .bind(0, username)
 .fetch()
 .one()
 .map(map -> {
 UserDetails details = User.builder()
 .username(username)
 .password(map.get("password").toString())
 
 
 
 
 .roles("admin", "sale","haha","delete")
 .build();
 
 
 
 
 return details;
 });
 
 return userDetailsMono;
 }
 }
 
 | 
 授权
@EnableReactiveMethodSecurity
| 12
 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
 
 | package blog.yuanyuan.security.controller;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RestController;
 import reactor.core.publisher.Mono;
 
 @RestController
 public class HelloController {
 
 
 @PreAuthorize("hasRole('admin')")
 @GetMapping("/hello")
 public Mono<String> hello(){
 
 return Mono.just("hello world!");
 }
 
 
 
 
 
 
 @PreAuthorize("hasRole('delete')")
 @GetMapping("/world")
 public Mono<String> world(){
 return Mono.just("world!!!");
 }
 }
 
 | 
配置: SecurityWebFilterChain
| 12
 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
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 
 | package blog.yuanyuan.security.config;
 import blog.yuanyuan.security.component.AppReactiveUserDetailsService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.autoconfigure.security.reactive.PathRequest;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Primary;
 import org.springframework.security.authentication.UserDetailsRepositoryReactiveAuthenticationManager;
 import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity;
 import org.springframework.security.config.web.server.ServerHttpSecurity;
 import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 import org.springframework.security.crypto.factory.PasswordEncoderFactories;
 import org.springframework.security.crypto.password.PasswordEncoder;
 import org.springframework.security.web.SecurityFilterChain;
 import org.springframework.security.web.server.SecurityWebFilterChain;
 
 @Configuration
 @EnableReactiveMethodSecurity
 public class AppSecurityConfiguration {
 
 @Autowired
 ReactiveUserDetailsService appReactiveUserDetailsService;
 
 @Bean
 SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
 
 http.authorizeExchange(authorize -> {
 
 authorize.matchers(PathRequest.toStaticResources()
 .atCommonLocations()).permitAll();
 
 authorize.anyExchange().authenticated();
 });
 
 
 http.formLogin(formLoginSpec -> {
 
 });
 
 
 http.csrf(csrfSpec -> {
 csrfSpec.disable();
 });
 
 
 
 
 
 
 
 
 
 
 http.authenticationManager(
 new UserDetailsRepositoryReactiveAuthenticationManager(
 appReactiveUserDetailsService)
 );
 
 
 return http.build();
 }
 
 
 @Primary
 @Bean
 PasswordEncoder passwordEncoder(){
 
 PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
 return encoder;
 }
 }
 
 |