Reactor 响应式编程(第四篇:Spring Security Reactive)
系列文章目录
Reactor 响应式编程(第一篇:Reactor核心)
Reactor 响应式编程(第二篇:Spring Webflux)
Reactor 响应式编程(第三篇:R2DBC)
Reactor 响应式编程(第四篇:Spring Security Reactive)
文章目录
- 系列文章目录
- 1. 整合
- 2. 开发
- 2.1 应用安全
- 2.2 RBAC权限模型
- 3. 认证
- 3.1 静态资源放行
- 3.2 其他请求需要登录
- 4. 授权
1. 整合
目标:
SpringBoot + Webflux + Spring Data R2DBC + Spring Security
任务:
- RBAC权限模型
- WebFlux配置:@EnableWebFluxSecurity、@EnableReactiveMethodSecurity
- SecurityFilterChain 组件
- AuthenticationManager 组件
- UserDetailsService 组件
- 基于注解的方法级别授权
<dependencies><!-- https://mvnrepository.com/artifact/io.asyncer/r2dbc-mysql --><dependency><groupId>io.asyncer</groupId><artifactId>r2dbc-mysql</artifactId><version>1.0.5</version></dependency><!-- 响应式 Spring Data R2dbc--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-r2dbc</artifactId></dependency><!-- 响应式Web --><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>
2. 开发
2.1 应用安全
- 防止攻击:
- DDos、CSRF、XSS、SQL注入…
- 控制权限
- 登录的用户能干什么。
- 用户登录系统以后要控制住用户的所有行为,防止越权;
- 传输加密
- https
- X509
- 认证:
- OAuth2.0
- JWT
2.2 RBAC权限模型
Role Based Access Controll: 基于角色的访问控制
一个网站有很多用户: zhangsan
每个用户可以有很多角色:
一个角色可以关联很多权限:
一个人到底能干什么?
权限控制:
- 找到这个人,看他有哪些角色,每个角色能拥有哪些权限。 这个人就拥有一堆的 角色 或者 权限
- 这个人执行方法的时候,我们给方法规定好权限,由权限框架负责判断,这个人是否有指定的权限
所有权限框架:
- 让用户登录进来: 认证(authenticate):用账号密码、各种其他方式,先让用户进来
- 查询用户拥有的所有角色和权限: 授权(authorize): 每个方法执行的时候,匹配角色或者权限来判定用户是否可以执行这个方法
导入Spring Security:默认效果
3. 认证
3.1 静态资源放行
3.2 其他请求需要登录
package com.atguigu.security.config;import com.atguigu.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;/*** @author lfy* @Description* @create 2023-12-24 21:39*/
@Configuration
@EnableReactiveMethodSecurity //开启响应式 的 基于方法级别的权限控制
public class AppSecurityConfiguration {@AutowiredReactiveUserDetailsService appReactiveUserDetailsService;@BeanSecurityWebFilterChain 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();}@Primary@BeanPasswordEncoder passwordEncoder(){PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();return encoder;}
}
这个界面点击登录,最终Spring Security 框架会使用 ReactiveUserDetailsService 组件,按照 表单提交的用户名 去数据库查询这个用户详情(基本信息[账号、密码],角色,权限);
把数据库中返回的 用户详情 中的密码 和 表单提交的密码进行比对。比对成功则登录成功;
package com.atguigu.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;/*** @author lfy* @Description* @create 2023-12-24 21:57*/
@Component // 来定义如何去数据库中按照用户名查用户
public class AppReactiveUserDetailsService implements ReactiveUserDetailsService {@AutowiredDatabaseClient databaseClient;// 自定义如何按照用户名去数据库查询用户信息@AutowiredPasswordEncoder passwordEncoder;@Overridepublic Mono<UserDetails> findByUsername(String username) {// PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();//从数据库查询用户、角色、权限所有数据的逻辑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())//自动调用密码加密器把前端传来的明文 encode
// .passwordEncoder(str-> passwordEncoder.encode(str)) //为啥???//权限
// .authorities(new SimpleGrantedAuthority("ROLE_delete")) //默认不成功.roles("admin", "sale","haha","delete") //ROLE成功.build();//角色和权限都被封装成 SimpleGrantedAuthority// 角色有 ROLE_ 前缀, 权限没有// hasRole:hasAuthorityreturn details;});return userDetailsMono;}
}
4. 授权
@EnableReactiveMethodSecurity
package com.atguigu.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;/*** @author lfy* @Description* @create 2023-12-24 21:31*/
@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!!!");}
}
官方实例
配置是: SecurityWebFilterChain
package com.atguigu.security.config;import com.atguigu.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;/*** @author lfy* @Description* @create 2023-12-24 21:39*/
@Configuration
@EnableReactiveMethodSecurity //开启响应式 的 基于方法级别的权限控制
public class AppSecurityConfiguration {@AutowiredReactiveUserDetailsService appReactiveUserDetailsService;@BeanSecurityWebFilterChain 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));//构建出安全配置return http.build();}@Primary@BeanPasswordEncoder passwordEncoder(){PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();return encoder;}
}
相关文章:

