【JavaWeb】你这么厉害,知道RBAC权限模型和ABAC权限模型吗?

news2024/11/20 1:29:14

文章目录

  • 一.RBAC 权限模型
    • 1.RBAC的组成
    • 2.RBAC模型分类
      • 2.1.基本模型RBAC0
      • 2.2.角色分层模型RBAC1
      • 2.3.角色限制模型RBAC2
      • 2.4.统一模型RBAC3
    • 3.RBAC0模型核心表结构
      • 3.1.表结构设计
      • 3.2.模块划分
    • 4.基于RBAC的延展—用户组
  • 二.ABAC权限模型(基于Java讲解)
    • 1.ABAC是什么
    • 2.ABAC的条件判断控制
    • 3.表达式语言(EL)
    • 4.ABAC实践
      • 4.1.数据库设计
      • 4.2.引入依赖
      • 4.3.修改配置
      • 4.4.CRUD代码
      • 4.5.security上下文
      • 4.6.启动类
      • 4.7.测试类
    • 5.Spring Security 和 Apache Shiro整合

权限在日常系统中算是一个比较常见的基本功能,对于存在有权限模块的系统中规定了登录用户能够操作哪些资源,不能够操作哪些资源。借助权限模块可以有效的控制系统不同身份人员要具体做的操作,可以说一个成熟的后端系统离不开一个比较完善的权限管理系统。

所以权限控制系统的目标就是:管理用户行为,保护系统功能。

那么如何进行权限控制呢?

  • 定义资源
  • 创建权限
  • 创建角色
  • 管理用户
  • 建立关联关系

一.RBAC 权限模型

1.RBAC的组成

  • 在RBAC模型里面,有3个基础组成部分,分别是:用户、角色和权限
    在这里插入图片描述
  • User(用户):每个用户都有唯一的UID识别,并被授予不同的角色
  • Role(角色):不同角色具有不同的权限
  • Permission(权限):访问权限
  • 用户-角色映射:用户和角色之间的映射关系
  • 角色-权限映射:角色和权限之间的映射

    角色权限关系

    • 权限→资源:单向多对多 一个权限可以包含多个资源,一个资源可以被分配给多个不同权限
    • 角色→权限:单向多对多 一个角色可以包含多个权限,一个权限可以被分配给多个不同角色
    • 用户→角色:双向多对多 一个角色可以包含多个用户,一个用户可以身兼数职

如管理员和普通用户被授予不同的权限,普通用户只能去修改和查看个人信息,而不能创建用户和冻结用户,而管理员由于被授予所有权限,所以可以做所有操作。
在这里插入图片描述

2.RBAC模型分类

2.1.基本模型RBAC0

RBAC0是基础,很多系统只需基于RBAC0就可以搭建权限模型了。

  • 在这个模型中,我们把权限赋予角色,再把角色赋予用户。用户和角色,角色和权限都是多对多的关系。用户拥有的权限等于他所有的角色持有权限之和。
    在这里插入图片描述

举个栗子:

  • 譬如我们做一款企业管理产品,可以抽象出几个角色,譬如销售经理、财务经理、市场经理等,然后把权限分配给这些角色,再把角色赋予用户。这样无论是分配权限还是以后的修改权限,只需要修改用户和角色的关系,或角色和权限的关系即可,更加灵活方便。
  • 此外,如果一个用户有多个角色,老王既负责销售部也负责市场部,那么可以给王先生赋予2个角色,即销售经理、市场经理,这样他就拥有这两个角色的所有权限

2.2.角色分层模型RBAC1

RBAC1建立在RBAC0基础之上,在角色中引入了 "继承" 的概念。简单理解就是,给角色可以分成几个等级,每个等级权限不同,从而实现更细粒度的权限管理
在这里插入图片描述
举个栗子:

  • 基于之前RBAC0的例子,我们又发现一个公司的销售经理可能是分几个等级的,譬如除了销售经理,还有销售副经理,而销售副经理只有销售经理的部分权限。这时候,我们就可以采用RBAC1的分级模型,把销售经理这个角色分成多个等级,给销售副经理赋予较低的等级即可。

