shiro安全框架擴展教程--如何擴展異步(ajax)請求認證失敗處理

        上一個章節我們學習瞭如何自定義自己的filter,這個只是爲了這一章打基礎;相信我們這一羣shiro使用者比較關注異步請求認證失敗會如何處理這個問題,確實我們現在的項目很大一部分請求都是異步的,所以這個問題是無可避免,我看了網上很多資料都是沒有完整地給出擴展方案,下面我把自己的處理方案給展示下,如有不爽,請勿跨省,家無水錶,不收快遞...


直接進入主題,先看看我們之前的配置,自定義一個RoleAuthorizationFilter


<!-- 過濾鏈配置 -->
	<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
		<property name="securityManager" ref="securityManager" />
		<property name="loginUrl" value="/" />
		<property name="successUrl" value="/cms/index.do" />
		<property name="unauthorizedUrl" value="/" />
		<property name="filters">
			<map>
				<entry key="role">
					<bean
						class="com.silvery.security.shiro.filter.RoleAuthorizationFilter" />
				</entry>
				<entry key="authc">
					<bean
						class="com.silvery.security.shiro.filter.SimpleFormAuthenticationFilter" />
				</entry>
			</map>
		</property>
	</bean>

	<!-- 權限資源配置 -->
	<bean id="filterChainDefinitionsService"
		class="com.silvery.security.shiro.service.impl.SimpleFilterChainDefinitionsService">
		<property name="definitions">
			<value>
				/static/** = anon
				/admin/user/login.do = anon
				/test/** = role[admin]
				/abc/** = authc
			</value>
		</property>
	</bean>


public class RoleAuthorizationFilter extends AuthorizationFilter {

	public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)
			throws IOException {

		Subject subject = getSubject(request, response);
		String[] rolesArray = (String[]) mappedValue;

		if (rolesArray == null || rolesArray.length == 0) {
			// no roles specified, so nothing to check - allow access.
			return true;
		}

		Set<String> roles = CollectionUtils.asSet(rolesArray);
		for (String role : roles) {
			if (subject.hasRole(role)) {
				return true;
			}
		}
		return false;
	}

}


我們先看看這個源碼,發現是繼承了AuthorizationFilter類,然後只重寫了isAccessAllowed方法,然後我們就想isAccessAllowed是判斷是否擁有權限,那肯定會有一個方法是認證失敗回調的方法,這是框架一貫的做法,來驗證下我們的想當然是不是正確的,我們打開AuthorizationFilter類的源碼看看


public abstract class AuthorizationFilter extends AccessControlFilter
{

    public AuthorizationFilter()
    {
    }

    public String getUnauthorizedUrl()
    {
        return unauthorizedUrl;
    }

    public void setUnauthorizedUrl(String unauthorizedUrl)
    {
        this.unauthorizedUrl = unauthorizedUrl;
    }

    protected boolean onAccessDenied(ServletRequest request, ServletResponse response)
        throws IOException
    {
        Subject subject = getSubject(request, response);
        if(subject.getPrincipal() == null)
        {
            saveRequestAndRedirectToLogin(request, response);
        } else
        {
            String unauthorizedUrl = getUnauthorizedUrl();
            if(StringUtils.hasText(unauthorizedUrl))
                WebUtils.issueRedirect(request, response, unauthorizedUrl);
            else
                WebUtils.toHttp(response).sendError(401);
        }
        return false;
    }

    private String unauthorizedUrl;
}

看到源碼的時候我就很開心地賤笑了,果然是我想的那樣,我們很明顯地看到一個方法onAccessDenied,認證失敗處理,邏輯就是如果登錄實體爲null就保存請求和跳轉登錄頁面,否則就跳轉無權限配置頁面


我們開始動手改造這個方法,把這個方法也在我們自己的RoleAuthorizationFilter裏重寫下


/**
 * 
 * 1.自定義角色鑑權過濾器(滿足其中一個角色則認證通過) 2.擴展異步請求認證提示功能;
 * 
 * @author shadow
 * 
 */
public class RoleAuthorizationFilter extends AuthorizationFilter {

	protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {

		HttpServletRequest httpRequest = (HttpServletRequest) request;
		HttpServletResponse httpResponse = (HttpServletResponse) response;

		Subject subject = getSubject(request, response);

		if (subject.getPrincipal() == null) {
			if (com.silvery.utils.WebUtils.isAjax(httpRequest)) {
				com.silvery.utils.WebUtils.sendJson(httpResponse, JsonUtils.toJSONString(new ViewResult(false,
						"您尚未登錄或登錄時間過長,請重新登錄!")));
			} else {
				saveRequestAndRedirectToLogin(request, response);
			}
		} else {
			if (com.silvery.utils.WebUtils.isAjax(httpRequest)) {
				com.silvery.utils.WebUtils.sendJson(httpResponse, JsonUtils.toJSONString(new ViewResult(false,
						"您沒有足夠的權限執行該操作!")));
			} else {
				String unauthorizedUrl = getUnauthorizedUrl();
				if (StringUtils.hasText(unauthorizedUrl)) {
					WebUtils.issueRedirect(request, response, unauthorizedUrl);
				} else {
					WebUtils.toHttp(response).sendError(401);
				}
			}
		}
		return false;
	}

	public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)
			throws IOException {

		Subject subject = getSubject(request, response);
		String[] rolesArray = (String[]) mappedValue;

		if (rolesArray == null || rolesArray.length == 0) {
			// no roles specified, so nothing to check - allow access.
			return true;
		}

		Set<String> roles = CollectionUtils.asSet(rolesArray);
		for (String role : roles) {
			if (subject.hasRole(role)) {
				return true;
			}
		}
		return false;
	}

}

其實改造也很簡單,只是再加一層ajax的判斷,至於如何判斷ajax就是自己個人的方式,有的項目喜歡加一個標識參數,有的人喜歡直接用header裏面的X-Requested-With參數,這個看自己的需求咯,我個人喜歡是ajax請求認證失敗是返回一串標準的json格式字符串,頁面兼容處理也方便


下面我們測試下如何效果,先寫一個html,配置/test/a.do是不夠權限請求的


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
		<title>test ajax request</title>
	<script type="text/javascript" src="/static/plugin/jquery/core.js"></script>
	<script>
		function test(){
			$.post('/test/a.do','',function(result){
				alert(result);
			});
		}
	</script>
	</head>
<body>
	<input type="button" value="click" οnclick="test();" />
</body>
</html>

html寫完,就跑起項目,然後先別登錄系統,直接打開html點擊下這個按鈕,發現alert提示{"message":"您尚未登錄或登錄時間過長,請重新登錄!","success":false,"value":null};

然後再登錄系統,再點擊這個按鈕請求一次看看發現alert提示{"message":"您沒有足夠的權限執行該操作!","success":false,"value":null};很明顯尚未登錄和權限不足的ajax時候提示都完美地出現,然後這章節我就可以功成身退了


最後總結下擴展方案,其實shiro的所有filter都是有統一的接口方法,你們可以看看這真實過濾器都是繼承了相同的父級filter,所以其他的filter也可以通過繼承重寫onAccessDenied方法提供我們的異步請求分支處理


歡迎拍磚...

發佈了57 篇原創文章 · 獲贊 211 · 訪問量 67萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章