1. 程式人生 > >Shiro(4)預設鑑權與自定義鑑權

Shiro(4)預設鑑權與自定義鑑權

=========預設鑑權========

過濾鏈中定義:

<!-- 過濾鏈定義 -->
		<property name="filterChainDefinitions">
			<value>
				...
				/pages/User/create.do* = perms[User:create]
				...
			</value>
		</property>

這段配置的含義是:/pages/User/create.do*這樣的請求路徑,需要鑑權,且需要使用者有“User:create”的許可權字串。

perms是攔截器的名字,預設實現類是:org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter

這個過濾器會得到配置中請求路徑對應的許可權字串,如“User:create”,然後到realm中查詢當前使用者包含的許可權,具體來說是呼叫reaml的回撥函式:

	/**
	 * 鑑權回撥函式,提取當事人的角色和許可權
	 * principals  當事人
	 */
    protected AuthorizationInfo doGetAuthorizationInfo(
            PrincipalCollection principals) {
    	//使用者名稱
        String username = (String) principals.fromRealm(
                getName()).iterator().next();
       
        /*這些程式碼應該是動態從資料庫中取出的,此處寫死*/
        if(username!=null&&username.equals("admin")){
        	SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//        	info.addRole("admin");//新增一個角色,不是配置意義上的新增,而是證明該使用者擁有admin角色
        	info.addStringPermission("User:create");
        	info.addStringPermission("/pages/index.jsp");//新增許可權
        	info.addStringPermission("/pages/info.jsp");//新增許可權
        	return info;
        }
        return null;
    }

此程式碼屬於自定義Realm——public class CustomRealm extends AuthorizingRealm

程式碼中我們使用的是測試資料,直接往info裡面新增角色字串和許可權字串,我們也可以從資料庫中獲取,這不是本節重點。

現在關鍵要明白SecurityManager是從請求端得到應該有的許可權字串,從Realm得到當事人具備的角色和許可權字串,然後比對,比對成功說明鑑權成功,否則鑑權失敗。

事實上,這種配置鑑權的方式,連自定義Realm都不需要,使用者資訊、角色資訊、許可權資訊都可以配置:

[users]
# user1 = sha256-hashed-hex-encoded password, role1, role2, ...
user1 = 2bb80d537b1da3e38bd30361aa855686bde0eacd7162fef6a25fe97bf527a25b, role1, role2, ... 
[roles]
# 'admin' role has all permissions, indicated by the wildcard '*'
admin = *
# The 'schwartz' role can do anything (*) with any lightsaber:
schwartz = lightsaber:*
# The 'goodguy' role is allowed to 'drive' (action) the winnebago (type) with
# license plate 'eagle5' (instance specific id)
goodguy = winnebago:drive:eagle5

這點可以參考官方文件。

===================自定義鑑權=================

往往我們的專案,特別是遺留專案都會設計幾張表來儲存使用者資訊和角色資訊以及許可權對映資訊,所以Realm這塊,一般要自定義。

而且通常的作法是直接用請求的URL作為許可權字串的,也就是不需要URL再去對映一些許可權字串,所以過濾器這塊,我們可能也需要自定義。

自定義鑑權過濾器:

package javacommon.shiro;

import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter;

/**
 * 基於URL的許可權判斷過濾器<p>
 * 我們自動根據URL產生所謂的許可權字串,這一項在Shiro示例中是寫在配置檔案裡面的,預設認為許可權不可動態配置<p>
 * URL舉例:/User/create.do?***=***  -->許可權字串:/User/create.do
 * @author zhengwei lastmodified 2013年8月15日
 *
 */
public class URLPermissionsFilter extends PermissionsAuthorizationFilter{
	/**
	 *@param mappedValue 指的是在宣告url時指定的許可權字串,如/User/create.do=perms[User:create].我們要動態產生這個許可權字串,所以這個配置對我們沒用
	 */
	public boolean isAccessAllowed(ServletRequest request,
			ServletResponse response, Object mappedValue) throws IOException {
		 return super.isAccessAllowed(request, response, buildPermissions(request));
	}
	/**
	 * 根據請求URL產生許可權字串,這裡只產生,而比對的事交給Realm
	 * @param request
	 * @return
	 */
	protected String[] buildPermissions(ServletRequest request) {
		String[] perms = new String[1];
		HttpServletRequest req = (HttpServletRequest) request;
		String path = req.getServletPath();
		perms[0] = path;//path直接作為許可權字串
		/*String regex = "/(.*?)/(.*?)\\.(.*)";
		if(url.matches(regex)){
			Pattern pattern = Pattern.compile(regex);
			Matcher matcher = pattern.matcher(url);
			String controller =  matcher.group(1);
			String action = matcher.group(2);
			
		}*/
		return perms;
	}
}

可以看出我們直接將請求路徑作為許可權字串,過濾器會去呼叫Realm,所以自定義Realm中也要為使用者新增同樣格式的許可權字串
info.addStringPermission("/pages/index.jsp");//新增許可權,admin可訪問這個路徑

最後再來看看全域性Filter的配置:
	<!-- Shiro Filter 攔截器相關配置 -->
	<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
		...
		<property name="filters">
			<util:map>
				<entry key="authc" value-ref="myAuthenFilter" />
				<entry key="perms" value-ref="URLPermissionsFilter" />
			</util:map>
		</property>
		<!-- 過濾鏈定義 -->
		<property name="filterChainDefinitions">
			<value>
				/login.jsp = authc
				/pages/* = authc,perms
				/logout.do = logout
				...
			</value>
		</property>
	</bean>

	...
	<!-- 自定義鑑權攔截器 -->
	<bean id="URLPermissionsFilter" class="javacommon.shiro.URLPermissionsFilter" />


這段配置意味著訪問/pages/*都需要鑑權,而鑑權使用的是自定義的攔截器。

測試:

以admin身份登入並訪問pages/info.jsp,沒問題,因為它擁有這個許可權。

訪問pages/NB.jsp,因為這個路徑沒有新增到admin的許可權中,所以鑑權失敗,將跳轉到unauthorizedUrl指定的路徑。

小節:

對於客戶要求程式開發者自己管理許可權,而且不需要動態配置的情況,使用預設配置法,非常簡單。

當需要動態管理許可權,那就要自定義Realm和Filter,關鍵在於請求URL--》許可權字串,Realm可返回一個使用者擁有的許可權字串。

這些字串應該能比對上。