2.3.角色限制模型RBAC2

RBAC2同样建立在RBAC0基础之上,仅是对用户、角色和权限三者之间增加了一些限制。

  • 这些限制可以分成两类,即静态职责分离SSD(Static Separation of Duty)和动态职责分离DSD(Dynamic Separation of Duty)。具体限制如下图:
    在这里插入图片描述

举个栗子:

  • 还是基于之前RBAC0的例子,我们又发现有些角色之间是需要互斥
    • 譬如给一个用户分配了销售经理的角色,就不能给他再赋予财务经理的角色了,否则他即可以录入合同又能自己审核合同
      • 再譬如,有些公司对角色的升级十分看重,一个销售员要想升级到销售经理,必须先升级到销售主管,这时候就要采用先决条件限制了。

2.4.统一模型RBAC3

RBAC3是RBAC1和RBAC2的合集,所以RBAC3既有**角色分层,也包括可以增加各种限制。**
在这里插入图片描述

3.RBAC0模型核心表结构

3.1.表结构设计

  • 从上面实体对应关系分析,权限表设计分为以下基本的5张表结构:用户表(t_user),角色表(t_role),t_user_role(用户角色表),资源表(t_module),权限表(t_permission),表结构关系如下:
    在这里插入图片描述
t_user	用户表			
	主键	id	int(11)	自增	主键id
	user_name	varchar(20)	非空	用户名
	user_pwd	varchar(100)	非空	用户密码
	true_name	varchar(20)	可空	真实姓名
	email	varchar(30)	可空	邮箱
	phone	varchar(20)	可空	电话
	is_valid	int(4)	可空	有效状态
	create_date	datetime	可空	创建时间
	update_date	datetime	可空	更新时间

t_role	角色表			
	主键	id	int(11)	自增	主键id
	role_name	varchar(20)	非空	角色名
	role_remarker	varchar(100)	可空	角色备注
	is_valid	int(4)	可空	有效状态
	create_date	datetime	可空	创建时间

t_user_role	用户角色表			
	主键	id	int(11)	自增	主键id
	user_id	int(4)	非空	用户id
	role_id	int(4)	角色id	角色id
	create_date	datetime	可空	创建时间
	update_date	datetime	可空	更新时间

t_module	资源表			
	主键	id	int(11)	自增	资源id
	module_name	varchar(20)	可空	资源名
	module_style	varchar(100)	可空	资源样式
	url	varchar(20)	可空	资源url地址
	parent_id	int(11)	非空	上级资源id
	parent_opt_value	varchar(20)	非空	上级资源权限码
	grade	int(11)	非空	层级
	opt_value	varchar(30)	可空	权限码
	orders	int(11)	非空	排序号
	is_valid	int(4)	可空	有效状态
	create_date	datetime	可空	创建时间
	update_date	datetime	可空	更新时间
	
t_permission	权限表			
	主键	id	int(11)	自增	主键id
	role_id	int(11)	非空	角色id
	module_id	int(11)	非空	资源id
	acl_value	varchar(20)	非空	权限码
	create_date	datetime	可空	创建时间
	update_date	datetime	可空	更新时间

3.2.模块划分

从表结构设计可以看出:这里有三张主表(t_user,t_role,t_module),功能实现上这里划分为三大模块:

用户管理

  • 用户基本信息维护
  • 用户角色分配

角色管理

  • 角色基本信息维护
  • 角色授权(给角色分配能够操作的菜单)
  • 角色认证(给角色拥有的权限进行校验)

资源管理

  • 资源信息维护
  • 菜单输出动态控制

