[spring] rest api security

[spring] rest api security

之前的 rest api CRUD 都没有实现验证(authentication)和授权(Authorization),这里使用 Spring security 进行补全

spring security 是一个非常灵活、可延伸的实现方式,比较简单的可以通过注解(declarative)的方式实现,想要更具体的,也可以通过编程式(programmatic)实现。

整体流程大概如下:

在这里插入图片描述

POM 更新

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>

一旦添加了 dependency、rebuild 之后,spring boot 就会默认开启验证功能,默认情况下提供的密码:

在这里插入图片描述

效果如下:

在这里插入图片描述

在这里插入图片描述

修改默认密码的方式是修改 properties 文件:

# Spring Security Property
spring.security.user.name=admin
spring.security.user.password=1234

效果如下:

在这里插入图片描述

基础实现

用户验证

下面是一个基础的用户权限管理:

User IDPasswordRoles
workerpass1234employee
bosspwdSecureemployee, manager
admin789passwordemployee, manager, admin

这里可以通过手写的方式写入用户名和权限,完成基础设定:

@Configuration
public class DemoSecurityConfig {
    @Bean
    public InMemoryUserDetailsManager userDetailsManager() {
        UserDetails worker = User.builder().username("worker").password("{noop}pass1234").roles("employee").build();
        UserDetails boss = User.builder().username("boss").password("{noop}pass1234").roles("employee", "manager")
                .build();
        UserDetails admin = User.builder().username("admin").password("{noop}pass1234")
                .roles("employee", "manager", "admin").build();

        return new InMemoryUserDetailsManager(worker, boss, admin);
    }
}

这个情况下 spring 会选择代码中写入的用户名和密码,而不是 properties 文件中,效果如下:

在这里插入图片描述

权限设定

权限的部分则是通过对 role 的授权实现,例如说 employee 只有读的权利,manager 有写的权利,admin 有删的权限:

HTTP MethodEndpointCRUD ActionRole
GET/api/employeesRead Allemployee
GET/api/employees/{id}Read Singleemployee
POST/api/employeesCreatemanager
PUT/api/employees/{id}Updatemanager
DELETE/api/employees/{id}Deleteadmin

这个部分可以通过添加 requestMatchers 实现,如:

@Bean
    public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.authorizeHttpRequests(configurer ->
                configurer
                        .requestMatchers(HttpMethod.GET, "/api/employees").hasRole("employee")
                        .requestMatchers(HttpMethod.GET, "/api/employees/**").hasRole("employee")
                        .requestMatchers(HttpMethod.POST, "/api/employees").hasRole("manager")
                        .requestMatchers(HttpMethod.PATCH, "/api/employees").hasRole("manager")
                        .requestMatchers(HttpMethod.PUT, "/api/employees").hasRole("manager")
                        .requestMatchers(HttpMethod.PUT, "/api/employees/**").hasRole("manager")
                        .requestMatchers(HttpMethod.DELETE, "/api/employees/**").hasRole("admin")
        );

        // use http basic auth
        httpSecurity.httpBasic(Customizer.withDefaults());

        // disable CSRF
        // in general, not required for stateless REST APIs
        httpSecurity.csrf(AbstractHttpConfigurer::disable);

        return httpSecurity.build();
    }

效果如下:

roleCRUD结果
employeedelete在这里插入图片描述
employeecreate在这里插入图片描述
employeeget在这里插入图片描述
bosscreate在这里插入图片描述
bossdelete在这里插入图片描述
admincreate在这里插入图片描述
admindelete在这里插入图片描述

使用 JDBC 链接

这里使用数据库代替硬代码去实现

明文密码

这个 demo 使用明文密码进行实现,这样比较直观