Reactor 响应式编程(第四篇:Spring Security Reactive)
系列文章目录 Reactor 响应式编程(第一篇:Reactor核心) Reactor 响应式编程(第二篇:Spring Webflux) Reactor 响应式编程(第三篇:R2DBC) Reactor 响应式编程(…...

JVM 双亲委派模型以及垃圾回收机制
目录 1. JVM 内存区域划分 2. JVM 中类加载的过程 1) 类加载的基本流程 2) 双亲委派模型 3. JVM 中垃圾回收机制 1) 找到垃圾 a) 引用计数 b) 可达性分析 2) 释放垃圾 1. JVM 内存区域划分 一个运行起来的 Java 进程,其实就是一个 JVM 虚拟机。 而进程是…...

Delphi编写涂鸦桌面的小程序
用Delphi编写涂鸦桌面的小程序,类似于腾讯会议中的画板功能的实现。这里用Delphi实现代码给大家提供一些思路; 首先,新建一个Application,将Form1的WindowState设为wsMaximized,BorderStyle设为bsNone。这样做的目的就…...

智星云技术文档:GPU测速教程
安装gpu burn git clone https://github.com/wilicc/gpu-burn cd gpu-burn/ make测试 ./gpu_burn 60100.0% procd: 14280 (7373 Gflop/s) - 13390 (6997 Gflop/s) - 15912 (7110 Gflop/s) - 13184 (7055 Gflop/s) - 13464 (7369 Gflop/s) - 13974 (7351 Gflop/s) - 16626 (7…...

《Kali Linux 软件源更换攻略:优化软件获取与系统更新》
KALI为什么要换源 速度提升 Kali Linux 默认的软件源服务器通常位于国外。在从这些国外源下载软件包、更新系统时,会受到网络带宽、网络延迟等因素的限制。例如,在中国,连接到国外服务器的网络速度可能较慢,尤其是在下载大型软件…...

C# 在dataview可以直接增删改查mysql数据库
C# 在dataview可以直接增删改查mysql数据库 首先,确保你的项目中已经安装了MySql.Data。你可以通过NuGet包管理器安装它: Install-Package MySql.Data -Version 8.0.28using System; using System.Data; using MySql.Data.MySqlClient;public class My…...

C#—泛型约束
C#—泛型约束 概念: 泛型约束就是告知编译器类型参数必须具备的功能。 在没有任何约束的情况下,类型参数可以是任何类型。 编译器只能假定 System.Object 的成员,它是任何 .NET 类型的最终基类。当分配给泛型的类型参数不满足约束的类型时&…...

