授權即訪問控制,它將判斷用戶在應用程序中對資源是否擁有相應的訪問權限。
如,判斷一個用戶有查看頁面的權限,編輯數據的權限,擁有某一按鈕的權限等等。
一、用戶權限模型
為實現一個較為靈活的用戶權限數據模型,通常把用戶信息單獨用一個實體表示,用戶權限信息用兩個實體表示。
- 用戶信息用 LoginAccount 表示,最簡單的用戶信息可能只包含用戶名 loginName 及密碼 password 兩個屬性。實際應用中可能會包含用戶是否被禁用,用戶信息是否過期等信息。
- 用戶權限信息用 Role 與 Permission 表示,Role 與 Permission 之間構成多對多關系。Permission 可以理解為對一個資源的操作,Role 可以簡單理解為 Permission 的集合。
- 用戶信息與 Role 之間構成多對多關系。表示同一個用戶可以擁有多個 Role,一個 Role 可以被多個用戶所擁有。

權限聲明及粒度
Shiro權限聲明通常是使用以冒號分隔的表達式。就像前文所講,一個權限表達式可以清晰的指定資源類型,允許的操作。同時,Shiro權限表達式支持簡單的通配符,可以更加靈活的進行權限設置。
下面以實例來說明權限表達式。
可查詢用戶數據
User:view
可查詢或編輯用戶數據
User:view,edit
可對用戶數據進行所有操作
User:*或 user
可編輯id為123的用戶數據
User:edit:123
授權處理過程
認證通過后接受 Shiro 授權檢查,授權驗證時,需要判斷當前角色是否擁有該權限。
只有授權通過,才可以訪問受保護 URL 對應的資源,否則跳轉到“未經授權頁面”。
如果我們自定義Realm實現,比如我后面的例子中,自定義了ShiroDbRealm類,當訪問被@RequiresPermissions注解的方法時,會先執行ShiroDbRealm.doGetAuthorizationInfo()進行授權。
- <span style="font-size:18px">@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);
- }</span>
二、授權實現
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標簽:
- <%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
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>
三、Shiro授權的內部處理機制

1、在應用程序中調用授權驗證方法(Subject的isPermitted*或hasRole*等)
2、Sbuject會委托應用程序設置的securityManager實例調用相應的isPermitted*或hasRole*方法。
3、接下來SecurityManager會委托內置的Authorizer的實例(默認是ModularRealmAuthorizer類的實例,類似認證實例)調用相應的授權方法。
4、每一個Realm將檢查是否實現了相同的Authorizer 接口。然后,將調用Reaml自己的相應的授權驗證方法。
四、授權代碼
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驗證用戶授權的類
- <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;
- }
- /**
- * 為當前登錄的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;
- }
- }</span>
