將 Shiro 做爲應用的權限基礎 三:基於註解實現的受權認證過程

受權即訪問控制,它將判斷用戶在應用程序中對資源是否擁有相應的訪問權限。 html

如,判斷一個用戶有查看頁面的權限,編輯數據的權限,擁有某一按鈕的權限等等。 java

 

1、用戶權限模型web

爲實現一個較爲靈活的用戶權限數據模型,一般把用戶信息單獨用一個實體表示,用戶權限信息用兩個實體表示。spring

  1. 用戶信息用 LoginAccount 表示,最簡單的用戶信息可能只包含用戶名 loginName 及密碼 password 兩個屬性。實際應用中可能會包含用戶是否被禁用,用戶信息是否過時等信息。
  2. 用戶權限信息用 Role 與 Permission 表示,Role 與 Permission 之間構成多對多關係。Permission 能夠理解爲對一個資源的操做,Role 能夠簡單理解爲 Permission 的集合。
  3. 用戶信息與 Role 之間構成多對多關係。表示同一個用戶能夠擁有多個 Role,一個 Role 能夠被多個用戶所擁有。

 


 

權限聲明及粒度 數據庫

Shiro權限聲明一般是使用以冒號分隔的表達式。就像前文所講,一個權限表達式能夠清晰的指定資源類型,容許的操做。同時,Shiro權限表達式支持簡單的通配符,能夠更加靈活的進行權限設置。 apache

下面以實例來講明權限表達式。 緩存

可查詢用戶數據 app

User:view jsp

可查詢或編輯用戶數據 測試

User:view,edit 

可對用戶數據進行全部操做 

User:*或 user 

可編輯id爲123的用戶數據 

User:edit:123 

 

受權處理過程

認證經過後接受 Shiro 受權檢查,受權驗證時,須要判斷當前角色是否擁有該權限。

只有受權經過,才能夠訪問受保護 URL 對應的資源,不然跳轉到「未經受權頁面」。

若是咱們自定義Realm實現,好比我後面的例子中,自定義了ShiroDbRealm類,當訪問ShiroDbRealm.doGetAuthorizationInfo()進行受權。

@Controller
@RequestMapping(value = "/user")
public class UserController {
 
@Resource(name="userService")
private IUserService userService;
 
/**
 * 測試權限
 * 只有擁有 user:create權限,才能進行註冊
 * @param user
 * @return
 */
@RequestMapping(value = "/register")
@ResponseBody
@RequiresPermissions("user:create")
public boolean register(User user){
return userService.register(user);
}



 

 

2、受權實現 

Shiro支持三種方式實現受權過程: 

  • 編碼實現
  • 註解實現
  • JSP Taglig實現

 

1、基於編碼的受權實現 

1、基於權限對象的實現 

建立org.apache.shiro.authz.Permission的實例,將該實例對象做爲參數傳遞給Subject.isPermitted()進行驗證。 

      

Permission printPermission = new PrinterPermission("laserjet4400n", "print");  
Subject currentUser = SecurityUtils.getSubject();  
if (currentUser.isPermitted(printPermission)) {  
    //show the Print button  
} else {  
    //don't show the button?  Grey it out?  
}  

 

 

2基於字符串的實現 

相比笨重的基於對象的實現方式,基於字符串的實現便顯得更加簡潔。 

       

Subject currentUser = SecurityUtils.getSubject();  
if (currentUser.isPermitted("printer:print:laserjet4400n")) {  
    //show the Print button  
} else {  
    //don't show the button?  Grey it out?  
}  

 

使用冒號分隔的權限表達式是org.apache.shiro.authz.permission.WildcardPermission默認支持的實現方式。 

這裏分別表明了資源類型:操做:資源ID 

 

2、基於註解的受權實現 

Shiro註解支持AspectJ、Spring、Google-Guice等,可根據應用進行不一樣的配置。 

 

相關的註解: 

@RequiresAuthentication 

能夠用戶類/屬性/方法,用於代表當前用戶需是通過認證的用戶。  

        

@RequiresAuthentication  
public void updateAccount(Account userAccount) {  
    //this method will only be invoked by a   
    //Subject that is guaranteed authenticated  
    ...  
} 

 

@RequiresPermissions 

當前用戶需擁有制定權限 

       

@RequiresPermissions("account:create")  
public void createAccount(Account account) {  
    //this method will only be invoked by a Subject  
    //that is permitted to create an account  
    ...  
} 


 

 

3、基於JSP TAG的受權實現 

Shiro提供了一套JSP標籤庫來實現頁面級的受權控制。 

在使用Shiro標籤庫前,首先須要在JSP引入shiro標籤: 


hasRole標籤 

驗證當前用戶是否屬於該角色

 

<shiro:hasRole name="administrator">  
    <a href="admin.jsp">Administer the system</a>  
</shiro:hasRole>  

 

hasPermission標籤 

驗證當前用戶是否擁有制定權限 

 

<shiro:hasPermission name="user:create">  
    <a href="createUser.jsp">Create a new User</a>  
</shiro:hasPermission>  



 

3、Shiro受權的內部處理機制 


 

一、在應用程序中調用受權驗證方法(Subject的isPermitted*或hasRole*等) 

二、Sbuject會委託應用程序設置的securityManager實例調用相應的isPermitted*或hasRole*方法。 

三、接下來SecurityManager會委託內置的Authorizer的實例(默認是ModularRealmAuthorizer類的實例,相似認證明例)調用相應的受權方法。 

四、每個Realm將檢查是否實現了相同的Authorizer 接口。而後,將調用Reaml本身的相應的受權驗證方法。 

 

4、受權代碼

UserController:處理用戶登陸後的請求(註冊)

    