MeiliSearch:一款轻量级开源搜索引擎
Meilisearch 是由 Meili (一家总部位于法国的软件开发公司)创建的搜索引擎,目前在 Github 上有 47.9k stars。 Meillisearch 具备以下特色功能(ChatGPT-4o 翻译): 混合搜索:结合语义搜索和全文…...

Ansible playbook 详解与实战操作
一、概述 playbook 与 ad-hoc 相比,是一种完全不同的运用 ansible 的方式,类似与 saltstack 的 state 状态文件。ad-hoc 无法持久使用,playbook 可以持久使用。 playbook 是由一个或多个 play 组成的列表,play 的主要功能在于将事先归并为一…...

青少年夏令营管理系统的设计与开发(社团管理)(springboot+vue)+文档
💗博主介绍💗:✌在职Java研发工程师、专注于程序设计、源码分享、技术交流、专注于Java技术领域和毕业设计✌ 温馨提示:文末有 CSDN 平台官方提供的老师 Wechat / QQ 名片 :) Java精品实战案例《700套》 2025最新毕业设计选题推荐…...

加速合并,音频与字幕的探讨
因上一节。合并时速度太慢了。显卡没用上。所以想快一点。1分钟的视频用了5分钟。 在合并视频时,进度条中的 now=None 通常表示当前处理的时间点没有被正确记录或显示。这可能是由于 moviepy 的内部实现细节或配置问题。为了加快视频合并速度并利用 GPU 加速,可以采取以下措…...

Uniapp插件如何通过NFC读取多种证卡信息?
nfc读卡uniapp插件,由中软高科进行开发,主要是通过NFC读取居民身份证、港澳台居住证、外国人居住证、护照等证卡的信息。经过多个版本的升级更新,目前性能已趋于稳定,并且读卡速度较之最初版本有了大的提升。 注意事项 测试使用的…...

米哈游C++开发精选60道面试题及参考答案
C++ 面向对象的三个特征 封装是把数据和操作数据的函数捆绑在一起,并且对数据的访问进行限制。这样做的好处是可以隐藏对象的内部实现细节,只暴露必要的接口给外部。例如,在一个银行账户类中,账户余额这个数据成员是被封装起来的,外部不能直接访问和修改,而是通过存款、取…...

深度与视差的关系及其转换
深度与视差的关系及其转换 在计算机视觉和立体视觉中,深度和视差是两个重要的概念。理解这两者之间的关系对于实现立体图像处理、三维重建以及深度估计至关重要。在这篇博客中,我们将深入探讨深度和视差的概念,并介绍它们之间的转换关系。 …...

安全见闻全解析
跟随 泷羽sec团队学习 声明! 学习视频来自B站up主 泷羽sec 有兴趣的师傅可以关注一下,如涉及侵权马上删除文章,笔记只是方便各位师傅的学习和探讨,文章所提到的网站以及内容,只做学习交流,其他均与本人以及…...

搭建Tomcat(四)---Servlet容器
目录 引入 Servlet容器 一、优化MyTomcat ①先将MyTomcat的main函数搬过来: ②将getClass()函数搬过来 ③创建容器 ④连接ServletConfigMapping和MyTomcat 连接: ⑤完整的ServletConfigMapping和MyTomcat方法: a.ServletConfigMappin…...

PT2044A 单触控单输出IC
1 产品概述 ● PT2044A 是一款单通道触摸检测芯片。该芯片内建稳压电路,提供稳定电压给触摸感应电路使用。同时内部集成高效完善的触摸检测算法,使得芯片具有稳定的触摸检测效果。该芯片专为取代传统按键而设计,具有宽工作电压与低功耗的特性…...

docker安装mysql5.7
1、宿主机创建映射目录 mkdir -p /data/mysql/log mkdir -p /data/mysql/data mkdir -p /data/mysql/conf这里我放在了/data/mysql目录下 2、拉取mysql镜像 docker pull mysql:5.7注意是5.7版本,如果是8版本操作会略有不同,下篇文章介绍安装8版本的操…...