sql 配置
USE `employee_directory`;

 DROP TABLE IF EXISTS `authorities`;
 DROP TABLE IF EXISTS `users`;

 --
 -- Table structure for table `users`
 --

 CREATE TABLE `users` (
 `username` varchar(50) NOT NULL,
 `password` varchar(50) NOT NULL,
 `enabled` tinyint NOT NULL,
 PRIMARY KEY (`username`)
 ) ENGINE=InnoDB DEFAULT CHARSET=latin1;

 --
 -- Inserting data for table `users`
 --

 INSERT INTO `users`
 VALUES
 ('john','{noop}test123',1),
 ('mary','{noop}test123',1),
 ('susan','{noop}test123',1);


 --
 -- Table structure for table `authorities`
 --

 CREATE TABLE `authorities` (
 `username` varchar(50) NOT NULL,
 `authority` varchar(50) NOT NULL,
 UNIQUE KEY `authorities_idx_1` (`username`,`authority`),
 CONSTRAINT `authorities_ibfk_1` FOREIGN KEY (`username`) REFERENCES `users` (`username`)
 ) ENGINE=InnoDB DEFAULT CHARSET=latin1;

 --
 -- Inserting data for table `authorities`
 --

 INSERT INTO `authorities`
 VALUES
 ('john','ROLE_EMPLOYEE'),
 ('mary','ROLE_EMPLOYEE'),
 ('mary','ROLE_MANAGER'),
 ('susan','ROLE_EMPLOYEE'),
 ('susan','ROLE_MANAGER'),
 ('susan','ROLE_ADMIN');

这里的表名是 spring security 默认的名称,后面会说怎么配置,从而可以不用默认的表名。另外就是 {noop}test123,这个语法表示 no-op,即不对用户名加密。结果如下:

mysql> show tables from employee_directory;
+------------------------------+
| Tables_in_employee_directory |
+------------------------------+
| authorities                  |
| employee                     |
| users                        |
+------------------------------+
3 rows in set (0.00 sec)

mysql> select * from employee_directory.authorities;
+----------+---------------+
| username | authority     |
+----------+---------------+
| john     | ROLE_EMPLOYEE |
| mary     | ROLE_EMPLOYEE |
| mary     | ROLE_MANAGER  |
| susan    | ROLE_ADMIN    |
| susan    | ROLE_EMPLOYEE |
| susan    | ROLE_MANAGER  |
+----------+---------------+
6 rows in set (0.00 sec)

mysql> select * from employee_directory.users;;
+----------+---------------+---------+
| username | password      | enabled |
+----------+---------------+---------+
| john     | {noop}test123 |       1 |
| mary     | {noop}test123 |       1 |
| susan    | {noop}test123 |       1 |
+----------+---------------+---------+
3 rows in set (0.00 sec)

后面会有 dbeaver 而不是命令行显示数据,这里是 dbeaver 生成的 ER Diagram:

在这里插入图片描述

修改 POM 和配置文件
	<dependency>
		<groupId>com.mysql</groupId>
		<artifactId>mysql-connector-j</artifactId>
		<scope>runtime</scope>
	</dependency>

这个是添加对 mysql 的支持,下面是 properties 文件的修改:

# JDBC properties
spring.datasource.url=jdbc:mysql://localhost:3306/employee_directory
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
修改 java 代码

这个只需要修改 userDetailsManager 里的 dataSource 即可,这部分 srping 也会自动进行依赖注入:

    // add support for JDBC
    @Bean
    public UserDetailsManager userDetailsManager(DataSource dataSource) {
        return new JdbcUserDetailsManager((dataSource));
    }

最终效果如下:

在这里插入图片描述

⚠️:employee, admin, manager 在数据库里是大写的,之前实现是小写,所以需要修改

bcrypt 加密

bcrypt 是一个单方向的加密方式,无法通过已经 hash 的值去解密,这也是 spring security 支持的密码加密方式。

这里网上随便找了一个 bcrypt 加密的网站显示一下 string 对比的结果:

在这里插入图片描述

⚠️:就算是同一个 string,每次加密后获得的 hash 值都不会完全一致

sql 配置
USE `employee_directory`;

DROP TABLE IF EXISTS `authorities`;
DROP TABLE IF EXISTS `users`;

--
-- Table structure for table `users`
--

