shiro安全框架的核心就是認(rèn)證和授權(quán)诅挑,前面已談到關(guān)于restful的改造,本文主要談一下認(rèn)證和授權(quán)過程绷蹲,以及粗粒度和細(xì)粒度的授權(quán)等傻昙。
參考:https://blog.csdn.net/johnstrive/article/details/74741783
權(quán)限管理
基本上涉及到用戶參與的系統(tǒng)都要權(quán)限管理廊谓,權(quán)限管理屬于系統(tǒng)安全的范疇梳猪,權(quán)限管理實(shí)現(xiàn)對(duì)用戶訪問系統(tǒng)的控制,按照安全規(guī)則或者安全策略控制用戶可以訪問而且只能訪問自己被授權(quán)的資源蒸痹。
權(quán)限管理包括身份認(rèn)證和授權(quán)兩部分春弥,簡稱認(rèn)證授權(quán)。對(duì)于需要訪問控制的資源用戶首先經(jīng)過身份認(rèn)證叠荠,認(rèn)證通過后用戶具有該資源的訪問權(quán)限方可訪問匿沛。
身份認(rèn)證
判斷一個(gè)用戶是否為合法用戶的處理過程。最常用的簡單身份認(rèn)證方式是系統(tǒng)通過核對(duì)用戶輸入的用戶名和口令榛鼎,看其是否與系統(tǒng)中存儲(chǔ)的該用戶的用戶名和口令一致逃呼,來判斷用戶身份是否正確。對(duì)于采用指紋等系統(tǒng)者娱,則出示指紋抡笼;對(duì)于硬件Key等刷卡系統(tǒng),則需要刷卡黄鳍。
認(rèn)證關(guān)鍵對(duì)象
- Subject:主體
訪問系統(tǒng)的用戶推姻,主體可以是用戶、程序等框沟,進(jìn)行認(rèn)證的都稱為主體藏古; - Principal:身份信息
是主體(subject)進(jìn)行身份認(rèn)證的標(biāo)識(shí)增炭,標(biāo)識(shí)必須具有唯一性,如用戶名拧晕、手機(jī)號(hào)隙姿、郵箱地址等,一個(gè)主體可以有多個(gè)身份厂捞,但是必須有一個(gè)主身份(Primary Principal)输玷。 - credential:憑證信息
是只有主體自己知道的安全信息,如密碼蔫敲、證書等饲嗽。
授權(quán)
授權(quán),即訪問控制奈嘿,控制誰能訪問哪些資源。主體進(jìn)行身份認(rèn)證后需要分配權(quán)限方可訪問系統(tǒng)的資源吞加,對(duì)于某些資源沒有權(quán)限是無法訪問的裙犹。
授權(quán)關(guān)鍵對(duì)象
授權(quán)可簡單理解為 who 對(duì) what(which) 進(jìn)行 How 操作:
- Who,即主體(Subject)衔憨,主體需要訪問系統(tǒng)中的資源叶圃。
- What,即資源(Resource)践图,如系統(tǒng)菜單掺冠、頁面、按鈕码党、類方法德崭、系統(tǒng)商品信息等。資源包括資源類型和資源實(shí)例揖盘,比如商品信息為資源類型眉厨,類型為t01的商品為資源實(shí)例,編號(hào)為001的商品信息也屬于資源實(shí)例兽狭。
- How憾股,權(quán)限/許可(Permission),規(guī)定了主體對(duì)資源的操作許可箕慧,權(quán)限離開資源沒有意義服球,如用戶查詢權(quán)限、用戶添加權(quán)限颠焦、某個(gè)類方法的調(diào)用權(quán)限斩熊、編號(hào)為001用戶的修改權(quán)限等,通過權(quán)限可知主體對(duì)哪些資源都有哪些操作許可蒸健。
權(quán)限分為粗顆粒和細(xì)顆粒座享,粗顆粒權(quán)限是指對(duì)資源類型的權(quán)限婉商,細(xì)顆粒權(quán)限是對(duì)資源實(shí)例的權(quán)限。
權(quán)限模型
對(duì)上節(jié)中的主體渣叛、資源丈秩、權(quán)限通過數(shù)據(jù)模型表示。
- 主體(賬號(hào)淳衙、密碼)
- 角色(角色名稱)
- 主體和角色關(guān)系(主體id蘑秽、角色id)
- 權(quán)限(權(quán)限名稱、資源id)
- 角色和權(quán)限關(guān)系(角色id箫攀、權(quán)限id)
- 資源(資源id肠牲、訪問地址)
如下圖:
權(quán)限控制
基于角色的訪問控制
RBAC基于角色的訪問控制(Role-Based Access Control)是以角色為中心進(jìn)行訪問控制,比如:主體的角色為總經(jīng)理可以查詢企業(yè)運(yùn)營報(bào)表靴跛,查詢員工工資信息等缀雳,訪問控制流程如下:
圖中的判斷邏輯代碼可以理解為:
if(主體.hasRole("總經(jīng)理角色id")){
查詢工資
}
缺點(diǎn):以角色進(jìn)行訪問控制粒度較粗,如果上圖中查詢工資所需要的角色變化為總經(jīng)理和部門經(jīng)理梢睛,此時(shí)就需要修改判斷邏輯為“判斷主體的角色是否是總經(jīng)理或部門經(jīng)理”肥印,系統(tǒng)可擴(kuò)展性差。
修改代碼如下:
if(主體.hasRole("總經(jīng)理角色id") || 主體.hasRole("部門經(jīng)理角色id")){
查詢工資
}
基于資源的訪問控制
RBAC基于資源的訪問控制(Resource-Based Access Control)是以資源為中心進(jìn)行訪問控制绝葡,比如:主體必須具有查詢工資權(quán)限才可以查詢員工工資信息等深碱,訪問控制流程如下:
上圖中的判斷邏輯代碼可以理解為:
if(主體.hasPermission("wage:query")){
查詢工資
}
優(yōu)點(diǎn):系統(tǒng)設(shè)計(jì)時(shí)定義好查詢工資的權(quán)限標(biāo)識(shí),即使查詢工資所需要的角色變化為總經(jīng)理和部門經(jīng)理也只需要將“查詢工資信息權(quán)限”添加到“部門經(jīng)理角色”的權(quán)限列表中藏畅,判斷邏輯不用修改敷硅,系統(tǒng)可擴(kuò)展性強(qiáng)。
粗顆粒度和細(xì)顆粒度
什么是粗顆粒度和細(xì)顆粒度
對(duì)資源類型的管理稱為粗顆粒度權(quán)限管理愉阎,即只控制到菜單绞蹦、按鈕、方法诫硕,粗粒度的例子比如:用戶具有用戶管理的權(quán)限坦辟,具有導(dǎo)出訂單明細(xì)的權(quán)限。對(duì)資源實(shí)例的控制稱為細(xì)顆粒度權(quán)限管理章办,即控制到數(shù)據(jù)級(jí)別的權(quán)限锉走,比如:用戶只允許修改本部門的員工信息,用戶只允許導(dǎo)出自己創(chuàng)建的訂單明細(xì)藕届。
如何實(shí)現(xiàn)粗顆粒度和細(xì)顆粒度
對(duì)于粗顆粒度的權(quán)限管理可以很容易做系統(tǒng)架構(gòu)級(jí)別的功能挪蹭,即系統(tǒng)功能操作使用統(tǒng)一的粗顆粒度的權(quán)限管理。
對(duì)于細(xì)顆粒度的權(quán)限管理不建議做成系統(tǒng)架構(gòu)級(jí)別的功能休偶,因?yàn)閷?duì)數(shù)據(jù)級(jí)別的控制是系統(tǒng)的業(yè)務(wù)需求梁厉,隨著業(yè)務(wù)需求的變更業(yè)務(wù)功能變化的可能性很大,建議對(duì)數(shù)據(jù)級(jí)別的權(quán)限控制在業(yè)務(wù)層個(gè)性化開發(fā),比如:用戶只允許修改自己創(chuàng)建的商品信息可以在service接口添加校驗(yàn)實(shí)現(xiàn)词顾,service接口需要傳入當(dāng)前操作人的標(biāo)識(shí)八秃,與商品信息創(chuàng)建人標(biāo)識(shí)對(duì)比,不一致則不允許修改商品信息肉盹。
基于url攔截
基于url攔截是企業(yè)中常用的權(quán)限管理方法昔驱,實(shí)現(xiàn)思路是:將系統(tǒng)操作的每個(gè)url配置在權(quán)限表中,將權(quán)限對(duì)應(yīng)到角色上忍,將角色分配給用戶骤肛,用戶訪問系統(tǒng)功能通過Filter進(jìn)行過慮,過慮器獲取到用戶訪問的url窍蓝,只要訪問的url是用戶分配角色中的url則放行繼續(xù)訪問腋颠。
如下圖:
Shiro中關(guān)鍵對(duì)象
-
Subject
即主體,外部應(yīng)用與subject進(jìn)行交互吓笙,subject記錄了當(dāng)前操作用戶淑玫,將用戶的概念理解為當(dāng)前操作的主體,可能是一個(gè)通過瀏覽器請(qǐng)求的用戶面睛,也可能是一個(gè)運(yùn)行的程序混移。 Subject在shiro中是一個(gè)接口,接口中定義了很多認(rèn)證授權(quán)相關(guān)的方法侮穿,外部程序通過subject進(jìn)行認(rèn)證授,而subject是通過SecurityManager安全管理器進(jìn)行認(rèn)證授權(quán) -
SecurityManager
即安全管理器毁嗦,對(duì)全部的subject進(jìn)行安全管理亲茅,它是shiro的核心,負(fù)責(zé)對(duì)所有的subject進(jìn)行安全管理狗准。通過SecurityManager可以完成subject的認(rèn)證克锣、授權(quán)等,實(shí)質(zhì)上SecurityManager是通過Authenticator進(jìn)行認(rèn)證腔长,通過Authorizer進(jìn)行授權(quán)袭祟,通過SessionManager進(jìn)行會(huì)話管理等。
SecurityManager是一個(gè)接口捞附,繼承了Authenticator, Authorizer, SessionManager這三個(gè)接口巾乳。 -
Authenticator
即認(rèn)證器,對(duì)用戶身份進(jìn)行認(rèn)證鸟召,Authenticator是一個(gè)接口胆绊,shiro提供ModularRealmAuthenticator實(shí)現(xiàn)類,通過ModularRealmAuthenticator基本上可以滿足大多數(shù)需求欧募,也可以自定義認(rèn)證器压状。 -
Authorizer
即授權(quán)器,用戶通過認(rèn)證器認(rèn)證通過跟继,在訪問功能時(shí)需要通過授權(quán)器判斷用戶是否有此功能的操作權(quán)限种冬。 -
realm
即領(lǐng)域镣丑,相當(dāng)于datasource數(shù)據(jù)源,securityManager進(jìn)行安全認(rèn)證需要通過Realm獲取用戶權(quán)限數(shù)據(jù)娱两,比如:如果用戶身份數(shù)據(jù)在數(shù)據(jù)庫那么realm就需要從數(shù)據(jù)庫獲取用戶身份信息莺匠。
注意:不要把realm理解成只是從數(shù)據(jù)源取數(shù)據(jù),在realm中還有認(rèn)證授權(quán)校驗(yàn)的相關(guān)的代碼谷婆。 -
SessionManager
即會(huì)話管理慨蛙,shiro框架定義了一套會(huì)話管理,它不依賴web容器的session纪挎,所以shiro可以使用在非web應(yīng)用上期贫,也可以將分布式應(yīng)用的會(huì)話集中在一點(diǎn)管理,此特性可使它實(shí)現(xiàn)單點(diǎn)登錄异袄。 -
SessionDAO
即會(huì)話dao通砍,是對(duì)session會(huì)話操作的一套接口,比如要將session存儲(chǔ)到數(shù)據(jù)庫烤蜕,可以通過jdbc將會(huì)話存儲(chǔ)到數(shù)據(jù)庫封孙。 -
CacheManager
CacheManager即緩存管理,將用戶權(quán)限數(shù)據(jù)存儲(chǔ)在緩存讽营,這樣可以提高性能虎忌。 -
Cryptography
即密碼管理,shiro提供了一套加密/解密的組件橱鹏,方便開發(fā)膜蠢。比如提供常用的散列、加/解密等功能莉兰。
Shiro認(rèn)證流程
自定義Realm
認(rèn)證時(shí)需要自己實(shí)現(xiàn)realm去獲取特定的數(shù)據(jù)挑围,如驗(yàn)證賬號(hào)密碼等。
public class CustomRealm1 extends AuthorizingRealm {
@Override
public String getName() {
return "customRealm1";
}
//支持UsernamePasswordToken
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof UsernamePasswordToken;
}
//認(rèn)證
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token) throws AuthenticationException {
//從token中 獲取用戶身份信息
String username = (String) token.getPrincipal();
//拿username從數(shù)據(jù)庫中查詢
//....
//如果查詢不到則返回null
if(!username.equals("zhang")){//這里模擬查詢不到
return null;
}
//獲取從數(shù)據(jù)庫查詢出來的用戶密碼
String password = "123";//這里使用靜態(tài)數(shù)據(jù)模擬糖荒。杉辙。
//返回認(rèn)證信息由父類AuthenticatingRealm進(jìn)行認(rèn)證
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
username, password, getName());
return simpleAuthenticationInfo;
}
//授權(quán)
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals) {
// TODO Auto-generated method stub
return null;
}
}
注意,在SimpleAuthenticationInfo中第一個(gè)參數(shù)是Object類型捶朵,即你可以把User對(duì)象存入蜘矢,在后面可以通過SecurityUtils.getSubject().getPrincipal()獲取用戶信息。
認(rèn)證密碼加密
散列算法
一般用于生成一段文本的摘要信息泉孩,散列算法不可逆硼端,將內(nèi)容可以生成摘要,無法將摘要轉(zhuǎn)成原始內(nèi)容寓搬。散列算法常用于對(duì)密碼進(jìn)行散列珍昨,常用的散列算法有MD5、SHA。
一般散列算法需要提供一個(gè)salt(鹽)與原始內(nèi)容生成摘要信息镣典,這樣做的目的是為了安全性兔毙,比如:111111的md5值是:96e79218965eb72c92a549dd5a330112,拿著“96e79218965eb72c92a549dd5a330112”去md5破解網(wǎng)站很容易進(jìn)行破解兄春,如果要是對(duì)111111和salt(鹽澎剥,一個(gè)隨機(jī)數(shù))進(jìn)行散列,這樣雖然密碼都是111111加不同的鹽會(huì)生成不同的散列值赶舆。
代碼如下:
//md5加密哑姚,不加鹽
String password_md5 = new Md5Hash("111111").toString();
System.out.println("md5加密,不加鹽="+password_md5);
//md5加密芜茵,加鹽叙量,一次散列
String password_md5_sale_1 = new Md5Hash("111111", "eteokues", 1).toString();
System.out.println("password_md5_sale_1="+password_md5_sale_1);
String password_md5_sale_2 = new Md5Hash("111111", "uiwueylm", 1).toString();
System.out.println("password_md5_sale_2="+password_md5_sale_2);
//兩次散列相當(dāng)于md5(md5())
//使用SimpleHash
String simpleHash = new SimpleHash("MD5", "111111", "eteokues",1).toString();
System.out.println(simpleHash);
在realm中使用
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token) throws AuthenticationException {
//用戶賬號(hào)
String username = (String) token.getPrincipal();
//根據(jù)用戶賬號(hào)從數(shù)據(jù)庫取出鹽和加密后的值
//..這里使用靜態(tài)數(shù)據(jù)
//如果根據(jù)賬號(hào)沒有找到用戶信息則返回null,shiro拋出異尘糯“賬號(hào)不存在”
//按照固定規(guī)則加密碼結(jié)果 绞佩,此密碼 要在數(shù)據(jù)庫存儲(chǔ),原始密碼 是111111猪钮,鹽是eteokues
String password = "cb571f7bd7a6f73ab004a70322b963d5";
//鹽品山,隨機(jī)數(shù),此隨機(jī)數(shù)也在數(shù)據(jù)庫存儲(chǔ)
String salt = "eteokues";
//返回認(rèn)證信息
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
username, password, ByteSource.Util.bytes(salt),getName());
return simpleAuthenticationInfo;
}
Shiro授權(quán)
授權(quán)流程
授權(quán)方式
Shiro 支持三種方式的授權(quán):
- 編程式:通過寫if/else 授權(quán)代碼塊完成:
Subject subject = SecurityUtils.getSubject();
if(subject.hasRole(“admin”)) {
//有權(quán)限
} else {
//無權(quán)限
}
- 注解式:通過在執(zhí)行的Java方法上放置相應(yīng)的注解完成:
@RequiresRoles("admin")
public void hello() {
//有權(quán)限
}
- JSP/GSP 標(biāo)簽:在JSP/GSP 頁面通過相應(yīng)的標(biāo)簽完成:
<shiro:hasRole name="admin">
<!— 有權(quán)限—>
</shiro:hasRole>
Permission 鑒權(quán)方式
我們了解到了 Shiro 的Authorization有三種方式烤低,作為細(xì)粒度化的 Authorization肘交,Permission 同樣也支持粗粒度的 Authorization 的三種方式即代碼判斷酸些,注解闯第,JSP頁面校驗(yàn)蛛淋。
- 代碼判斷/注解
@RequiresRoles("admin")
@RequiresPermissions("admin:view:*")
@RequestMapping(value="/admin")
public String AuthorizationOne () {
Subject admin =SecurityUtils.getSubject();
System.out.println("角色 " + admin.getPrincipal());
admin.isPermitted("admin:view:*");
System.out.println("角色 " + admin.getPrincipal()+" 是否擁有 admin:view:* 權(quán)限:"+admin.isPermitted("admin:view:*"));
return "admin";
}
- JSP 頁面校驗(yàn)
<!-- 只有 user:view:* 權(quán)限才能顯示一下內(nèi)容 -->
<shiro:hasPermission name="user:view:*">
Only User has 'user:view:*' can access to those words
</shiro:hasPermission>
<br>
<!-- 只有 admin:view:* 權(quán)限才能顯示一下內(nèi)容 -->
<shiro:hasPermission name="admin:view:*">
Only Admin has 'admin:view:*' can access to those words
</shiro:hasPermission>
Shiro Authorization 大致可以被概述為實(shí)現(xiàn)了角色授權(quán)和權(quán)限授權(quán)
- 角色授權(quán):粗粒度授權(quán),為當(dāng)前Subject 做角色判定或賦予
- 權(quán)限授權(quán):細(xì)粒度授權(quán)杨伙,為當(dāng)前Role 做權(quán)限判定或賦予毁菱。
- 在細(xì)粒度授權(quán)時(shí)要重分理解 資源標(biāo)識(shí)符:操作:對(duì)象實(shí)例ID 規(guī)則的定義的應(yīng)用的實(shí)際場景。
權(quán)限字符串規(guī)則
權(quán)限一般是以字符串的形式表示的,權(quán)限字符串的規(guī)則是:“資源標(biāo)識(shí)符:操作:資源實(shí)例標(biāo)識(shí)符”,意思是對(duì)哪個(gè)資源的哪個(gè)實(shí)例具有什么操作,“:”是資源/操作/實(shí)例的分割符吝镣,, 表示操作的分割闸溃,* 表示任意資源/操作/實(shí)例拴测。
如下:
用戶創(chuàng)建權(quán)限:user:create汇跨,或user:create:*
用戶修改實(shí)例001的權(quán)限:user:update:001
用戶實(shí)例001的所有權(quán)限:user:*:001
資源-操作-實(shí)例
資源-操作-實(shí)例 是 Shiro 做細(xì)粒度鑒權(quán) persmission時(shí)的一種規(guī)則毅厚。
- 擴(kuò)展
默認(rèn)支持通配符權(quán)限字符串,: 表示資源/操作/實(shí)例的分割;, 表示操作的分割,* 表示任意資源/操作/實(shí)例动分。 - 單個(gè)權(quán)限
- user:query坟乾、user:edit。
- 冒號(hào)是一個(gè)特殊字符躏鱼,它用來分隔權(quán)限字符串的下一部件:第一部分是權(quán)限被操作的領(lǐng)域,第二部分是被執(zhí)行的操作染苛。
- 多個(gè)值:每個(gè)部件能夠保護(hù)多個(gè)值鹊漠。因此主到,除了授予用戶 user:query和 user:edit 權(quán)限外,也可以簡單地授予他們一個(gè):user:query, edit躯概。
- 還可以用 * 號(hào)代替所有的值登钥,如:user:* , 也可以寫:*:query娶靡,表示某個(gè)用戶在所有的領(lǐng)域都有 query 的權(quán)限牧牢。
- 例子
- 單個(gè)資源多個(gè)權(quán)限 user:query user:add 多值 user:query,add
- 單個(gè)資源所有權(quán)限 user:query,add,update,delete user:*
- 所有資源某個(gè)權(quán)限 *:view
- 實(shí)例級(jí)訪問控制
- 規(guī)則: 資源標(biāo)識(shí)符:操作:對(duì)象實(shí)例 ID
- 這種情況通常會(huì)使用三個(gè)部件:域、操作姿锭、被付諸實(shí)施的實(shí)例塔鳍。如:user:edit:manager
- 也可以使用通配符來定義,如:user:edit:呻此、user::轮纫、user::manager
- 部分省略通配符:缺少的部件意味著用戶可以訪問所有與之匹配的值,比如:user:edit 等價(jià)于 user:edit :焚鲜、
user 等價(jià)于 user: :* - 通配符只能從字符串的結(jié)尾處省略部件危号,也就是說 user:edit 并不等價(jià)于 user:*:edit
- 例子
- 單個(gè)實(shí)例的單個(gè)權(quán)限 printer:query:lp7200 printer:print:epsoncolor
- 對(duì)資源printer的lp7200實(shí)例擁有query權(quán)限。
- 對(duì)資源printer的epsoncolor實(shí)例擁有query權(quán)限快耿。
- 所有實(shí)例的單個(gè)權(quán)限 printer:print:*
- 對(duì)資源printer的所有r實(shí)例擁有query權(quán)限盏袄。
- 所有實(shí)例的所有權(quán)限 printer::
- 對(duì)資源printer的1實(shí)例擁有所有權(quán)限。然后通過如下代碼判斷
subject().checkPermissions(“printer:setting:1”, “printer:printe:2”);
- 對(duì)資源printer的1實(shí)例擁有所有權(quán)限。然后通過如下代碼判斷
- 單個(gè)實(shí)例的所有權(quán)限 printer:*:lp7200
- 對(duì)資源printer的lp7200實(shí)例擁有所有權(quán)限
- 單個(gè)實(shí)例的多個(gè)權(quán)限 printer:query,print:lp7200
- 對(duì)資源printer的lp7200實(shí)例擁有query,print權(quán)限
- 單個(gè)實(shí)例的單個(gè)權(quán)限 printer:query:lp7200 printer:print:epsoncolor
基于角色的授權(quán)
// 用戶授權(quán)檢測 基于角色授權(quán)
// 是否有某一個(gè)角色
System.out.println("用戶是否擁有一個(gè)角色:" + subject.hasRole("role1"));
// 是否有多個(gè)角色
System.out.println("用戶是否擁有多個(gè)角色:" + subject.hasAllRoles(Arrays.asList("role1", "role2")));
對(duì)應(yīng)的check方法:
subject.checkRole("role1");
subject.checkRoles(Arrays.asList("role1", "role2"));
基于資源授權(quán)
// 基于資源授權(quán)
System.out.println("是否擁有某一個(gè)權(quán)限:" + subject.isPermitted("user:delete"));
System.out.println("是否擁有多個(gè)權(quán)限:" + subject.isPermittedAll("user:create:1", "user:delete"));
對(duì)應(yīng)的check方法:
subject.checkPermission("sys:user:delete");
subject.checkPermissions("user:create:1","user:delete");
自定義realm
與上邊認(rèn)證自定義realm一樣贝乎,大部分情況是要從數(shù)據(jù)庫獲取權(quán)限數(shù)據(jù)情连,這里直接實(shí)現(xiàn)基于資源的授權(quán)。
在認(rèn)證章節(jié)寫的自定義realm類中完善doGetAuthorizationInfo方法览效,此方法需要完成:根據(jù)用戶身份信息從數(shù)據(jù)庫查詢權(quán)限字符串却舀,由shiro進(jìn)行授權(quán)。
// 授權(quán)
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals) {
// 獲取身份信息
String username = (String) principals.getPrimaryPrincipal();
// 根據(jù)身份信息從數(shù)據(jù)庫中查詢權(quán)限數(shù)據(jù)
//....這里使用靜態(tài)數(shù)據(jù)模擬
List<String> permissions = new ArrayList<String>();
permissions.add("user:create");
permissions.add("user.delete");
//將權(quán)限信息封閉為AuthorizationInfo
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
for(String permission:permissions){
simpleAuthorizationInfo.addStringPermission(permission);
}
return simpleAuthorizationInfo;
}
自定義permission和RolePerminssion
通過addStringPermission 默認(rèn)是用Permission的實(shí)現(xiàn)類封裝的 當(dāng)然也可以實(shí)現(xiàn)自定義的Permission锤灿。
public class MyPermission implements Permission {
String permissionCode;
public MyPermission(String name) {
permissionCode=name;
}
@Override
public boolean implies(Permission permission) {
//自定義比較
// TODO Auto-generated method stub
return false;
}
}
當(dāng)我們調(diào)用subject.isPermitted("user:update")會(huì)調(diào)用將指令傳達(dá)給SecurityManager挽拔,SecurityManager 再將指令傳達(dá)給授權(quán)管理類Authorizer,Authorizer會(huì)通過reaml獲得授權(quán)信息SimpleAuthorizationInfo如果我們返回的授權(quán)信息擁有角色 會(huì)調(diào)用RolePermissionResolver實(shí)現(xiàn)類的方法 將角色的權(quán)限追加到SimpleAuthorizationInfo(默認(rèn)是沒有實(shí)現(xiàn)的)但校。
public class MyRolePermissionResolver implements RolePermissionResolver{
@Override
public Collection<Permission> resolvePermissionsInRole(String roleString) {
// TODO Auto-generated method stub
return Arrays.asList((Permission)new MyPermission("menu:*"));
}
這里面是根據(jù)角色查詢權(quán)限
最終 遍歷SimpleAuthorizationInfo的權(quán)限信息 (我們的權(quán)限信息都封裝Permission接口實(shí)現(xiàn)類 調(diào)用implies方法進(jìn)行比較 如果比較成功返回true 表示授權(quán)通過)自定義Permission的好處就是我們可以自定義匹配規(guī)則螃诅。
@RequiresPermissions 注解說明
@RequiresAuthentication
驗(yàn)證用戶是否登錄,等同于方法subject.isAuthenticated() 結(jié)果為true時(shí)状囱。
@RequiresUser
驗(yàn)證用戶是否被記憶术裸,user有兩種含義:
一種是成功登錄的(subject.isAuthenticated() 結(jié)果為true);
另外一種是被記憶的(subject.isRemembered()結(jié)果為true)亭枷。
@RequiresGuest
驗(yàn)證是否是一個(gè)guest的請(qǐng)求袭艺,與@RequiresUser完全相反。
換言之叨粘,RequiresUser == !RequiresGuest猾编。
此時(shí)subject.getPrincipal() 結(jié)果為null.
@RequiresRoles
例如:@RequiresRoles("aRoleName");
void someMethod();
如果subject中有aRoleName角色才可以訪問方法someMethod瘤睹。如果沒有這個(gè)權(quán)限則會(huì)拋出異常AuthorizationException。
@RequiresPermissions
例如: @RequiresPermissions({"file:read", "write:aFile.txt"} )
void someMethod();
要求subject中必須同時(shí)含有file:read和write:aFile.txt的權(quán)限才能執(zhí)行方法someMethod()答倡。否則拋出異常AuthorizationException轰传。