受權即訪問控制,它將判斷用戶在應用程序中對資源是否擁有相應的訪問權限。 html
如,判斷一個用戶有查看頁面的權限,編輯數據的權限,擁有某一按鈕的權限等等。 java
1、用戶權限模型web
爲實現一個較爲靈活的用戶權限數據模型,一般把用戶信息單獨用一個實體表示,用戶權限信息用兩個實體表示。spring
權限聲明及粒度 數據庫
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支持三種方式實現受權過程:
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;
}
}