安卓 文件管理相关功能记录
文件管理细分为图片、视频、音乐、文件四类 目录 权限 静态声明权限 动态检查和声明权限方法 如何开始上述动态申请的流程 提示 图片 获取图片文件的对象列表 展示 删除 视频 获取视频文件的对象列表 获取视频file列表 按日期装载视频文件列表 展示 播放 删除…...

GB28181系列三:GB28181流媒体服务器ZLMediaKit
我的音视频/流媒体开源项目(github) GB28181系列目录 目录 一、ZLMediaKit介绍 二、 ZLMediaKit安装、运行(Ubuntu) 1、安装 2、运行 3、配置 三、ZLMediaKit使用 一、ZLMediaKit介绍 ZLMediaKit是一个基于C11的高性能运营级流媒体服务框架,项目地址…...

ScottPlot学习的常用笔记
ScottPlot学习的常用笔记 写在前面版本的选择第一个障碍:版本问题。 ScottPlot4.0的官方网站与示例官方起始页cookbook5.0Demo4.1 demo以4.1为例,解压和运行如下: 下载源代码和编译先说结论: 写在前面 之前调研的TraceCompass&am…...

二、mapbox-gl实现白膜立体建筑
有时候我们只有二维的面数据,怎么实现类似高德地图中的白膜立体建筑呢?在mapbox-gl中很容易实现,具体如下。 要在Vue中结合Mapbox显示自定义的GeoJSON数据,并实现建筑物的白膜效果,我们需要执行以下步骤: …...

mybatisplus 分库查询
mybatisplus 分库查询 比如我们的项目有两个数据库 不同的表在不同的库 我们是可以使用mybatisplus来实现 首选引入pom <dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId><vers…...

计算属性 (vue3)
二 实例 1route.query.type...

RabbitMQ 安装、配置和使用介绍 使用前端js直接调用方式
1. 安装 RabbitMQ 1.1 安装 Erlang RabbitMQ 是基于 Erlang 语言开发的,因此首先需要安装 Erlang。 在 Ubuntu 上安装 Erlang: bash sudo apt-get update sudo apt-get install erlang 在 CentOS 上安装 Erlang: bash sudo yum insta…...

电脑显示器选购指南2024
选择显示器是五花八门的显示参数,如何选择,以下给出参数说明,及部分参考: 1. 尺寸和分辨率 尺寸(英寸) 根据使用距离和用途选择合适的屏幕尺寸: 21-24 英寸:适合小桌面空间、日常…...

vue2中如何实现自定义指令
实现自动聚焦功能 1.不用自定义指令 使用生命周期钩子mounted 2.使用自定义指令 1. 使用全局组件 首先在main.js中注册 然后在组件中直接使用v-指令名 2. 使用局部注册 局部注册和全局注册类似 如果很多组件需要自定义,建议使用自定义指令注册在全局...

QT从入门到精通(一)——Qlabel介绍与使用
1. QT介绍——代码测试 Qt 是一个跨平台的应用程序开发框架,广泛用于开发图形用户界面(GUI)应用程序,也支持非图形应用程序的开发。Qt 提供了一套工具和库,使得开发者能够高效地构建高性能、可移植的应用程序。以下是…...

【jpa】springboot使用jpa示例
目录 1. 请求示例2. pom依赖3. application.yaml4.controller5. service6. repository7. 实体8. 启动类 1. 请求示例 curl --location --request POST http://127.0.0.1:8080/user \ --header User-Agent: Apifox/1.0.0 (https://apifox.com) \ --header Content-Type: applic…...

Python Flask Web框架快速入门
Flask 入门Demo Flask 开发环境搭建,执行如下指令: pip install flask# 第一节: Flask 快速入门from flask import Flask app Flask(__name__)app.route(/flask) def hello_flask():return Hello Flaskapp.run()核心代码剖析: 从flask包导…...