CREATE TABLE `users` (
  `username` varchar(50) NOT NULL,
  `password` char(68) NOT NULL,
  `enabled` tinyint NOT NULL,
  PRIMARY KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

--
-- Inserting data for table `users`
--
-- NOTE: The passwords are encrypted using BCrypt
--

INSERT INTO `users`
VALUES
('john','{bcrypt}$2y$08$I9qqnyR8fMLO/WFQWLjzfe7TCz6357dM/CaXgppCReDdSMktqUIPW',1),
('mary','{bcrypt}$2y$08$I9qqnyR8fMLO/WFQWLjzfe7TCz6357dM/CaXgppCReDdSMktqUIPW',1),
('susan','{bcrypt}$2y$08$I9qqnyR8fMLO/WFQWLjzfe7TCz6357dM/CaXgppCReDdSMktqUIPW',1);


--
-- Table structure for table `authorities`
--

CREATE TABLE `authorities` (
  `username` varchar(50) NOT NULL,
  `authority` varchar(50) NOT NULL,
  UNIQUE KEY `authorities4_idx_1` (`username`,`authority`),
  CONSTRAINT `authorities4_ibfk_1` FOREIGN KEY (`username`) REFERENCES `users` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

--
-- Inserting data for table `authorities`
--

INSERT INTO `authorities`
VALUES
('john','ROLE_EMPLOYEE'),
('mary','ROLE_EMPLOYEE'),
('mary','ROLE_MANAGER'),
('susan','ROLE_EMPLOYEE'),
('susan','ROLE_MANAGER'),
('susan','ROLE_ADMIN');

查看数据库,确定密码已经从明文更新为 bcrypt:

在这里插入图片描述

⚠️:bcrypt 的密码是 test1234

这个时候运行一下 postman,原本的 123 验证会失败,但是 1234 会通过:

在这里插入图片描述

自定义表名

sql 配置
USE `employee_directory`;

DROP TABLE IF EXISTS `roles`;
DROP TABLE IF EXISTS `members`;

--
-- Table structure for table `members`
--

CREATE TABLE `members` (
  `user_id` varchar(50) NOT NULL,
  `pw` char(68) NOT NULL,
  `active` tinyint NOT NULL,
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

--
-- Inserting data for table `members`
--
-- NOTE: The passwords are encrypted using BCrypt
--

INSERT INTO `members`
VALUES
('john','{bcrypt}$2a$10$qeS0HEh7urweMojsnwNAR.vcXJeXR1UcMRZ2WcGQl9YeuspUdgF.q',1),
('mary','{bcrypt}$2a$10$qeS0HEh7urweMojsnwNAR.vcXJeXR1UcMRZ2WcGQl9YeuspUdgF.q',1),
('susan','{bcrypt}$2a$10$qeS0HEh7urweMojsnwNAR.vcXJeXR1UcMRZ2WcGQl9YeuspUdgF.q',1);


--
-- Table structure for table `authorities`
--

CREATE TABLE `roles` (
  `user_id` varchar(50) NOT NULL,
  `role` varchar(50) NOT NULL,
  UNIQUE KEY `authorities5_idx_1` (`user_id`,`role`),
  CONSTRAINT `authorities5_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `members` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

--
-- Inserting data for table `roles`
--

INSERT INTO `roles`
VALUES
('john','ROLE_EMPLOYEE'),
('mary','ROLE_EMPLOYEE'),
('mary','ROLE_MANAGER'),
('susan','ROLE_EMPLOYEE'),
('susan','ROLE_MANAGER'),
('susan','ROLE_ADMIN');

这里会创建两个新的表去建立关联:

在这里插入图片描述

其中 role 等同于 auth,members 等同于 user

⚠️:这里新修改的密码是 fun123

java 更新

这里更新的地方要让 jdbcUserDetailsManager 能够找到用户和权限的表,就需要新写一下 query,让 spring security 通过新的 query 去找到 user 和 role:

    @Bean
    public UserDetailsManager userDetailsManager(DataSource dataSource) {
        JdbcUserDetailsManager jdbcUserDetailsManager = new JdbcUserDetailsManager((dataSource));

        // define query to retrieve a user by username
        jdbcUserDetailsManager.setUsersByUsernameQuery(
                "select user_id, pw, active from members where user_id=?"
        );

        // define query to retrieve the roles by username
        jdbcUserDetailsManager.setAuthoritiesByUsernameQuery(
                "select user_id, role from roles where user_id=?"
        );

        return jdbcUserDetailsManager;
    }

效果如下:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/554518.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

初识LangChain的快速入门指南

LangChain 概述 LangChain是一个基于大语言模型用于构建端到端语言模型应用的框架&#xff0c;它提供了一系列工具、套件和接口&#xff0c;让开发者使用语言模型来实现各种复杂的任务&#xff0c;如文本到图像的生成、文档问答、聊天机器人等。 LangChain简化了LLM应用程序生…

PostCSS概述和应用

文章目录 PostCSS概述**核心特性与工作原理&#xff1a;****应用场景与优势&#xff1a;****社区与生态&#xff1a;** PostCSS应用实例 PostCSS概述 PostCSS 是一款开源的、用 JavaScript 编写的 CSS 处理工具&#xff0c;其核心设计理念是利用 JavaScript 的强大编程能力和丰…

51-40 Align your Latents,基于LDM的高分辨率视频生成

由于数据工程、仿真测试工程&#xff0c;咱们不得不进入AIGC图片视频生成领域。兜兜转转&#xff0c;这一篇与智驾场景特别密切。23年4月&#xff0c;英伟达Nvidia联合几所大学发布了带文本条件融合、时空注意力的Video Latent Diffusion Models。提出一种基于LDM的高分辨率视…

synchronized的优化策略^o^

synchronized 特点&#xff1a; 开始是乐观锁&#xff0c;如果锁冲突&#xff0c;就转换为悲观锁开始是轻量级锁&#xff0c;如果锁被持有的时间较长&#xff0c;就转换为重量级锁实现轻量级锁的时候大概率用到的是自旋锁策略是一种不公平锁是一种可重入锁不是读写锁 synchro…

【考研数学】《1800》《660》《880》如何选择及搭配?看这一篇!

可以刷880&#xff01;但一定要把心态稳住&#xff01;&#xff01;&#xff01; 我考研的时候刷880前几章还可以&#xff0c;越往后越刷不动 因为很多人在备考前两轮的后期听课和刷题都不如前几章细心...越往后知识点掌握的越来越不熟练&#xff0c;所以也建议大家在前几轮复…

Kafka复习

消息中间件的作用: 异步处理: 与并行相比,虽然减少了时间,但是还是得等待其他线程执行完,但是消息中间件对于简单的业务处理,还要引入一个中间件也比较复杂如果我投递了简历之后需要发送成功邮件以及短信,就可以交给消息中间件就像数据库、redis数据一致性,需要用到延迟…

VScode使用记录

代码颜色是白色 发现没有根据对应的文本类型显示颜色 解决方法&#xff1a; 效果&#xff1a;

「sentinel」流量控制组件的应用

「sentinel」流量控制组件的应用 Sentinel版本QPS 一、初识Sentinel1、Sentinel2、Sentinel 和 Hystrix对比3、雪崩问题 二、环境搭建1、下载安装Sentinel2、微服务整合Sentinel 三、流量控制1、簇点链路2、流控设置3、流控模式直接关联链路 4、流控效果流控效果解释 四、热点限…

线性表的链式存储(循环链表)

文章目录 前言一、循环链表是什么&#xff1f;二、循环链表的操作实现总结 前言 T_T此专栏用于记录数据结构及算法的&#xff08;痛苦&#xff09;学习历程&#xff0c;便于日后复习&#xff08;这种事情不要啊&#xff09;。所用教材为《数据结构 C语言版 第2版》严蔚敏。有关…

Codeforces Round 939 (Div. 2) A~E

A.Nene’s Game&#xff08;循环&#xff09; 题意&#xff1a; 妮妮发明了一种基于递增序列 a 1 , a 2 , … , a k a_1,a_2,\ldots,a_k a1​,a2​,…,ak​的新游戏。 在这个游戏中&#xff0c;最初 n n n个玩家排成一排。在这个游戏的每一轮中&#xff0c;都会发生以下情况…

详解SPI通信协议以及FPGA实现

文章目录 一、SPI简介二、SPI通信结构三、多从机模式四、时钟极性&#xff08;CPOL&#xff09;和时钟相位&#xff08;CPHA&#xff09;五、SPI通信过程六、实现SPI主机发送程序6.1 波形图分析6.2 Verilog代码6.3 发送数据控制模块6.4 仿真代码编写以及仿真结果分析 七、Veril…

Kubernetes Pod的配置管理 ConfigMap和Secret

目录 前言 一、为什么需要配置管理 二、使用ConfigMap管理Pod的配置信息 2.1 创建ConfigMap&#xff08;4种方式&#xff09; 2.1.1 指定ConfigMap的参数创建 2.1.2 指定配置文件创建ConfigMap 2.1.3 通过一个文件内的多个键值对创建ConfigMap 2.1.4 yaml文件创建Config…

Android 性能优化(七):APK安装包体积优化

包体积优化重要性 移动 App 特别关注投放转化率指标&#xff0c;而 App 包体积是影响用户新增的重要因素&#xff0c;而 App 的包体积又是影响投放转化率的重要因素。 Google 2016 年公布的研究报告显示&#xff0c;包体积每上升 6MB 就会带来下载转化率降低 1%&#xff0c; …

114 接口中幂等性的保证

前言 同样是 面试问题 如何确保接口的 幂等性 幂等是一个 较为抽象的概念, 多次重复访问, 不会导致业务逻辑的异常 这里从增删改查, 几个方面列一下 一般来说, 我们核心需要关注的就是 新增 和 更新 对于 增加元素, 首先针对唯一约束进行校验, 然后再处理新增的相关业…

二刷大数据(三)- Flink1.17

目录 Flink概念与SparkStreaming区别分层API 工作流程部署模式**Local Mode****Standalone Mode****YARN Mode****Kubernetes Mode****Application Mode** 运行架构stand alone 核心概念算子链任务槽 窗口窗口**窗口的目的与作用****时间窗口&#xff08;Time Windows&#xff…

vue3中web前端JS动画案例(一)

上述案例主要使用定时器&#xff0c;和绝对定位产生动画 <script setup> import { ref, onMounted, watch } from vue // ----------------------- 01 js 动画介绍--------------------- // 1、匀速运动 // 2、缓动运动&#xff08;常见&#xff09; // 3、透明度运动 //…

抖店底层逻辑,关于5个“为什么”的答案~

我是王路飞。 很多人对抖店的底层逻辑有一种抗拒心态&#xff0c;不想学习和了解。 认为做抖店不就是开店卖货嘛&#xff0c;什么火、什么有热度卖什么就好了&#xff0c;了解那么多“理论知识”有什么用呢&#xff1f; 但往往正是这些基础理论&#xff0c;底层逻辑&#xf…

Linux系统一键安装DataEase结合内网穿透实现公网访问本地WebUI界面

文章目录 前言1. 安装DataEase2. 本地访问测试3. 安装 cpolar内网穿透软件4. 配置DataEase公网访问地址5. 公网远程访问Data Ease6. 固定Data Ease公网地址 前言 DataEase 是开源的数据可视化分析工具&#xff0c;帮助用户快速分析数据并洞察业务趋势&#xff0c;从而实现业务…

PMM2 MySQL监控管理工具

目录 1. PMM介绍 2. 安装PMM服务端 2.1 安装docker 2.1.1 下载docker 2.1.2 上传docker包 2.1.3 启动守护进程 2.1.4 查看docker状态 2.2 安装PMM 2.2.1 下载镜像 2.2.2 load镜像 2.2.3 查看镜像 2.2.4 创建容器 2.2.5 运行镜像 2.2.6 验证PMM服务器 2.2.7 删除…

MySQL行格式(row format)

MySQL行格式&#xff08;row format&#xff09; 表的行格式决定了其行的物理存储方式&#xff0c;这反过来又会影响查询和 DML 操作的性能。随着单个磁盘页面容纳更多行&#xff0c;查询和索引查找可以更快地工作&#xff0c;缓冲池中需要的高速缓存内存更少&#xff0c;写出…