授權(quán)即訪問控制,,它將判斷用戶在應(yīng)用程序中對資源是否擁有相應(yīng)的訪問權(quán)限,。
如,判斷一個用戶有查看頁面的權(quán)限,,編輯數(shù)據(jù)的權(quán)限,,擁有某一按鈕的權(quán)限等等。
一,、用戶權(quán)限模型
為實現(xiàn)一個較為靈活的用戶權(quán)限數(shù)據(jù)模型,,通常把用戶信息單獨用一個實體表示,用戶權(quán)限信息用兩個實體表示,。
-
用戶信息用 LoginAccount 表示,,最簡單的用戶信息可能只包含用戶名 loginName 及密碼 password 兩個屬性。實際應(yīng)用中可能會包含用戶是否被禁用,,用戶信息是否過期等信息,。
- 用戶權(quán)限信息用 Role 與 Permission 表示,Role 與 Permission 之間構(gòu)成多對多關(guān)系,。Permission 可以理解為對一個資源的操作,,Role 可以簡單理解為 Permission 的集合。
- 用戶信息與 Role 之間構(gòu)成多對多關(guān)系,。表示同一個用戶可以擁有多個 Role,,一個 Role 可以被多個用戶所擁有,。
權(quán)限聲明及粒度
Shiro權(quán)限聲明通常是使用以冒號分隔的表達(dá)式。就像前文所講,,一個權(quán)限表達(dá)式可以清晰的指定資源類型,,允許的操作,。同時,Shiro權(quán)限表達(dá)式支持簡單的通配符,,可以更加靈活的進行權(quán)限設(shè)置,。
下面以實例來說明權(quán)限表達(dá)式。
可查詢用戶數(shù)據(jù)
User:view
可查詢或編輯用戶數(shù)據(jù)
User:view,edit
可對用戶數(shù)據(jù)進行所有操作
User:*或 user
可編輯id為123的用戶數(shù)據(jù)
User:edit:123
授權(quán)處理過程
認(rèn)證通過后接受 Shiro 授權(quán)檢查,,授權(quán)驗證時,,需要判斷當(dāng)前角色是否擁有該權(quán)限。
只有授權(quán)通過,,才可以訪問受保護 URL 對應(yīng)的資源,,否則跳轉(zhuǎn)到“未經(jīng)授權(quán)頁面”。
如果我們自定義Realm實現(xiàn),,比如我后面的例子中,,自定義了ShiroDbRealm類,,當(dāng)訪問被@RequiresPermissions注解的方法時,會先執(zhí)行ShiroDbRealm.doGetAuthorizationInfo()進行授權(quán),。
- <span style="font-size:18px">@Controller
- @RequestMapping(value = "/user")
- public class UserController {
-
- @Resource(name="userService")
- private IUserService userService;
-
- /**
- * 測試權(quán)限
- * 只有擁有 user:create權(quán)限,,才能進行注冊
- * @param user
- * @return
- */
- @RequestMapping(value = "/register")
- @ResponseBody
- @RequiresPermissions("user:create")
- public boolean register(User user){
- return userService.register(user);
- }</span>
二、授權(quán)實現(xiàn)
Shiro支持三種方式實現(xiàn)授權(quán)過程:
- 編碼實現(xiàn)
- 注解實現(xiàn)
- JSP Taglig實現(xiàn)
1,、基于編碼的授權(quán)實現(xiàn)
1,、基于權(quán)限對象的實現(xiàn)
創(chuàng)建org.apache.shiro.authz.Permission的實例,將該實例對象作為參數(shù)傳遞給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,、基于字符串的實現(xiàn)
相比笨重的基于對象的實現(xiàn)方式,基于字符串的實現(xiàn)便顯得更加簡潔,。
- Subject currentUser = SecurityUtils.getSubject();
- if (currentUser.isPermitted("printer:print:laserjet4400n")) {
- //show the Print button
- } else {
- //don't show the button? Grey it out?
- }
使用冒號分隔的權(quán)限表達(dá)式是org.apache.shiro.authz.permission.WildcardPermission默認(rèn)支持的實現(xiàn)方式,。
這里分別代表了資源類型:操作:資源ID
2、基于注解的授權(quán)實現(xiàn)
Shiro注解支持AspectJ,、spring,、Google-Guice等,可根據(jù)應(yīng)用進行不同的配置,。
相關(guān)的注解:
@RequiresAuthentication
可以用戶類/屬性/方法,,用于表明當(dāng)前用戶需是經(jīng)過認(rèn)證的用戶。
- @RequiresAuthentication
- public void updateAccount(Account userAccount) {
- //this method will only be invoked by a
- //Subject that is guaranteed authenticated
- ...
- }
@RequiresPermissions
當(dāng)前用戶需擁有制定權(quán)限
- @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的授權(quán)實現(xiàn)
Shiro提供了一套JSP標(biāo)簽庫來實現(xiàn)頁面級的授權(quán)控制,。
在使用Shiro標(biāo)簽庫前,首先需要在JSP引入shiro標(biāo)簽:
- <%@ taglib prefix="shiro" uri="http://shiro./tags" %>
hasRole標(biāo)簽
驗證當(dāng)前用戶是否屬于該角色
- <shiro:hasRole name="administrator">
- <a href="admin.jsp">Administer the system</a>
- </shiro:hasRole>
hasPermission標(biāo)簽
驗證當(dāng)前用戶是否擁有制定權(quán)限
- <shiro:hasPermission name="user:create">
- <a href="createUser.jsp">Create a new User</a>
- </shiro:hasPermission>
三,、Shiro授權(quán)的內(nèi)部處理機制
1,、在應(yīng)用程序中調(diào)用授權(quán)驗證方法(Subject的isPermitted*或hasRole*等)
2、Sbuject會委托應(yīng)用程序設(shè)置的securityManager實例調(diào)用相應(yīng)的isPermitted*或hasRole*方法,。
3,、接下來SecurityManager會委托內(nèi)置的Authorizer的實例(默認(rèn)是ModularRealmAuthorizer類的實例,類似認(rèn)證實例)調(diào)用相應(yīng)的授權(quán)方法,。
4,、每一個Realm將檢查是否實現(xiàn)了相同的Authorizer 接口。然后,,將調(diào)用Reaml自己的相應(yīng)的授權(quán)驗證方法,。
四、授權(quán)代碼
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;
-
- /**
- * 測試權(quán)限
- * 只有擁有 user:create 權(quán)限,,才能進行注冊
- * @param user
- * @return
- */
- @RequestMapping(value = "/register")
- @ResponseBody
- @RequiresPermissions("user:create")
- public boolean register(User user){
- return userService.register(user);
- }
-
- /**
- * 測試角色
- * 只有擁有 administrator 角色,,才能跳轉(zhuǎn)到register頁面
- * @return
- */
- @RequestMapping(value = "/toRegister")
- @RequiresRoles("administrator")
- public String toRegister(){
- return "/system/user/register";
- }
- }
ShiroDbRealm:自定義的指定Shiro驗證用戶授權(quán)的類
- <span style="font-size:18px">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;
- }
-
- /**
- * 為當(dāng)前登錄的Subject授予角色和權(quán)限
- * @see 經(jīng)測試:本例中該方法的調(diào)用時機為需授權(quán)資源被訪問時
- * @see經(jīng)測試:并且每次訪問需授權(quán)資源時都會執(zhí)行該方法中的邏輯,這表明本例未啟用AuthorizationCache
- * @seeweb層可以有shiro的緩存,dao層可以配有hibernate的緩存(后面介紹)
- */
- protectedAuthorizationInfo doGetAuthorizationInfo(
- PrincipalCollectionprincipals) {
-
- //獲取當(dāng)前登錄的用戶名,等價于(String)principals.fromRealm(this.getName()).iterator().next()
- Stringaccount = (String) super.getAvailablePrincipal(principals);
-
- List<String>roles = new ArrayList<String>();
- List<String>permissions = new ArrayList<String>();
-
- //從數(shù)據(jù)庫中獲取當(dāng)前登錄用戶的詳細(xì)信息
- Useruser = userService.getByAccount(account);
-
- if(user!= null){
- //實體類User中包含有用戶角色的實體類信息
- if(user.getRoles() != null && user.getRoles().size() > 0) {
- //獲取當(dāng)前登錄用戶的角色
- for(Role role : user.getRoles()) {
- roles.add(role.getName());
- //實體類Role中包含有角色權(quán)限的實體類信息
- if(role.getPmss() != null && role.getPmss().size() > 0) {
- //獲取權(quán)限
- for(Permission pmss : role.getPmss()) {
- if(!StringUtils.isEmpty(pmss.getPermission())){
- permissions.add(pmss.getPermission());
- }
- }
- }
- }
- }
- }else{
- thrownew AuthorizationException();
- }
-
- //為當(dāng)前用戶設(shè)置角色和權(quán)限
- SimpleAuthorizationInfoinfo = new SimpleAuthorizationInfo();
- info.addRoles(roles);
- info.addStringPermissions(permissions);
-
- returninfo;
-
- }
-
- }</span>
Shiro學(xué)習(xí)的系列文章
將 Shiro 作為應(yīng)用的權(quán)限基礎(chǔ) 一:shiro的整體架構(gòu)
將 Shiro 作為應(yīng)用的權(quán)限基礎(chǔ) 二:shiro認(rèn)證
將 Shiro 作為應(yīng)用的權(quán)限基礎(chǔ) 三:shiro授權(quán)
將 Shiro 作為應(yīng)用的權(quán)限基礎(chǔ) 四:配置說明
將 Shiro 作為應(yīng)用的權(quán)限基礎(chǔ) 五:SpringMVC+Apache
Shiro+JPA(hibernate)整合配置
將 Shiro 作為應(yīng)用的權(quán)限基礎(chǔ) 六:源碼以及下載地址
|