自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

TienChin 項目中的 RBAC 是怎么玩的?

開發(fā) 前端 項目管理
RBAC(Role-based access control)是一種以角色為基礎的訪問控制(Role-based access control,RBAC),它是一種較新且廣為使用的權限控制機制,這種機制不是直接給用戶賦予權限,而是將權限賦予角色。

這篇文章最后留下來一個問題,就是用戶的權限該如何設置?今天我們就來聊聊這個話題。

1. 角色與權限

首先我們先來看看角色與權限,該如何設計角色與權限,其實有很多非常成熟的理論,最為常見的莫過于 RBAC 了。

1.1 RBAC 簡介

RBAC(Role-based access control)是一種以角色為基礎的訪問控制(Role-based access control,RBAC),它是一種較新且廣為使用的權限控制機制,這種機制不是直接給用戶賦予權限,而是將權限賦予角色。

RBAC 權限模型將用戶按角色進行歸類,通過用戶的角色來確定用戶對某項資源是否具備操作權限。RBAC 簡化了用戶與權限的管理,它將用戶與角色關聯(lián)、角色與權限關聯(lián)、權限與資源關聯(lián),這種模式使得用戶的授權管理變得非常簡單和易于維護。

1.2 RBAC 的提出

權限、角色這些東西,在早期 1970 年代的商業(yè)計算機程序中就可以找到相關的應用,但是早期的程序相對簡單,而且并不存在一個明確的、通用的、公認的權限管理模型。

Ferraiolo 和 Kuhn 兩位大佬于 1992 年提出了一種基于通用角色的訪問控制模型(看來這個模型比松哥年齡還大),首次提出了 RBAC 權限模型用來代替?zhèn)鹘y(tǒng)的 MAC 和 DAC 兩種權限控制方案,并且就 RBAC 中的相關概念給出了解釋。

Ferraiolo,Cugini 和 Kuhn 于 1995 年擴展了 1992 年提出的權限模型。該模型的主要功能是所有訪問都是通過角色進行的,而角色本質(zhì)上是權限的集合,并且所有用戶只能通過角色獲得權限。在組織內(nèi),角色相對穩(wěn)定,而用戶和權限都很多,并且可能會迅速變化。因此,通過角色控制權限可以簡化訪問控制的管理和檢查。

到了 1996 年,Sandhu,Coyne,F(xiàn)einstein 和 Youman 正式提出了 RBAC 模型,該模型以模塊化方式細化了 RBAC,并提出了基于該理論的 RBAC0-RBAC3 四種不同模型。

今天,大多數(shù)信息技術供應商已將 RBAC 納入其產(chǎn)品線,除了常規(guī)的企業(yè)級應用,RBAC 也廣泛應用在醫(yī)療、國防等領域。

目前網(wǎng)上關于 RBAC 理論性的東西松哥只找到英文的,感興趣的小伙伴可以看下,地址是:

https://csrc.nist.gov/projects/Role-Based-Access-Control

如果小伙伴們有中文的資料鏈接,歡迎留言說明。

1.3 RBAC 三原則

  • 最小權限:給角色配置的權限是其完成任務所需要的最小權限集合。
  • 責任分離:通過相互獨立互斥的角色來共同完成任務。
  • 數(shù)據(jù)抽象:通過權限的抽象來體現(xiàn),RBAC 支持的數(shù)據(jù)抽象程度與 RBAC 的實現(xiàn)細節(jié)有關。

數(shù)據(jù)抽象:通過權限的抽象來體現(xiàn),RBAC 支持的數(shù)據(jù)抽象程度與 RBAC 的實現(xiàn)細節(jié)有關。

1.4 RBAC 模型分類

1.4.1 RBAC0

RBAC0 是最簡單的用戶、角色、權限模型。RBAC0 是 RBAC 權限模型中最核心的一部分,后面其他模型都是在此基礎上建立。

圖片

在 RBAC0 中,一個用戶可以具備多個角色,一個角色可以具備多個權限,最終用戶所具備的權限是用戶所具備的角色的權限并集。

1.4.2 RBAC1

RBAC1 則是在 RABC0 的基礎上引入了角色繼承,讓角色有了上下級關系。

圖片

在本系列前面的文章中,松哥也曾多次向大家介紹過 Spring Security 中的角色繼承。

1.4.3 RBAC2

RBAC2 也是在 RBAC0 的基礎上進行擴展,引入了靜態(tài)職責分離和動態(tài)職責分離。

圖片

要理解職責分離,我們得先明白角色互斥。