4.基于RBAC的延展—用户组

  • 基于RBAC模型,还可以适当延展,使其更适合我们的产品。譬如增加用户组概念,直接给用户组分配角色,再把用户加入用户组。这样用户除了拥有自身的权限外,还拥有了所属用户组的所有权限。
    在这里插入图片描述
    举个栗子:

  • 譬如,我们可以把一个部门看成一个用户组,如销售部,财务部,再给这个部门直接赋予角色,使部门拥有部门权限,这样这个部门的所有用户都有了部门权限。用户组概念可以更方便的给群体用户授权,且不影响用户本来就拥有的角色权限`。

二.ABAC权限模型(基于Java讲解)

1.ABAC是什么

ABAC(Attribute Base Access Control)

  • 基于 属性的权限控制不同于常见的将用户通过某种方式关联到权限的方式,ABAC则是通过动态计算一个或一组属性来是否满足某种条件来进行授权判断(可以编写简单的逻辑)。
  • 属性通常来说分为四类:用户属性(如用户年龄)环境属性(如当前时间)操作属性(如读取)和对象属性,所以理论上能够实现非常灵活的权限控制,几乎能满足所有类型的需求。

2.ABAC的条件判断控制

基于ABAC访问控制需要动态计算实体的属性、操作类型、相关的环境来控制是否有对操作对象的权限,所以在设计的时候需要考虑的条件判断的灵活性、通用性、易用性,用户只需要通过web页面即可配置授权,这需要减少硬编码似得逻辑变得简单通用,那么这需要满足一些运算符来实现。

类型运算符
算术运算符+, -, *, /, %, ^, div, mod
关系运算符<, >, ==, !=, <=, >=, lt, gt, eq, ne, le, ge
逻辑运算符and, or, not, &&,
条件?:
  • 举个栗子: 用户只需要配置 user.age > 20 的条件即可获得特定的权限。

3.表达式语言(EL)

正如上一节所说的需要对某种条件进行解析那么就需要表达式语言,使用表达式语言可以方便的访问对象中的属性、或是进行各种数学运算,条件判断等

正如:Spring Framework的@Value注解和MyBatis的<if test=“”>

// 相信很多 Java boy都使用过的吧
@Value("A?B:C")
private String A;
<select id = "XXX">
    <if test="user != null">
        XXXX
    </if>
</select

此ABAC的的核心就是Expression Language(EL),虽然演示是Java使用作为演示,但是其他的编程语言都是有着自己的EL框架的。

java EL框架列表

  • spring-expression

  • OGNL

  • MVEL

  • JBoss EL

感兴趣可以查看 Java EL生态排名:https://mvnrepository.com/open-source/expression-languages

4.ABAC实践

4.1.数据库设计

在这里插入图片描述
在这里插入图片描述

# 用户表
DROP TABLE IF EXISTS USER;
CREATE TABLE USER (
    id BIGINT(20) NOT NULL COMMENT '主键ID',
    NAME VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
    age INT(11) NULL DEFAULT NULL COMMENT '年龄',
    email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
    PRIMARY KEY (id) COMMENT '用户表'
);
# 用户边缘数据
DROP TABLE IF EXISTS user_contribution;
CREATE TABLE user_contribution (
    id BIGINT(20) NOT NULL COMMENT '主键ID',
    user_id BIGINT(20)  NOT NULL COMMENT '用户表ID',
    repository VARCHAR(100) NOT NULL COMMENT '仓库',
    PRIMARY KEY (id) COMMENT '用户边缘数据'
);
# 权限表
DROP TABLE IF EXISTS permission;
CREATE TABLE permission (
    id BIGINT(20) NOT NULL COMMENT '主键ID',
    permission VARCHAR(100) NOT NULL COMMENT '权限名称',
    PRIMARY KEY (id) COMMENT '权限表'
);
# abac表达式表
DROP TABLE IF EXISTS abac;
CREATE TABLE abac (
    id BIGINT(20) NOT NULL COMMENT '主键ID',
    expression VARCHAR(100) NOT NULL COMMENT 'abac表达式',
    PRIMARY KEY (id) COMMENT 'abac表达式表'
);
# abac表和权限表的关联表, o2m
DROP TABLE IF EXISTS abac_permission;
CREATE TABLE abac_permission (
    id BIGINT(20) NOT NULL COMMENT '主键ID',
    abac_id BIGINT(20) NOT NULL COMMENT 'abac表ID',
    permission_id BIGINT(20) NOT NULL COMMENT 'permission表ID',
    PRIMARY KEY (id) COMMENT 'abac表和权限表的关联表, o2m'
);


#插入用户数据
DELETE FROM USER;
INSERT INTO USER (id, NAME, age, email)
VALUES (1, '魏昌进', 26, 'mail@wcj.plus'),
       (2, 'test', 1, 'mail1@wcj.plus'),
       (3, 'admin', 1, 'mail2@wcj.plus');
 
 #插入用户边缘数据
DELETE FROM user_contribution;
INSERT INTO user_contribution (id, user_id, repository)
VALUES (1, 1, 'galaxy-sea/spring-cloud-apisix'),
       (2, 2, 'spring-cloud/spring-cloud-commons'),
       (3, 2, 'spring-cloud/spring-cloud-openfeign'),
       (4, 2, 'alibaba/spring-cloud-alibaba'),
       (5, 2, 'Tencent/spring-cloud-tencent'),
       (6, 2, 'apache/apisix-docker');
       
#插入权限数据
DELETE FROM permission;
INSERT INTO permission (id, permission)
VALUES (1, 'github:pr:merge'),
       (2, 'github:pr:close'),
       (3, 'github:pr:open'),
       (4, 'github:pr:comment');
 
#插入abac表达式数据 
DELETE FROM abac;
INSERT INTO abac (id, expression)
VALUES (1, 'contributions.contains(''galaxy-sea/spring-cloud-apisix'')'),
       (2, 'name == ''admin'''),
       (3, 'metadata.get(''ip'') == ''192.168.0.1''');
 
#插入abac表达式-权限映射关系数据  
DELETE FROM abac_permission;
INSERT INTO abac_permission (id, abac_id, permission_id)
VALUES (1, 1, 1),
       (2, 2, 1),
       (3, 2, 2),
       (4, 2, 3),
       (5, 2, 4),
       (6, 3, 1),
       (7, 3, 2),
       (8, 3, 3),
       (9, 3, 4);

4.2.引入依赖

本章仅实现ABAC的原理,不会对Spring Security和 Apache Shiro做任何的集成

框架

  • Java 8
  • Spring Boot 2.x
  • MyBatis Plus 3.5.x
  • MySQL 8.0
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>abac</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-abac</name>
    <description>springboot-abac</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>

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

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

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

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.2</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>compile</scope>
        </dependency>

        <!-- security权限依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

4.3.修改配置

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      password: root
      username: root
      driver-class-name: com.mysql.cj.jdbc.Driver
      #数据库名字自己定义
      url: jdbc:mysql://127.0.0.1:3306/abac_test?serverTimezone=UTC&useUnicode=true&charaterEncoding=utf-8&useSSL=false
      validation-query: SELECT 1


mybatis-plus:
  #配置 Maaper xml文件所在路径
  mapper-locations: classpath*:/mapper/**/*.xml
  #配置映射类所在的包名
  type-aliases-package: com.example.entity

server:
  port: 8090

4.4.CRUD代码

entiy

@Data
public class Abac {
    private Long id;
    private String expression;
    /**
     * expression对应的permissions列表
     */
    @TableField(exist = false)
    private List<String> permissions;
}
@Data
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;

    /** 用户提交过仓库 */
    @TableField(exist = false)
    private List<String> contributions = new ArrayList<>();

    /** 存放一些乱七八糟的数据,当然contributions字段也放在这里 */
    @TableField(exist = false)
    private Map<String, Object> metadata = new HashMap<>();
}

dao层

@Mapper
public interface AbacDao extends BaseMapper<Abac> {
    /** 获取abacId关联权限 */
    @Select("SELECT p.permission\n" +
            "FROM abac_permission ap LEFT JOIN permission p ON p.id = ap.permission_id\n" +
            "WHERE ap.abac_id = #{abacId}")
    List<String> selectPermissions(Long abacId);
}
@Mapper
public interface UserDao extends BaseMapper<User> {
    /** 获取用户的仓库信息 */
    @Select("SELECT repository FROM user_contribution WHERE user_id = #{userId}")
    List<String> selectRepository(@Param("userId") Long userId);
}

service层

@Service
@RequiredArgsConstructor
public class AbacService {
    private final AbacDao abacDao;

    /** 获取abac表达式详细信息列表 */
    public List<Abac> getAll() {
        List<Abac> abacs = abacDao.selectList(null);
        for (Abac abac : abacs) {
            List<String> permissions = abacDao.selectPermissions(abac.getId());
            abac.setPermissions(permissions);
        }
        return abacs;
    }
}
@Service
@RequiredArgsConstructor
public class UserService {
    private final UserDao userDao;

    /** 根据userId获取用户详细信息 */
    public User get(Long userId) {
        User user = userDao.selectById(userId);
        List<String> repository = userDao.selectRepository(userId);
        user.setContributions(repository);
        return user;
    }
}

4.5.security上下文

/**
 * 自定义用户元数据  用于获取一些实体的属性、操作类型、相关的环境
 */
public interface MetadataCustomizer {

    /** 自定义用户元数据 */
    void customize(User user);
}
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 解析abac表达式
 */
@Component
public class SecurityContext {
    /**
     * SpEL表达式解析器
     */
    private final ExpressionParser expressionParser = new SpelExpressionParser();

    /**
     * 解析abac表达式
     *
     * @param user  用户详细信息
     * @param abacs abac表达式详细信息集合
     * @return expressions集合, 将这个结果集存放到 Spring Security 或者Apache APISIX的userDetail上下文中
     */
    public List<String> rbacPermissions(User user, List<Abac> abacs) {
        return this.rbacPermissions(user, abacs, Collections.emptyList());
    }

    /**
     * 解析abac表达式
     *
     * @param user                用户详细信息
     * @param abacs               abac表达式详细信息集合
     * @param metadataCustomizers 自定义用户元数据  用于获取一些实体的属性、操作类型、相关的环境
     * @return expressions集合, 将这个结果集存放到 Spring Security 或者Apache APISIX的userDetail上下文中
     */
    public List<String> rbacPermissions(User user, List<Abac> abacs, List<MetadataCustomizer> metadataCustomizers) {
        // 处理自定义元数据
        metadataCustomizers.forEach(metadataCustomizer -> metadataCustomizer.customize(user));

        List<String> expressions = new ArrayList<>();
        for (Abac abac : abacs) {
            // 解析表达式的求值器
            Expression expression = expressionParser.parseExpression(abac.getExpression());
            // 创建环境上下文
            EvaluationContext context = new StandardEvaluationContext(user);
            // 获取expression的结果
            if (expression.getValue(context, boolean.class)) {
                expressions.addAll(abac.getPermissions());
            }
        }
        return expressions;
    }
}

4.6.启动类

@SpringBootApplication
public class SpringbootAbacApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootAbacApplication.class, args);
    }
}

4.7.测试类

@SpringBootTest
class AbacApplicationTests {

    @Autowired
    private UserService userService;

    @Autowired
    private AbacService abacService;

    @Autowired
    private SecurityContext securityContext;

    /** 获取不同用户的abac权限 */
    @Test
    void testRbac() {
        User user = userService.get(1L);
        System.out.println(user);

        List<Abac> rbac = abacService.getAll();
        System.out.println(rbac);

        List<String> permissions = securityContext.rbacPermissions(user, rbac);
        System.out.println(permissions);

        user = userService.get(2L);
        System.out.println(user);
        permissions = securityContext.rbacPermissions(user, rbac);
        System.out.println(permissions);

        user = userService.get(3L);
        System.out.println(user);
        permissions = securityContext.rbacPermissions(user, rbac);
        System.out.println(permissions);

    }

    /**
     * 获取自定义权限
     */
    @Test
    void testMetadataCustomizer() {
        User user = userService.get(1L);
        System.out.println(user);

        List<Abac> rbac = abacService.getAll();
        System.out.println(rbac);

        List<String> permissions = securityContext.rbacPermissions(user, rbac);
        System.out.println(permissions);

        permissions = securityContext.rbacPermissions(user, rbac, getMetadataCustomizer());
        System.out.println(permissions);
    }

    /** 模拟网络ip */
    private List<MetadataCustomizer> getMetadataCustomizer() {
        return new ArrayList<MetadataCustomizer>() {{
            add(user -> user.getMetadata().put("ip", "192.168.0.1"));
        }};
    }
}

testRbac()测试效果

#用户1
User(id=1, name=魏昌进, age=26, email=mail@wcj.plus, contributions=[galaxy-sea/spring-cloud-apisix], metadata={})
#所有ABAC
[Abac(id=1, expression=contributions.contains('galaxy-sea/spring-cloud-apisix'), permissions=[github:pr:merge]), Abac(id=2, expression=name == 'admin', permissions=[github:pr:merge, github:pr:close, github:pr:open, github:pr:comment]), Abac(id=3, expression=metadata.get('ip') == '192.168.0.1', permissions=[github:pr:merge, github:pr:close, github:pr:open, github:pr:comment])]
#用户1权限
[github:pr:merge]

#用户2
User(id=2, name=test, age=1, email=mail1@wcj.plus, contributions=[spring-cloud/spring-cloud-commons, spring-cloud/spring-cloud-openfeign, alibaba/spring-cloud-alibaba, Tencent/spring-cloud-tencent, apache/apisix-docker], metadata={})
#用户2权限
[]

#用户3
User(id=3, name=admin, age=1, email=mail2@wcj.plus, contributions=[], metadata={})
#用户3权限
[github:pr:merge, github:pr:close, github:pr:open, github:pr:comment]

testMetadataCustomizer()测试效果

#用户1
User(id=1, name=魏昌进, age=26, email=mail@wcj.plus, contributions=[galaxy-sea/spring-cloud-apisix], metadata={})
#所有ABAC
[Abac(id=1, expression=contributions.contains('galaxy-sea/spring-cloud-apisix'), permissions=[github:pr:merge]), Abac(id=2, expression=name == 'admin', permissions=[github:pr:merge, github:pr:close, github:pr:open, github:pr:comment]), Abac(id=3, expression=metadata.get('ip') == '192.168.0.1', permissions=[github:pr:merge, github:pr:close, github:pr:open, github:pr:comment])]
#解析abac表达式,获取用户1满足条件的权限
[github:pr:merge]
#自定义用户元数据 ,判断用户1是否满足ip环境权限
[github:pr:merge, github:pr:merge, github:pr:close, github:pr:open, github:pr:comment]

5.Spring Security 和 Apache Shiro整合

Spring Security只需要修改拦截器即可在获取到UserDetailsSecurityContext#rbacPermissions转换为GrantedAuthority即可

/**
 * 这里是伪代码, 展示一下大概逻辑
 */
public class IamOncePerRequestFilter implements OncePerRequestFilter {
 
  @Autowired
  private SecurityContext securityContext;
 
  @Autowired
  private AbacService abacService;
 
  @Autowired
  private List<MetadataCustomizer> metadataCustomizers;
 
  @Autowired
  public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) {
    UserDetails user = toUser();
    List<String> permissions = securityContext.rbacPermissions(user, abacService.getAll(), metadataCustomizers);
    List<GrantedAuthority> abacAuthority = permissions.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());
    user.getAuthorities().addAll(abacAuthority);
  }
}

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

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

相关文章

WSUS 修补程序管理的替代方法

什么是 WSUS Windows Server Update Services &#xff08;WSUS&#xff09; 是 Microsoft for Windows Server 提供的免费附加组件&#xff0c;WSUS 从Microsoft更新中下载必要的修补程序和更新&#xff0c;并将其分发到 Windows 网络中存在的 Windows 操作系统和相关Microso…

头条文章采集工具-快速获取头条文章方法

头条文章采集。在这个信息快速爆炸的时代&#xff0c;如何轻松而高效地获取头条新闻和热门文章成为了许多人的问题。而147SEO将成为您的信息导航&#xff0c;为您打开头条文章采集的大门&#xff0c;让您畅游信息海洋。 免费全自动采集发布批量管理网站工具-147SEO​www.147seo…

应用层 DNS Linux 下解析域名命令 dig 命令使用详解

查询单个域名的 DNS 信息 dig 命令最典型的用法就是查询单个主机的信息。 linuxidclinuxidc:~$ dig www.linuxidc.com; < <>> DiG 9.11.3-1Ubuntu1.5-Ubuntu < <>> www.linuxidc.com ;; global options: cmd ;; Got answer: ;; ->>HEADER< &…

浅述AI视频智能分析技术及TSINGSEE视频智能解决方案

AI视频智能分析是一种基于人工智能的技术&#xff0c;传统的视频监控方法通常需要由人工对大量视频流进行手动观察&#xff0c;而视频智能分析技术则可以详细检查视频流&#xff0c;并能及时发现异常。TSINGSEE青犀视频AI视频智能分析技术主要包括以下几个方面&#xff1a; 1&a…

指针(二)------字符指针+数组指针+指针与数组(传参)

&#x1f493;博主csdn个人主页&#xff1a;小小unicorn ⏩专栏分类&#xff1a;C语言 &#x1f69a;代码仓库&#xff1a;小小unicorn的代码仓库&#x1f69a; &#x1f339;&#x1f339;&#x1f339;关注我带你学习编程知识 前言&#xff1a; 在指针&#xff08;一&#xf…

[医学图像知识]CT图和PET图的成像表现形式

1.CT图通常来说是单通道灰色图&#xff0c;用灰度值表示了结构对于x射线的吸收程度。 2.PET/SPECT图最初也是灰度图&#xff0c;用灰度值表示细胞的反射gama射线的程度&#xff0c;但是为了更好的观测不同细胞等的区别&#xff0c;通常将灰度图转化为了 伪彩色图像。 找个例子…

原生js值之数据类型详解

js的数据类型 数据类型分类基本数据类型boolean:布尔类undefined:未定义的值null类型数值转换 NumberparseInt 转换整数 parseFloat转换浮点数 String类型特点如何转换成字符串模板字面量字符串插值模板字面量标签函数 symbol类型特性使用 BigInt类型复杂数据类型Object类属性与…

卡奥斯第二届1024程序员节正式启动!

代码改变世界&#xff0c;开源创造未来&#xff0c;卡奥斯第二届1024程序员节开始啦&#xff01; 本次活动设置4个线上活动分会场: 低代码分会场、开源分会场、知识分会场和产品分会场&#xff0c;共12个奖项&#xff0c;1100多个奖品&#xff0c;雷神911X猎荒者游戏本&#x…

一文搞定Postman(菜鸟必看)

什么是Postman&#xff1f; Postman是一个可扩展的 API 测试工具&#xff0c;可以快速集成到 CI/CD 管道中。它于 2012 年作为 Abhinav Asthana 的一个副项目启动&#xff0c;旨在简化测试和开发中的 API 工作流程。API 代表应用程序编程接口&#xff0c;它允许软件应用程序通…

【JVM】类加载器

类与类加载器 类加载器虽然只用于实现类的加载动作&#xff0c;但它在Java程序中起到的作用却远超类加载阶段。对于任意一个类&#xff0c;都必须由加载它的类加载器和这个类本身一起共同确立其在Java虚拟机中的唯一性&#xff0c;每 一个类加载器&#xff0c;都拥有一个独立的…

k8s 中master 与node的通信

一、Master 节点与 Node 节点通讯原理 Master 节点启动时&#xff0c;会运行一个 kube-apiserver 进程&#xff0c;它提供了集群管理的 API 接口&#xff0c;是集群内各个功能模块之间数据交互和通信的中心枢纽&#xff0c;并且它页提供了完备的集群安全机制。在 Node 节点上&a…

【新品发布】洛微科技全新工业级高性能 D系列 TOF相机D3重磅上线!

近日&#xff0c;洛微科技对外发布新款高性能D系列 TOF相机D3&#xff0c;这是一款专为工业环境中高性能操作设计的3D TOF智能相机。 D3基于行业领先的Sony DepthSense像素技术开发&#xff0c;具有毫米级测量精度、VGA深度分辨率、抗环境光能力强、软/硬件多触发方式、HDR适配…

夸克扫描王识别精度领跑行业 愿携手各方伙伴探索AIGC应用新范式

日前&#xff0c;在“AIGC与场景化应用创新”主题研讨会上&#xff0c;阿里智能信息事业群夸克视觉技术负责人黄锐华分享了AI技术应用于扫描场景的最新成果。他表示&#xff0c;AIGC给手机扫描产品打开了创新空间&#xff0c;搭载大模型技术的夸克扫描王对识别手写字体、复杂公…

【验证码逆向专栏】螺丝帽人机验证逆向分析

声明 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;不提供完整代码&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 本文章未…

Maven 查看项目中的依赖

文章目录 通过 Maven 命令查看依赖dependency:listdependency:tree将命令结果输出到文档 视图化查看依赖Maven HelperIDEA 自带工具 想查看项目中使用的依赖列表时&#xff0c;可以参考下面几种方式 通过 Maven 命令查看依赖 在有 pom.xml 的路径下&#xff0c;通过命令行工…

人工智能(pytorch)搭建模型19-手把手利用pytorch框架搭建目标检测DarkNet模型,并展现网络结构

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下人工智能(pytorch)搭建模型19-手把手利用pytorch框架搭建目标检测DarkNet模型&#xff0c;并展现网络结构。随着深度学习技术的不断发展&#xff0c;各种卷积神经网络模型层出不穷&#xff0c;其中DarkNet作为一种快…

CMD脚本实战教程

要在 Windows 11 上编写一个自定义关机的 CMD 脚本文件&#xff0c;你可以创建一个扩展名为 .bat 或 .cmd 的文本文件&#xff0c;并在其中编写脚本。 一、常用语法 rem&#xff1a;注释 pause&#xff1a;暂停正在执行的批处理文件&#xff0c;并提示用户按键之后继续执行 r…

VisualStudio2017社区版安装完毕后,找不到stdio.h等头文件的解决方案

安装完VisualStudio2017社区版后&#xff0c;创建一个C的测试项目&#xff0c;好使&#xff0c;一时心血来潮创建了纯C的项目&#xff0c;结果死活提示找不到头文件“stdio.h”&#xff0c;测试代码如下&#xff1a; #include<stdio.h>int main() {printf("Hello w…

超详细 | CISP-信息安全专业认证考前攻略

近年来&#xff0c;中央关于信息安全的政策不断加码&#xff0c;网络安全已上升到国家战略&#xff0c;安全专业人才缺口以每年1.5万人的速度递增。作为信息安全从业人员&#xff0c;持证上岗已是大势所趋。今天&#xff0c;重点聊一下信息安全国内第一认证CISP&#xff01; 注…

关于表单快速开发低代码技术平台的内容介绍

运用什么样的表单快速开发软件平台可以实现高效率创收&#xff1f;随着科技的进步和飞速发展&#xff0c;专业的低代码技术平台已经走入了很多企业的办公职场中&#xff0c;它们灵活、轻量级、优质、高效、易维护等优势特点&#xff0c;可以高效助力广大企业提质增效&#xff0…