SpringSecurity5.7+最新案例 -- 授权 --

news2024/11/28 0:32:47

一、前提

书接上回 SpringSecurity5.7+最新案例 – 用户名密码+验证码+记住我······

本文 继续处理SpringSecurity授权 …

目前由 难 -> 简,即自定义数据库授权,注解授权,config配置授权

二、自定义授权

0. 数据准备

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `age` int(11) NULL DEFAULT NULL,
  `address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `phone` varchar(13) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `passwd` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `pwd` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `role` json NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user
-- ----------------------------
BEGIN;
INSERT INTO `user` VALUES (1, 'a', 35, '太原', '11', '$2a$10$tbFi2xQwHQcjIp4jt5eGZ..CER7ws.Pzg9RiBM1tTGH1omkNprXqC', '123', '[{\"name\": \"系统管理员\", \"role\": \"ROLE_ADMIN\"}, {\"name\": \"普通用户\", \"role\": \"ROLE_USER\"}]');
INSERT INTO `user` VALUES (2, 'b', 51, '沧州', '22', '$2a$10$c8.tVJ36cqcTHV/lGugyc.uf3ft9nQ4jNJpqhhWESpAv7uOspDg1K', '456', '[{\"name\": \"普通用户\", \"role\": \"ROLE_USER\"}]');
INSERT INTO `user` VALUES (3, 'c', 43, '兖州', '33', '$2a$10$akbjbzIjdGjy3/4sUvizae11LvEZXxLUPO0n.4x1NfPupCJYI4aUm', '789', '[{\"name\": \"游客\", \"role\": \"ROLE_GUEST\"}]');
COMMIT;



-- ----------------------------
-- Table structure for menu
-- ----------------------------
DROP TABLE IF EXISTS `menu`;
CREATE TABLE `menu`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `pattern` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `role` json NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of menu
-- ----------------------------
BEGIN;
INSERT INTO `menu` VALUES (1, '/admin/**', '[{\"name\": \"系统管理员\", \"role\": \"ROLE_ADMIN\"}]');
INSERT INTO `menu` VALUES (2, '/user/**', '[{\"name\": \"普通用户\", \"role\": \"ROLE_USER\"}]');
INSERT INTO `menu` VALUES (3, '/guest/**', '[{\"name\": \"普通用户\", \"role\": \"ROLE_USER\"}, {\"name\": \"游客\", \"role\": \"ROLE_GUEST\"}]');
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;

1. 说明

​ 由于使用的是jpa,以及在mysql中使用到了json数据,所以先在项目中引入json支持, 使用 mybatis 的绕行

<dependency>
    <groupId>com.vladmihalcea</groupId>
    <artifactId>hibernate-types-52</artifactId>
    <version>2.3.5</version>
</dependency>

2. UserDstail变化

image-20230807174353840

3. SecurityConfig中配置变化如下

image-20230807173743413

4. 实现 FilterInvocationSecurityMetadataSource

@Component
public class MySecurityMetadataSource implements FilterInvocationSecurityMetadataSource {

    // 这里注入的查询menu的接口类
    final MenuRepository menuRepository;

    public MySecurityMetadataSource(MenuRepository menuRepository) {
        this.menuRepository = menuRepository;
    }

    AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        String requestURI = ((FilterInvocation) object).getRequest().getRequestURI();
        // 获取无需权限就能访问的公共资源, 不需权限的访问,直接返回,不需要在进入数据量中查询
        String[] pubSource = PublicRequestSource.get();
        for (String source : pubSource) {
            if (antPathMatcher.match(source, requestURI)) {
                return null;
            }
        }
        // 如果要访问的资源不在公共资源里面,则去数据库中查询,然后创建访问权限
        List<MenuEntity> allMenu = menuRepository.findAll();
        for (MenuEntity menu : allMenu) {
            if (antPathMatcher.match(menu.getPattern(), requestURI)) {
                List<RoleEntity> role = menu.getRole();
                String[] roles = role.stream().map(RoleEntity::getRole).toArray(String[]::new);
                return SecurityConfig.createList(roles);
            }
        }
        return null;
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return FilterInvocation.class.isAssignableFrom(clazz);
    }
}

5. 配置公共资源

​ – 也可以写在配置文件中

public class PublicRequestSource {

    public static String[] get() {
        return new String[]{
                "/hello", "/login"
        };
    }

}

6. 实现ObjectPostProcessor

@Component
public class MyObjectPostProcessor implements ObjectPostProcessor<FilterSecurityInterceptor> {

    @Resource
    private MySecurityMetadataSource mySecurityMetadataSource;

    @Override
    public <O extends FilterSecurityInterceptor> O postProcess(O object) {
        object.setSecurityMetadataSource(mySecurityMetadataSource);
        // 打开公共资源访问权限
        object.setRejectPublicInvocations(false);
        return object;
    }

}

7. 创建TestController测试请求接口权限

// 实际这里会写在多个controller类下,这里为了测试
@RestController
public class TestController {
    @GetMapping("/admin/hello")
    public String admin() {
        return "hello admin";
    }

    @GetMapping("/user/hello")
    public String user() {
        return "hello user";
    }

    @GetMapping("/guest/hello")
    public String guest() {
        return "hello guest";
    }

    @GetMapping("/hello")
    public String hello() {
        return "hello";
    }

}

8. 然后用postman登录测试接口把

image-20230807174918920

image-20230807174954184

嗯,其它的就不测试了…

三、注解授权

1. 开启全局授权

@SpringBootApplication
// 开启全局授权(详细解释如下)
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class SplitBackWeb03Application {
    public static void main(String[] args) {
        SpringApplication.run(SplitBackWeb03Application.class);
    }
}
  • perPostEnabled: 开启 Spring Security 提供的四个权限注解,@PostAuthorize、@PostFilter、@PreAuthorize 以及@PreFilter。
  • securedEnabled: 开启 Spring Security 提供的 @Secured 注解支持,该注解不支持权限表达式
  • jsr250Enabled: 开启 JSR-250 提供的注解,主要是@DenyAll、@PermitAll、@RolesAll 同样这些注解也不支持权限表达式
# 以上注解含义如下:
- @PostAuthorize: 在目前标方法执行之后进行权限校验。
- @PostFiter: 在目标方法执行之后对方法的返回结果进行过滤。
- @PreAuthorize:在目标方法执行之前进行权限校验。
- @PreFiter:在目前标方法执行之前对方法参数进行过滤。
- @Secured:访问目标方法必须具各相应的角色。
- @DenyAll:拒绝所有访问。
- @PermitAll:允许所有访问。
- @RolesAllowed:访问目标方法必须具备相应的角色。

这些基于方法的权限管理相关的注解,一般来说只要设置 prePostEnabled=true 就够用了。

2. 注意事项

当开启了开启全局授权后,无需额外配置(即SecurityConfig最简如下),当然,也可以和自定义授权一起使用(但不推荐)

httpSecurity.formLogin().and().csrf().disable();

3. 创建测试controller类

@RestController
@RequestMapping("/hello")
public class AuthorizeMethodController {

    // 当角色为 ADMIN 并且 认证的用户名为“a”时,才允许访问,否则403
    @PreAuthorize("hasRole('ADMIN') and authentication.name=='a'")
    @GetMapping
    public String hello() {
        return "hello";
    }

    // 当认证的用户名 和 参数中传递的用户名 一致 时,才允许访问,否则403
    @PreAuthorize("authentication.name==#name")
    @GetMapping("/name")
    public String hello(String name) {
        return "hello:" + name;
    }

    // 过滤作用,如传递的UserList id为1,2,3的对象,则后台实际收到的是id为1,3的对象的数据
    // filterTarget 必须是数组 集合 类型
    @PreFilter(value = "filterObject.id%2!=0",filterTarget = "users")
    @PostMapping("/users")
    public void addUsers(@RequestBody List<User> users) {
        System.out.println("users = " + users);
    }

    // 认证作用,当参数中的id=1的时候,可以返回相应的数据,否则403
    @PostAuthorize("returnObject.id==1")
    @GetMapping("/userId")
    public User getUserById(Integer id) {
        return new User(id, "mose");
    }

    // 过滤作用,如结果集中的UserList id为1,2,3的对象,则返回实际的对象只有id 为 2 的对象
    // 返回数据 最好是 数组 或者集合,不然无效
    @PostFilter("filterObject.id%2==0")
    @GetMapping("/lists")
    public List<User> getAll() {
        return userService.findAll();
    }

    // 只能判断角色,当角色为 ROLE_USER 可访问
    @Secured({"ROLE_USER"}) 
    @GetMapping("/secured")
    public User getUserByUsername() {
        return new User(99, "secured");
    }

    // 只能判断角色,当角色具有其中一个即可
    @Secured({"ROLE_ADMIN","ROLE_USER"}) 
    @GetMapping("/username")
    public User getUserByUsername2(String username) {
        return new User(99, username);
    }

    // 允许所有
    @PermitAll
    @GetMapping("/permitAll")
    public String permitAll() {
        return "PermitAll";
    }

    // 拒绝所有
    @DenyAll
    @GetMapping("/denyAll")
    public String denyAll() {
        return "DenyAll";
    }

    // 角色判断,当具有其中一个角色即可
    @RolesAllowed({"ROLE_ADMIN","ROLE_USER"}) 
    @GetMapping("/rolesAllowed")
    public String rolesAllowed() {
        return "RolesAllowed";
    }
}

四、配置授权

1. 在securityConfig中配置即可,如下

httpSecurity.authorizeHttpRequests()
        .antMatchers("/admin/**").hasRole("ADMIN")
        .antMatchers("/user/**").hasAnyRole("USER", "ADMIN")
        .antMatchers("/guest").hasRole("GUEST")
        .anyRequest().authenticated()
        .and().formLogin()
        .and().csrf().disable();

2. 解释

方法说明
hasAuthority(String authority)当前用户是否具备指定权限
hasAnyAuthority(String… authorities)当前用户是否具备指定权限中任意一个
hasRole(String role)当前用户是否具备指定角色
hasAnyRole(String… roles);当前用户是否具备指定角色中任意一个
permitAll();放行所有请求/调用
denyAll();拒绝所有请求/调用
isAnonymous();当前用户是否是一个匿名用户
isAuthenticated();当前用户是否已经认证成功
isRememberMe();当前用户是否通过 Remember-Me 自动登录
isFullyAuthenticated();当前用户是否既不是匿名用户又不是通过 Remember-Me 自动登录的
hasPermission(Object targetId, Object permission);当前用户是否具备指定目标的指定权限信息
hasPermission(Object targetId, String targetType, Object permission);当前用户是否具备指定目标的指定权限信息

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

FFmpeg将编码后数据保存成mp4

以下测试代码实现的功能是&#xff1a;持续从内存块中获取原始数据&#xff0c;然后依次进行解码、编码、最后保存成mp4视频文件。 可保存成单个视频文件&#xff0c;也可指定每个视频文件的总帧数&#xff0c;保存多个视频文件。 为了便于查看和修改&#xff0c;这里将可独立的…

Redis追本溯源(四)集群:主从模式、哨兵模式、cluster模式

文章目录 一、主从模式1.主从复制——全量复制2.主从复制——增量复制 二、哨兵模式1.实时监控与故障转移2.Sentinel选举领导者 三、cluster模式1.三种分片方案2.cluster模式 Redis 有多种集群搭建方式&#xff0c;比如&#xff0c;主从模式、哨兵模式、Cluster 模式。 一、主…

一些不错的VSCode设置和插件

设置 同步设置 我们做的各项设置&#xff0c;不希望再到其他机器的时候还得再重新配置一次。VSCode中我们可以登陆微软账号或者GitHub账号&#xff0c;登陆后我们可以开启同步设置。开启设置同步&#xff0c;根据提示登陆即可。 允许侧边栏水平滑动 在目录层次较深或者文件…

Leetcode-每日一题【剑指 Offer 07. 重建二叉树】

题目 输入某二叉树的前序遍历和中序遍历的结果&#xff0c;请构建该二叉树并返回其根节点。 假设输入的前序遍历和中序遍历的结果中都不含重复的数字。 示例 1: Input: preorder [3,9,20,15,7], inorder [9,3,15,20,7]Output: [3,9,20,null,null,15,7] 示例 2: Input: preo…

【Python小demo】JOJO替身面板生成

文章目录 1 前言2 代码实现3 总结 1 前言 最近暑假在数模研赛备赛&#xff0c;情理之中地接触了一些图表。也是第一次才知道雷达图这么个图表形式&#xff0c;用于直观地评价多个变量或指标。看到雷达图&#xff0c;我不禁联想到了JOJO中的替身面板&#xff0c;于是兴起想用Py…

.NET 应用程序 部署

**硬件支持型号 点击 查看 硬件支持 详情** DTU701 产品详情 DTU702 产品详情 DTU801 产品详情 DTU802 产品详情 DTU902 产品详情 G5501 产品详情 本文内容 在设备上部署 dotnet应用&#xff0c;与任何其他平台的部署相同&#xff0c;可以2种方式&#xff1a; 依赖于框…

Zebec Protocol ,不止于 Web3 世界的 “Paypal”

Paypal 是传统支付领域的巨头企业&#xff0c;在北美支付市场占有率约为 77% 以上。从具体的业务数据看&#xff0c;在8月初&#xff0c;Paypal 公布的 2023 年第二季度财报显示&#xff0c;PayPal 第二季度净营收为 73 亿美元&#xff0c;净利润为 10.29 亿美元。虽然 Paypal …

【C# 基础精讲】运算符和表达式

在C#编程中&#xff0c;运算符和表达式是构建复杂逻辑的关键元素。运算符用于执行各种数学、逻辑和其他操作&#xff0c;而表达式则由运算符、变量、常量和函数组成&#xff0c;用于生成计算结果。本文将详细介绍C#中常见的运算符和表达式的概念&#xff0c;以及它们在程序中的…

全面讲解最小二乘法

常见的最小二乘法我们就不多说了&#xff0c;下面主要介绍一下最小二乘法的一些先进方法。 正则化的最小二乘法 在使用常见的最小二乘法进行回归分析时&#xff0c;常常会遇到过拟合的问题&#xff0c;也就是在训练数据集上表现的很好&#xff0c;但是在测试数据集上表现的很…

Flutter 让软键盘不再自动弹起

1、问题说明&#xff1a; 在开发中&#xff0c;经常遇到这种事&#xff0c;一个页面有输入框&#xff0c;点击输入框后&#xff0c;会弹起软键盘&#xff0c;同时输入框会聚焦&#xff0c;手动收起软键盘后&#xff0c;点击另一个按钮前往下一个页面或者显示一个弹窗&#xff0…

heima头条项目学习--Day1: 环境搭建、SpringCloud微服务(注册发现、网关)

Nacos注册发现、网关 a. 项目介绍b. app登录1) 需求分析2) 表结构分析3) 手动加密&#xff08;md5随机字符串&#xff09;4) 用户端微服务搭建5) 功能实现6) app网关7) 网关校验jwt8) 前端集成, 配置nginx a. 项目介绍 业务说明 技术栈说明 [外链图片转存失败,源站可能有防盗…

Nginx的优化和防盗链(面试高频!!!)

Nginx的优化和防盗链 全篇高能&#xff01;&#xff01;&#xff01;&#xff01;干货较多&#xff01;&#xff01;&#xff01;&#xff01;本篇含面试高频题&#xff1a; 修改配置文件时&#xff0c;先备份&#xff01;&#xff01;&#xff01;以便回滚&#xff01;&…

API接口:企业信息核验

企业信息核验是现代企业管理中必不可少的一项业务&#xff0c;它可以帮助企业做出正确的决策。在这篇文章里&#xff0c;我们将会介绍如何使用API接口来对企业信息进行核验&#xff0c;并实现快捷、准确的查询。 一、API接口 在这里我们使用的是挖数据提供的企业信息核验API接…

医学影像PACS临床信息系统源码

医学影像临床信息系统&#xff08;Picture Archiving and Communication Systems&#xff09;PACS是指从医疗影像设备中获得数字影像&#xff0c;利用高速网络进行存储、管理、传输的医疗影像信息管理系统。通过该系统&#xff0c;能实现影像数字化、无胶片化管理。 登记系统 …

MyCat水平分表

1.水平拆分案例场景 2.MyCat配置 这个表只是在 schema.xml配置的逻辑表&#xff0c;在具体的数据库里面是没有的 根据id的模确定数据存在哪个节点上&#xff01;&#xff01;

8.7 作业 c高级

1.写一个函数&#xff0c;获取用户的uid和gid并使用变量接收&#xff1a; #!/bin/bash function fun() {uidid -u ubuntugidid -g ubuntu }funecho "UID: $uid" echo "GID: $gid"思维导图&#xff1a;

(树) 剑指 Offer 54. 二叉搜索树的第k大节点 ——【Leetcode每日一题】

❓剑指 Offer 54. 二叉搜索树的第k大节点 难度&#xff1a;简单 给定一棵二叉搜索树&#xff0c;请找出其中第 k 大的节点的值。 示例 1: 输入: root [3,1,4,null,2], k 13/ \1 4\2 输出: 4示例 2: 输入: root [5,3,6,2,4,null,null,1], k 35/ \3 6/ \2 4/1 输出…

【多音音频测试信号】具有指定采样率和样本数的多音信号,生成多音信号的相位降低波峰因数研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Jmeter响应中的乱码问题

文章目录 问题描述解决办法 问题描述 Jmeter在访问接口的时候&#xff0c;响应内容如果有中文可能会显示乱码 响应页面没有做编码处理&#xff0c;JMeter默认按照ISO-8859-1编码格式进行解析 解决办法 在线程组中添加BeanShell PostProcessor后置处理器 prev.setDataEnco…

爬虫009_字符串高级_替换_去空格_分割_取长度_统计字符_间隔插入---python工作笔记028

然后再来看字符串的高级操作 取长度 查找字符串下标位置 判断是否以某个字符,开头结尾 计算字符出现次数 替换