在實際項目中,有一些角色是互斥的,對立的,例如財務這個角色一般是不能和其他角色兼任的,否則自己報賬自己審批,豈不是爽歪歪!

通過職責分離可以解決這個問題:

靜態(tài)職責分離

在設置階段就做好了限制。比如同一用戶不能授予互斥的角色,用戶只能有有限個角色,用戶獲得高級權限之前要有低級權限等等。

動態(tài)職責分離

在運行階段進行限制。比如運行時同一用戶下5個角色中只能同時有2個角色激活等等。

1.4.4 RBAC3

將 RBAC1 和 RBAC2 結合起來,就形成了 RBAC3。

圖片

1.5 擴展

我們?nèi)粘R姷降暮芏鄼嘞弈P投际窃?RBAC 的基礎上擴展出來的。

例如在有的系統(tǒng)中我們可以見到用戶組的概念,就是將用戶分組,用戶同時具備自身的角色以及分組的角色。

我們 TienChin 項目所用的腳手架中的權限,就基本上是按照 RBAC 這套權限模型來的。

2. 表設計

我們來看下 RuoYi-Vue 腳手架中跟用戶、角色以及權限相關的表。

這里主要涉及到如下幾張表:

  • sys_user:這個是用戶表。
  • sys_role:這個是角色表。
  • sys_user_role:這個是用戶角色關聯(lián)表。
  • sys_menu:這個是菜單表,也可以理解為是資源表。
  • sys_role_menu:這個是資源角色關聯(lián)表。

通過用戶的 id,可以去 sys_user_role? 表中查詢到這個用戶具備的角色 id,再根據(jù)角色 id,去 sys_role_menu? 表中查詢到這個角色可以操作的資源 id,再根據(jù)資源 id,去 sys_menu 表中查詢到對應的資源,基本上就是這個樣一個流程。

那么 Java 代碼中該怎么做呢?

3. 代碼實現(xiàn)

首先定義了一個 Java 類 SysUser,這個跟數(shù)據(jù)庫中的 sys_user? 表是對應的,我們來看 UserDetailsService 的具體實現(xiàn):

@Service
public class UserDetailsServiceImpl implements UserDetailsService {
private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class);

@Autowired
private ISysUserService userService;

@Autowired
private SysPermissionService permissionService;

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
SysUser user = userService.selectUserByUserName(username);
if (StringUtils.isNull(user)) {
log.info("登錄用戶:{} 不存在.", username);
throw new ServiceException("登錄用戶:" + username + " 不存在");
} else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) {
log.info("登錄用戶:{} 已被刪除.", username);
throw new ServiceException("對不起,您的賬號:" + username + " 已被刪除");
} else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
log.info("登錄用戶:{} 已被停用.", username);
throw new ServiceException("對不起,您的賬號:" + username + " 已停用");
}

return createLoginUser(user);
}

public UserDetails createLoginUser(SysUser user) {
return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user));
}
}

從數(shù)據(jù)庫中查詢到的就是 SysUser 對象,然后對該對象稍作改造,將之改造成為一個 LoginUser 對象,這個 LoginUser 則是 UserDetails 接口的實現(xiàn)類,里邊保存了當前登錄用戶的關鍵信息。

在創(chuàng)建 LoginUser 對象的時候,有一個 permissionService.getMenuPermission 方法用來查詢用戶的權限,根據(jù)當前用戶的 id,查詢到用戶的角色,再根據(jù)用戶角色,查詢到用戶的權限,另外,如果當前用戶的角色是 admin,那么就設置用戶角色為 *:*:*,這是一段硬編碼。

我們再來看看 LoginUser 的設計:

