記事ディレクトリ
権限は日常のシステムで比較的一般的な基本機能であり、権限モジュールを備えたシステムではログインが必要です用户能够操作哪些资源,不能够操作哪些资源
。パーミッション モジュールの助けを借りて、システム内で異なる ID を持つ人々によって実行される特定の操作を効果的に制御できます。成熟したバックエンド システムは、比較的完全なパーミッション管理システムから切り離せないものであると言えます。
したがって、権限制御システムの目標は次のとおりです。管理用户行为,保护系统功能。
では、権限制御はどのように行うのでしょうか?
- リソースを定義する
- 権限の作成
- ロールの作成
- ユーザーを管理する
- 協会を設立する
1.RBAC許可モデル
1.RBACの構成
- RBAC モデルには、次の 3 つの基本コンポーネントがあります。
用户、角色和权限
- ユーザー: 各ユーザーは一意の UID によって識別され、異なる役割が付与されます。
- 役割: 役割が異なると権限も異なります。
- 権限: アクセス権
- ユーザーとロールのマッピング: ユーザーとロール間のマッピング関係
- ロールと権限のマッピング: ロールと権限間のマッピング
ロール権限関係
- 権限 → リソース: 一方向多対多1 つの権限に複数のリソースを含めることができ、1 つのリソースを複数の異なる権限に割り当てることができます。
- ロール → 権限: 一方向の多対多。1つのロールに複数の権限を含めることができ、1 つの権限を複数の異なるロールに割り当てることができます。
- ユーザー → ロール: 双方向の多対多。1つのロールに複数のユーザーを含めることができ、1 人のユーザーが複数のポジションを保持できます。
たとえば、管理者と一般ユーザーには異なる権限が付与されます。一般ユーザーは個人情報の変更と表示のみが可能ですが、ユーザーの作成やユーザーの凍結はできません。管理者はすべての権限が付与されているため、すべての操作が可能です。
2.RBACモデルの分類
2.1.ベーシックモデルRBAC0
RBAC0 が基盤であり、多くのシステムは RBAC0 のみに基づいて権限モデルを構築できます。
- このモデルでは、
把权限赋予角色,再把角色赋予用户
. ユーザーとロール、ロールと権限は多对多
すべて関連しています。ユーザーが持つ権限は、そのユーザーのすべてのロールが持つ権限の合計と等しくなります。
例を挙げると、次のようになります。
- たとえば、エンタープライズ管理製品を作成する場合、
抽象出几个角色
営業マネージャー、財務マネージャー、マーケティング マネージャーなどを管理できます。この方法では、アクセス許可を割り当てる場合でも、将来アクセス許可を変更する場合でも、変更するだけで済むため、より柔軟で便利です。把权限分配给这些角色,再把角色赋予用户
用户和角色的关系,或角色和权限的关系
- さらに、ユーザーが複数の役割を持ち、王氏が営業部門とマーケティング部門の両方の責任を負っている場合、王氏に
2个角色
営業マネージャーとマーケティング マネージャーの役割を与えて、両方を持たせることができます这两个角色的所有权限
。
2.2. 役割階層モデル RBAC1
RBAC1 は RBAC0 に基づいています在角色中引入了 "继承" 的概念
。簡単に理解すると、 です给角色可以分成几个等级,每个等级权限不同,从而实现更细粒度的权限管理
。
例を挙げると、次のようになります。
- 前述の RBAC0 の例に基づいて、企業の営業マネージャーがいくつかのレベルに分かれていることもわかりました。営業マネージャーの権限の一部。現時点では、RBAC1 等級モデルを使用して、営業マネージャーの役割を複数のレベルに分割し、より低いレベルを副営業マネージャーに割り当てることができます。
2.3. 役割制限モデル RBAC2
RBAC2 もRBAC0
基盤上に構築されていますが、ユーザー、ロール、権限にいくつかの制限が追加されています。
静态职责分离SSD
これらの制限は、 (静的職務分離) と动态职责分离DSD
(動的職務分離)の 2 つのカテゴリに分類できます。具体的な制限は次のとおりです。
例を挙げると、次のようになります。
互斥
前述の RBAC0 の例に基づいて、いくつかの役割が必要で あることがわかりました。- たとえば、ユーザーに営業マネージャーの役割が割り当てられている場合、財務マネージャーの役割を割り当てることはできません。それ以外の場合は、財務マネージャーの役割を割り当てることができます
录入合同
。审核合同
- 別の例として、一部の企業では役割のアップグレードを非常に重視しており、営業マンが営業マネージャーに昇格したい場合は、まず営業スーパーバイザーに昇格する必要があり、この時点でそれを採用する必要があります
先决条件限制
。
- 別の例として、一部の企業では役割のアップグレードを非常に重視しており、営業マンが営業マネージャーに昇格したい場合は、まず営業スーパーバイザーに昇格する必要があり、この時点でそれを採用する必要があります
- たとえば、ユーザーに営業マネージャーの役割が割り当てられている場合、財務マネージャーの役割を割り当てることはできません。それ以外の場合は、財務マネージャーの役割を割り当てることができます
2.4. 統合モデル RBAC3
RBAC3 は であるRBAC1和RBAC2的合集
ため、RBAC3 には **角色分层
と OKの両方があります增加各种限制
。**
3.RBAC0モデルのコアテーブル構造
3.1.テーブル構造の設計
- 上記のエンティティ対応分析から、権限テーブルの設計は、ユーザー テーブル (t_user)、ロール テーブル (t_role)、t_user_role (ユーザー ロール テーブル)、リソース テーブル (t_module)、権限テーブル (t_permission) の 5 つの基本テーブル構造に分割されます。 ) の場合、テーブル構造の関係は次のとおりです。
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. モジュール分割
テーブル構造の設計からわかります。3 つのメイン テーブル (t_user、t_role、t_module) があり、関数実装の観点から 3 つの主要なモジュールに分割されています。
ユーザー管理
- ユーザー基本情報の保守
- ユーザー役割の割り当て
役割管理
- 基本的な役割情報の保守
- ロール権限(ロールごとに操作できるメニューの割り当て)
- ロール認証 (ロールが所有する権限を確認します)
資源管理
- リソース情報のメンテナンス
- メニュー出力ダイナミック制御
4. RBAC ベースの内線番号 - ユーザー グループ
-
RBAC モデルに基づいて、当社の製品により適したものにするために適切に拡張することもできます。たとえば
增加用户组概念
、ユーザー户组分配角色,再把用户加入用户
グループに直接送信します。このように、ユーザーは自分自身の権限に加えて、所属するユーザー グループのすべての権限も持ちます。
例を挙げると、次のようになります。 -
たとえば、
部门看成一个用户组
営業部門と財務部門などの 1 つを結合し、这个部门直接赋予角色,使部门拥有部门权限,这样这个部门的所有用户都有了部门权限。
ユーザー グループの概念を与えて、ユーザーがすでに持っているロール権限に影響を与えることなく、グループ ユーザーをより簡単に認可できます。
2. ABACパーミッションモデル(Javaベースで説明)
1.ABACとは
ABAC(属性ベースアクセス制御)
属性
ABAC は、何らかの方法でユーザーを権限に関連付ける一般的な方法とは異なる権限制御に基づいて、1つまたは属性のグループを動的に計算して特定の条件が満たされるかどうかを判断することによって権限の判断を行います(単純なロジックを作成できます)。用户属性(如用户年龄)
属性は通常、 、环境属性(如当前时间)
、の 4 つのカテゴリに分類される操作属性(如读取)和对象属性
ため、理論的には非常に柔軟な権限制御を実現でき、ほぼすべてのタイプのニーズを満たすことができます。
2. ABAC条件判定制御
ABAC アクセス制御要件に基づいて要动态计算实体的属性、操作类型、相关的环境来控制是否有对操作对象的权限
、条件判断の柔軟性、汎用性、使いやすさを設計時に考慮する必要があります。ユーザーは Web ページを通じて認可を設定するだけで済みます。これには、ハードコーディングを削減し、ロジックをシンプルかつユニバーサルにする必要があります。 . の場合、これを達成するにはいくつかの演算子を満たす必要があります。
タイプ | オペレーター |
---|---|
算術演算子 | +、-、*、/、%、^、div、mod |
関係演算子 | <、>、==、!=、<=、>=、lt、gt、eq、ne、le、ge |
論理演算子 | そして、または、ではありません &&、 |
状態 | ?: |
- 例: ユーザーは、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 フレームワークのリスト
-
春の表情
-
OGNL
-
MVEL
-
JBoss EL
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, '[email protected]'),
(2, 'test', 1, '[email protected]'),
(3, 'admin', 1, '[email protected]');
#插入用户边缘数据
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
- スプリングブート 2.x
- MyBatis プラス 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コード
実体
@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<>();
}
層
@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
@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.セキュリティコンテキスト
/**
* 自定义用户元数据 用于获取一些实体的属性、操作类型、相关的环境
*/
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=[email protected], 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=[email protected], 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=[email protected], contributions=[], metadata={
})
#用户3权限
[github:pr:merge, github:pr:close, github:pr:open, github:pr:comment]
testMetadataCustomizer() テスト効果
#用户1
User(id=1, name=魏昌进, age=26, email=[email protected], 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 Taro の統合
Spring Security はインターセプターを変更して、それを次のようにUserDetails
変換するだけで済みます。SecurityContext#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);
}
}