shiro安全架構擴充教程--如何擴充非同步(ajax)請求認證失敗處理

來源:互聯網
上載者:User

        上一個章節我們學習了如何自訂自己的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"><beanclass="com.silvery.security.shiro.filter.RoleAuthorizationFilter" /></entry><entry key="authc"><beanclass="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" onclick="test();" /></body></html>

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

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


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


歡迎拍磚...

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.