public class LoginUser implements UserDetails {
/**
* 權限列表
*/
private Set<String> permissions;

/**
* 用戶信息
*/
private SysUser user;
public LoginUser(Long userId, Long deptId, SysUser user, Set<String> permissions) {
this.userId = userId;
this.deptId = deptId;
this.user = user;
this.permissions = permissions;
}

@JSONField(serialize = false)
@Override
public String getPassword() {
return user.getPassword();
}

@Override
public String getUsername() {
return user.getUserName();
}
/**
* 賬戶是否未過期,過期無法驗證
*/
@JSONField(serialize = false)
@Override
public boolean isAccountNonExpired() {
return true;
}
/**
* 指定用戶是否解鎖,鎖定的用戶無法進行身份驗證
*
* @return
*/
@JSONField(serialize = false)
@Override
public boolean isAccountNonLocked() {
return true;
}
/**
* 指示是否已過期的用戶的憑據(jù)(密碼),過期的憑據(jù)防止認證
*
* @return
*/
@JSONField(serialize = false)
@Override
public boolean isCredentialsNonExpired() {
return true;
}
/**
* 是否可用 ,禁用的用戶不能身份驗證
*
* @return
*/
@JSONField(serialize = false)
@Override
public boolean isEnabled() {
return true;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
}

有一些屬性我省略掉了,大家可以文末下載源碼查看。

小伙伴們看到,這個 LoginUser 實現(xiàn)了 UserDetails 接口,但是和 vhr 中有一個很大的不同,就是這里沒有處理 getAuthorities 方法,也就是說當系統(tǒng)想要去獲取用戶權限的時候,二話不說直接返回一個 null。這是咋回事呢?

因為在這個腳手架中,將來進行權限校驗的時候,是按照下面這樣來的:

@PreAuthorize("@ss.hasPermi('system:menu:add')")
@PostMapping
public AjaxResult add(@Validated @RequestBody SysMenu menu) {
//省略
}

@PreAuthorize 注解中的 @ss.hasPermi('system:menu:add')? 表達式,表示調(diào)用 Spring 容器中一個名為 ss 的 Bean 的 hasPermi 方法,去判斷當前用戶是否具備一個名為 system:menu:add 的權限。一個名為 ss 的 Bean 的 hasPermi 方法如下:

@Service("ss")
public class PermissionService {
/**
* 所有權限標識
*/
private static final String ALL_PERMISSION = "*:*:*";

/**
* 管理員角色權限標識
*/
private static final String SUPER_ADMIN = "admin";

private static final String ROLE_DELIMETER = ",";

private static final String PERMISSION_DELIMETER = ",";

/**
* 驗證用戶是否具備某權限
*
* @param permission 權限字符串
* @return 用戶是否具備某權限
*/
public boolean hasPermi(String permission) {
if (StringUtils.isEmpty(permission)) {
return false;
}
LoginUser loginUser = SecurityUtils.getLoginUser();
if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) {
return false;
}
return hasPermissions(loginUser.getPermissions(), permission);
}
/**
* 判斷是否包含權限
*
* @param permissions 權限列表
* @param permission 權限字符串
* @return 用戶是否具備某權限
*/
private boolean hasPermissions(Set<String> permissions, String permission) {
return permissions.contains(ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission));
}
}

由于這里是純手工操作,在比較的時候,直接獲取到當前登錄用戶對象 LoginUser,再手動調(diào)用他的 hasPermissions 方法去判斷權限是否滿足,由于都是自定義操作,所以是否實現(xiàn) UserDetails#getAuthorities 方法已經(jīng)不重要了,不過按照這里的比對方案,是不支持通配符的比對的。

例如用戶具備針對字典表的所有操作權限,表示為 system:dict:*?,但是當和 system:dict:list 進行比較的時候,發(fā)現(xiàn)比較結果為 false,這塊想要比對成功也是可以的,例如可以通過正則表達式或者其他方式來操作,反正都是字符串比較,相信大家都能自己搞得定。

現(xiàn)在,前端提供操作頁面,也可以配置每一個用戶的角色,也可以配置每一個角色可以操作的權限就行了,這個就比較簡單了,不多說。

責任編輯:武曉燕 來源: 江南一點雨
相關推薦

2022-07-04 10:39:24

TienChin項目自定義

2024-03-15 08:06:58

MySQLJOIN命令

2022-06-02 08:30:55

項目React重構

2023-01-03 11:39:06

2020-05-28 15:41:48

微軟C+語言

2013-06-20 09:44:06

程序員

2022-07-11 10:38:06

TienChin項目動態(tài)

2017-09-19 14:13:53

Snapshot數(shù)據(jù)庫HBase

2024-06-28 12:47:29

C#弱引用底層

2022-08-25 09:08:40

微服務架構

2022-05-30 16:19:26

C#多態(tài)底層虛方法

2025-04-18 08:50:57

項目裝飾器日志

2020-04-30 09:24:46

Go項目語言

2019-11-19 16:10:24

面試官Java編程語言

2009-06-16 13:40:06

OSGiApache Feli

2009-07-21 09:52:06

小型軟件項目

2024-09-12 15:36:57

2009-06-24 14:18:47

資源管理敏捷項目

2009-06-24 17:34:58

使用JSF的經(jīng)驗

2013-05-29 10:42:59

阿里巴巴阿里巴巴菜鳥大數(shù)據(jù)
點贊
收藏

51CTO技術棧公眾號