		package org.shiro.demo.controller;
		
		import javax.annotation.Resource;
		
		import org.apache.shiro.authz.annotation.RequiresPermissions;
		import org.apache.shiro.authz.annotation.RequiresRoles;
		import org.shiro.demo.entity.User;
		import org.shiro.demo.service.IUserService;
		import org.springframework.stereotype.Controller;
		import org.springframework.web.bind.annotation.RequestMapping;
		import org.springframework.web.bind.annotation.ResponseBody;
		
		@Controller
		@RequestMapping(value = "/user")
		public class UserController {
			
			@Resource(name="userService")
			private IUserService userService;
		
			/**
			 * 測試權限
			 * 只有擁有 user:create 權限,才能進行註冊
			 * @param user
			 * @return
			 */
			@RequestMapping(value = "/register")
			@ResponseBody
			@RequiresPermissions("user:create")
			public boolean register(User user){
				return userService.register(user);
			}
			
			/**
			 * 測試角色
			 * 只有擁有 administrator 角色,才能跳轉到register頁面
			 * @return
			 */
			@RequestMapping(value = "/toRegister")
			@RequiresRoles("administrator")
			public String toRegister(){
				return "/system/user/register";
			}
		}

 

 

ShiroDbRealm:自定義的指定Shiro驗證用戶受權的類

packageorg.shiro.demo.service.realm;
 
importjava.util.ArrayList;
importjava.util.List;
 
importjavax.annotation.Resource;
 
importorg.apache.commons.lang.StringUtils;
importorg.apache.shiro.authc.AuthenticationException;
importorg.apache.shiro.authc.AuthenticationInfo;
importorg.apache.shiro.authc.AuthenticationToken;
importorg.apache.shiro.authc.SimpleAuthenticationInfo;
importorg.apache.shiro.authc.UsernamePasswordToken;
importorg.apache.shiro.authz.AuthorizationException;
importorg.apache.shiro.authz.AuthorizationInfo;
importorg.apache.shiro.authz.SimpleAuthorizationInfo;
importorg.apache.shiro.realm.AuthorizingRealm;
importorg.apache.shiro.subject.PrincipalCollection;
importorg.shiro.demo.entity.Permission;
importorg.shiro.demo.entity.Role;
importorg.shiro.demo.entity.User;
importorg.shiro.demo.service.IUserService;
 
/**
 * 自定義的指定Shiro驗證用戶登陸的類
 * @author TCH
 *
 */
publicclass ShiroDbRealm extends AuthorizingRealm{
 
//@Resource(name="userService")
privateIUserService userService;
 
publicvoid setUserService(IUserService userService) {
this.userService= userService;
}
 
    /**
     * 爲當前登陸的Subject授予角色和權限
     * @see 經測試:本例中該方法的調用時機爲需受權資源被訪問時
     * @see經測試:而且每次訪問需受權資源時都會執行該方法中的邏輯,這代表本例未啓用AuthorizationCache
     * @seeweb層能夠有shiro的緩存,dao層能夠配有hibernate的緩存(後面介紹)
     */
protectedAuthorizationInfo doGetAuthorizationInfo(
PrincipalCollectionprincipals) {
 
//獲取當前登陸的用戶名,等價於(String)principals.fromRealm(this.getName()).iterator().next() 
Stringaccount = (String) super.getAvailablePrincipal(principals);
 
List<String>roles = new ArrayList<String>(); 
List<String>permissions = new ArrayList<String>();
 
//從數據庫中獲取當前登陸用戶的詳細信息 
Useruser = userService.getByAccount(account);
 
if(user!= null){
//實體類User中包含有用戶角色的實體類信息 
if(user.getRoles() != null && user.getRoles().size() > 0) {
//獲取當前登陸用戶的角色
for(Role role : user.getRoles()) {
roles.add(role.getName());
 //實體類Role中包含有角色權限的實體類信息 
if(role.getPmss() != null && role.getPmss().size() > 0) {
 //獲取權限 
for(Permission pmss : role.getPmss()) {
if(!StringUtils.isEmpty(pmss.getPermission())){
permissions.add(pmss.getPermission());
}
}
}
}
}
}else{
thrownew AuthorizationException();
}
 
//爲當前用戶設置角色和權限
SimpleAuthorizationInfoinfo = new SimpleAuthorizationInfo();
info.addRoles(roles);
        info.addStringPermissions(permissions);
       
returninfo;
 
}
 
}
相關文章
相關標籤/搜索