一、shiro授權基礎概念
(1)基於角色的訪問控制
“資源標識符:操作:對象實例ID” 即對哪個資源的哪個實例可以進行什麼操作。其默認支持通配符權限字符串,“:”表示資源/操作/實例的分割;“,”表示操作的分割;“*”表示任意資源/操作/實例。
二、shiro鑑權實例
前提:
緊接着上一篇文章shiro學習和使用實例(2)——登陸認證和授權,登陸認證通過後,清除掉原來的權限緩存,加上當前登陸賬號的最新權限。接着就是登陸成功跳轉到index(首頁),此時,我們要對當前登陸賬號鑑權。
思路:
賬號登陸成功跳轉到首頁時,我們要對當前賬號鑑權,通過判斷賬號是否具有某項操作的權限,來決定是否對當前登陸賬號顯示該操作的頁面菜單、按鈕或鏈接。當我們加載首頁的時候,通過js將頁面所有菜單的標識、權限發送到服務端,服務端調用shiro的isPermitted(String permission)從緩存中獲取當前賬號的權限信息(登陸成功後已將當前賬號的最新權限信息放到緩存中了),如果緩存中沒有,shiro會遠程從數據庫中獲取。isPermitted(String permission)把從緩存中獲取的權限和頁面發送來的權限進行比較,將有無權限的結果返回到頁面,通過回調函數決定菜單是否顯示(有權限顯示,無權限不顯示)。
這是一種方案,也是我們接下來實例的內容。其實,還有粒度更小的權限限制方案,即對每個請求的方法加上權限判斷,登陸賬號具有這個權限,就可以請求此方法,得到響應,否則拒絕該請求。這個可以用shiro提供的註解來實現鑑權。
(1)、頁面請求
首先要給所有的頁面菜單唯一的資源標識,然後就是該菜單的操作(veiw,insert,update,delete等),這樣就滿足了shiro的權限格式。例如系統中有“基礎數據管理”這個菜單,則“com.isoftstone.yyp.portal.om.basic.data:veiw”。注意,這裏的資源標識和操作要和授權時,加載到緩存中的權限要一致,也就是說,數據庫保存的權限信息也必須是這個格式。
下面我們來看頁面js的代碼:
- $.permissionFilter(
- [
- {
- permission : "view",
- depBundle_SymbolicName : "com.isoftstone.yyp.portal.om.member.role",
- display : function(status) {
- if (status == 0) {//0:有權限,1:無權限
- $("#hymember,#member_role").show();
- permission.memberPermissoin = true;
- } else {
- $("#member_role").remove();
- }
- }
- },
- {
- permission : "view",
- depBundle_SymbolicName : "com.isoftstone.yyp.portal.om.member.query",
- display : function(status) {
- if (status == 0) {//0:有權限,1:無權限
- $("#hymember,#member_query").show();
- permission.memberPermissoin = true;
- } else {
- $("#member_query").remove();
- }
- }
- }
- ], "",
- "../../mvc/oBtnLinkController/getPermissions.json");
depBundle_SymbolicName:資源標識
display:回調函數,用於菜單顯示
這裏只保留了兩個菜單的權限信息,如果有更多,按這種格式加上接下來是js權限公共方法:
- (function($){
- var options = {
- guid : function(){
- var guid = new Date().getTime(), i;
- for (i = 0; i < 5; i++) {
- guid += Math.floor(Math.random() * 65535);
- }
- return guid ;
- },
- display : function(status,obj,o){ //默認處理邏輯 針對界面的CRUD按鈕進行操作,其中R(查詢)按鈕對應類型FORM,由於框架一定會生成,並且查詢和菜單權限綁定,暫時不作處理
- switch(obj.type){
- case 'ADDBUTTON':
- if(status == 1){
- o.grid.addable = false;
- }else if(status == 0){
- o.grid.addable = true;
- }
- break;
- case 'EDITBUTTON':
- if(status == 1){
- o.grid.updateable = false;
- }else if(status == 0){
- o.grid.updateable = true;
- }
- break;
- case 'REMOVEBUTTON':
- if(status == 1 ){
- o.grid.deleteable = false;
- }else if(status == 0){
- o.grid.deleteable = true;
- }
- break;
- case 'FORM':
- if(status == '1'){
- //$("."+obj.id+"").remove();
- }
- break;
- }
- }
- };
- /**
- * 限定三個參數:
- * 權限控制數組、
- * 本bundle的SymbolicNames、
- * 傳遞給CRUD框架參數(可選)
- * 其中權限控制數組對象格式爲
- * {permission:"",type:"",depBundle_SymbolicName:"",display:function:{}}
- * type和display 選擇其一
- * depBundle_SymbolicName爲空,則默認爲 方法第二個參數【本bundle的SymbolicNames】
- */
- $.permissionFilter = function(){
- var elements = null;
- if((arguments.length == 3 ||arguments.length == 2) && typeof arguments[0] == "object"){
- elements = arguments[0];
- }else{
- return;
- }
- var belongbundle_SymbolicName = arguments[1];//權限所屬於模板標識,單個
- var url = arguments[2];//傳遞給CRUD框架參數
- var permissions = "";//權限標識,
- var bundle_SymbolicNames = "";//權限所屬於模板標識,數組,
- var ids = ""; //
- $.each(elements,function(i,e){
- if(e){
- permissions += (e.permission?e.permission:"nopermission") + ",";
- e.id = (e.id?e.id:options.guid());
- ids += e.id + ",";
- bundle_SymbolicNames += (e.depBundle_SymbolicName?e.depBundle_SymbolicName:belongbundle_SymbolicName) + ",";
- }
- });
- if(!url){
- url="../../mvc/btnLinkController/getPermissions.json";
- }
- permissions = permissions.substr(0,permissions.length-1);
- ids = ids.substr(0,ids.length-1);
- bundle_SymbolicNames = bundle_SymbolicNames.substr(0,bundle_SymbolicNames.length-1);
- $.ajax({
- url:url,
- dataType:"json",
- data:{"permissions":permissions,"bundle_SymbolicNames":bundle_SymbolicNames,"belongbundle_SymbolicName":belongbundle_SymbolicName,"ids":ids},
- async:false,
- type:'post',
- success:function(result){
- for(var j =0; j<result.length; j++){
- var element = result[j];
- for(var i=0; i<elements.length; i++){
- if(elements[i].id == element.id){
- if(elements[i].display){//沒有定義type類型,就必須定義display方法
- elements[i].display(element.display);
- }else{
- //options.display(element.display,elements[i],optionsObj)
- }
- break;
- }
- }
- }
- }
- });
- }
- })(jQuery);
(2)服務端鑑權
先上代碼,一看就明白
- @Override
- ublic List<HTMLElement> getPermissions(List<String> permissions,String []bundle_SymbolicNames,String belongbundle_SymbolicName,List<String> ids) {
- if(permissions == null || bundle_SymbolicNames == null){
- return null;
- }
- if(bundle_SymbolicNames.length != permissions.size() ||( permissions.size() != ids.size())){
- return null;
- }
- List<HTMLElement> elements = new ArrayList<HTMLElement>();
- Subject currentUser = SecurityUtils.getSubject();
- for(int i=0; i<permissions.size(); i++){
- boolean exist = false;
- String permissionName = bundle_SymbolicNames[i].concat(":").concat(permissions.get(i));
- //驗證是否有權限
- if(currentUser.isPermitted(permissionName)){
- exist = true;
- HTMLElement element = new HTMLElement(ids.get(i),0);
- elements.add(element);
- }
- if(!exist){
- HTMLElement element = new HTMLElement(ids.get(i),1);
- elements.add(element);
- }
- }
- return elements;
這裏要重點說明一下shiro的isPermitted方法。
- public boolean isPermitted(PrincipalCollection principals, String permission) {
- Permission p = getPermissionResolver().resolvePermission(permission);
- return isPermitted(principals, p);
- }
- public boolean isPermitted(PrincipalCollection principals, Permission permission) {
- AuthorizationInfo info = getAuthorizationInfo(principals);
- return isPermitted(permission, info);
- }
- protected AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals) {
- if (principals == null) {
- return null;
- }
- AuthorizationInfo info = null;
- if (log.isTraceEnabled()) {
- log.trace("Retrieving AuthorizationInfo for principals [" + principals + "]");
- }
- Cache<Object, AuthorizationInfo> cache = getAvailableAuthorizationCache();
- if (cache != null) {
- if (log.isTraceEnabled()) {
- log.trace("Attempting to retrieve the AuthorizationInfo from cache.");
- }
- Object key = getAuthorizationCacheKey(principals);
- info = cache.get(key);
- if (log.isTraceEnabled()) {
- if (info == null) {
- log.trace("No AuthorizationInfo found in cache for principals [" + principals + "]");
- } else {
- log.trace("AuthorizationInfo found in cache for principals [" + principals + "]");
- }
- }
- }
- if (info == null) {
- // Call template method if the info was not found in a cache
- info = doGetAuthorizationInfo(principals);
- // If the info is not null and the cache has been created, then cache the authorization info.
- if (info != null && cache != null) {
- if (log.isTraceEnabled()) {
- log.trace("Caching authorization info for principals: [" + principals + "].");
- }
- Object key = getAuthorizationCacheKey(principals);
- cache.put(key, info);
- }
- }
- return info;
- }