一、概述
1.1应用安全
防止攻击:
○ DDos、CSRF、XSS、SQL注入...
控制权限
○ 登录的用户能干什么。
○ 用户登录系统以后要控制住用户的所有行为,防止越权;
传输加密
○ https
○ X509
认证:
○ OAuth2.0
○ JWT
1.2RBAC权限模型
Role Based Access Controll: 基于角色的访问控制
一个网站有很多用户: zhangsan
每个用户可以有很多角色:
一个角色可以关联很多权限:
一个人到底能干什么?
权限控制:
- 找到这个人,看他有哪些角色,每个角色能拥有哪些权限。 这个人就拥有一堆的 角色 或者 权限
- 这个人执行方法的时候,我们给方法规定好权限,由权限框架负责判断,这个人是否有指定的权限
所有权限框架:
- 让用户登录进来: 认证(authenticate):用账号密码、各种其他方式,先让用户进来
- 查询用户拥有的所有角色和权限: 授权(authorize): 每个方法执行的时候,匹配角色或者权限来判定用户是否可以执行这个方法
二、快速入门
2.1认证
1、静态资源放行
2、其他请求需要登录
@Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
//1、定义哪些请求需要认证,哪些不需要
http.authorizeExchange(authorize -> {
//1.1、允许所有人都访问静态资源;
authorize.matchers(PathRequest.toStaticResources()
.atCommonLocations()).permitAll();
//1.2、剩下的所有请求都需要认证(登录)
authorize.anyExchange().authenticated();
});
//2、开启默认的表单登录
http.formLogin(formLoginSpec -> {
// formLoginSpec.loginPage("/haha");
});
//3、安全控制:
http.csrf(csrfSpec -> {
csrfSpec.disable();
});
// 目前认证: 用户名 是 user 密码是默认生成。
// 期望认证: 去数据库查用户名和密码
//4、配置 认证规则: 如何去数据库中查询到用户;
// Sprinbg Security 底层使用 ReactiveAuthenticationManager 去查询用户信息
// ReactiveAuthenticationManager 有一个实现是
// UserDetailsRepositoryReactiveAuthenticationManager: 用户信息去数据库中查
// UDRespAM 需要 ReactiveUserDetailsService:
// 我们只需要自己写一个 ReactiveUserDetailsService: 响应式的用户详情查询服务
http.authenticationManager(
new UserDetailsRepositoryReactiveAuthenticationManager(
appReactiveUserDetailsService)
);
// http.addFilterAt()
//构建出安全配置
return http.build();
}
基于内存的使用
如果我们想配置基于内存的用户信息,该怎么配置呢?添加如下配置类即可:
@Configuration
public class SecurityConfig {
@Bean
MapReactiveUserDetailsService mapReactiveUserDetailsService() {
UserDetails ud1 = User.withUsername("admin")
.password("{noop}123")
.roles("admin")
.build();
UserDetails ud2 = User.withUsername("zhangsan")
.password("{noop}123")
.roles("user")
.build();
return new MapReactiveUserDetailsService(ud1, ud2);
}
}
基于数据库的使用
@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()// all()
.map(map -> {
UserDetails details = User.builder()
.username(username)
.password(map.get("password").toString())
// 使用密码编码器对密码进行加密处理
// 权限
// 设置用户的角色和权限,这里使用了硬编码的方式,实际应用中应该从数据库或其他配置中获取
.roles("admin", "sale","haha","delete") //ROLE成功
.build();
// 返回构建好的用户详细信息对象
return details;
});
// 返回包含用户详细信息的Mono对象
return userDetailsMono;
}
}
这个界面点击登录,最终Spring Security 框架会使用 ReactiveUserDetailsService 组件,按照 表单提交的用户名 去数据库查询这个用户详情(基本信息[账号、密码],角色,权限);
把数据库中返回的 用户详情 中的密码 和 表单提交的密码进行比对。比对成功则登录成功;
2.2授权
@EnableReactiveMethodSecurity
@RestController
public class HelloController {
@PreAuthorize("hasRole('admin')")
@GetMapping("/hello")
public Mono<String> hello(){
return Mono.just("hello world!");
}
// 角色 haha: ROLE_haha:角色
// 没有ROLE 前缀是权限
//复杂的SpEL表达式
@PreAuthorize("hasRole('delete')")
@GetMapping("/world")
public Mono<String> world(){
return Mono.just("world!!!");
}
}