Chinaunix首页 | 论坛 | 博客
  • 博客访问: 104314
  • 博文数量: 27
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 129
  • 用 户 组: 普通用户
  • 注册时间: 2014-11-12 20:32
文章分类

全部博文(27)

文章存档

2016年(4)

2015年(23)

我的朋友

分类: jQuery

2016-08-10 17:28:43

转载地址: http://blog.csdn.net/shadowsick/article/details/39021265

更多shiro资料:

张开涛博客:http://jinnianshilongnian.iteye.com/blog/2025656

上一个章节我们学习了如何自定义自己的filter,这个只是为了这一章打基础;相信我们这一群shiro使用者比较关注异步请求认证失败会如何处理这个问题,确实我们现在的项目很大一部分请求都是异步的,所以这个问题是无可避免,我看了网上很多资料都是没有完整地给出扩展方案,下面我把自己的处理方案给展示下,如有不爽,请勿跨省,家无水表,不收快递...


直接进入主题,先看看我们之前的配置,自定义一个RoleAuthorizationFilter



点击(此处)折叠或打开

  1. <!-- 过滤链配置 -->
  2.     <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
  3.         <property name="securityManager" ref="securityManager" />
  4.         <property name="loginUrl" value="/" />
  5.         <property name="successUrl" value="/cms/index.do" />
  6.         <property name="unauthorizedUrl" value="/" />
  7.         <property name="filters">
  8.             <map>
  9.                 <entry key="role">
  10.                     <bean
  11.                         class="com.silvery.security.shiro.filter.RoleAuthorizationFilter" />
  12.                 </entry>
  13.                 <entry key="authc">
  14.                     <bean
  15.                         class="com.silvery.security.shiro.filter.SimpleFormAuthenticationFilter" />
  16.                 </entry>
  17.             </map>
  18.         </property>
  19.     </bean>

  20.     <!-- 权限资源配置 -->
  21.     <bean id="filterChainDefinitionsService"
  22.         class="com.silvery.security.shiro.service.impl.SimpleFilterChainDefinitionsService">
  23.         <property name="definitions">
  24.             <value>
  25.                 /static/** = anon
  26.                 /admin/user/login.do = anon
  27.                 /test/** = role[admin]
  28.                 /abc/** = authc
  29.             </value>
  30.         </property>
  31.     </bean>


点击(此处)折叠或打开

  1. public class RoleAuthorizationFilter extends AuthorizationFilter {

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

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

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

  10.         Set<String> roles = CollectionUtils.asSet(rolesArray);
  11.         for (String role : roles) {
  12.             if (subject.hasRole(role)) {
  13.                 return true;
  14.             }
  15.         }
  16.         return false;
  17.     }

  18. }



我们先看看这个源码,发现是继承了AuthorizationFilter类,然后只写了isAccessAllowed方法,然后我们就想isAccessAllowed是判断是否拥有权限,那肯定会有一个方法是认证失败回调的方法,这是框架一贯的做法,来验证下我们的想当然是不是正确的,我们打开AuthorizationFilter类的源码看看



点击(此处)折叠或打开

  1. public abstract class AuthorizationFilter extends AccessControlFilter
  2. {

  3.     public AuthorizationFilter()
  4.     {
  5.     }

  6.     public String getUnauthorizedUrl()
  7.     {
  8.         return unauthorizedUrl;
  9.     }

  10.     public void setUnauthorizedUrl(String unauthorizedUrl)
  11.     {
  12.         this.unauthorizedUrl = unauthorizedUrl;
  13.     }

  14.     protected boolean onAccessDenied(ServletRequest request, ServletResponse response)
  15.         throws IOException
  16.     {
  17.         Subject subject = getSubject(request, response);
  18.         if(subject.getPrincipal() == null)
  19.         {
  20.             saveRequestAndRedirectToLogin(request, response);
  21.         } else
  22.         {
  23.             String unauthorizedUrl = getUnauthorizedUrl();
  24.             if(StringUtils.hasText(unauthorizedUrl))
  25.                 WebUtils.issueRedirect(request, response, unauthorizedUrl);
  26.             else
  27.                 WebUtils.toHttp(response).sendError(401);
  28.         }
  29.         return false;
  30.     }

  31.     private String unauthorizedUrl;
  32. }


看到源码的时候我就很开心地贱笑了,果然是我想的那样,我们很明显地看到一个方法onAccessDenied,认证失败处理,逻辑就是如果登录实体为null就保存请求和跳转登录页面,否则就跳转无权限配置页面



我们开始动手改造这个方法,把这个方法也在我们自己的RoleAuthorizationFilter里写下
【声明】:以下方法由于原作者的一些处理方法未提供,故改之,效果是一样的

  1. package com.aviva.agent.support.shiro;

  2. import java.io.IOException;
  3. import java.io.PrintWriter;
  4. import java.util.Set;

  5. import javax.servlet.ServletRequest;
  6. import javax.servlet.ServletResponse;
  7. import javax.servlet.http.HttpServletRequest;
  8. import javax.servlet.http.HttpServletResponse;

  9. import org.apache.shiro.subject.Subject;
  10. import org.apache.shiro.util.CollectionUtils;
  11. import org.apache.shiro.web.filter.authz.AuthorizationFilter;
  12. import org.apache.shiro.web.util.WebUtils;
  13. import org.slf4j.Logger;
  14. import org.slf4j.LoggerFactory;
  15. import org.springframework.util.StringUtils;
  16. /**
  17.  *
  18.  * 1.自定义角色鉴权过滤器(满足其中一个角色则认证通过) 2.扩展异步请求认证提示功能;
  19.  *
  20.  */
  21. public class RoleAuthorizationFilter extends AuthorizationFilter {

  22.     private static final Logger logger = LoggerFactory.getLogger(RoleAuthorizationFilter.class);
  23.     
  24.     @Override
  25.      protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {

  26. //     HttpServletRequest httpRequest = (HttpServletRequest) request;
  27. //     HttpServletResponse httpResponse = (HttpServletResponse) response;

  28.      Subject subject = getSubject(request, response);

  29.      if (subject.getPrincipal() == null) {
  30.      if (isAjax(request)) {//异步请求是会出现无访问权限但是会返回给ajax一个重定向的html,导致无法重定向,且走了ajax的error行方法

  31.          logger.info("RoleAuthorizationFilter-onAccessDenied,登录超时,ajax请求");
  32.          
  33.          saveRequest(request);
  34.          WebUtils.toHttp(response).sendError(HttpServletResponse.SC_UNAUTHORIZED);//错误码401:访问受限

  35.      } else {
  36.          logger.info("RoleAuthorizationFilter-onAccessDenied,登录超时,非ajax请求");
  37.      saveRequestAndRedirectToLogin(request, response);
  38.      }
  39.      } else {
  40.           // If subject is known but not authorized, redirect to the unauthorized URL if there is one
  41.      // If no unauthorized URL is specified, just return an unauthorized HTTP status code
  42.      String unauthorizedUrl = getUnauthorizedUrl();
  43.      //SHIRO-142 - ensure that redirect _or_ error code occurs - both cannot happen due to response commit:
  44.      if (StringUtils.hasText(unauthorizedUrl)) {
  45.      WebUtils.issueRedirect(request, response, unauthorizedUrl);
  46.      } else {
  47.      WebUtils.toHttp(response).sendError(HttpServletResponse.SC_UNAUTHORIZED);
  48.      }
  49.      }
  50.      return false;
  51.      }
  52.     
  53.     @Override
  54.     public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)
  55.             throws IOException {

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

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

  62.         Set<String> roles = CollectionUtils.asSet(rolesArray);
  63.         for (String role : roles) {
  64.             if (subject.hasRole(role)) {
  65.                 return true;
  66.             }
  67.         }
  68.         return false;
  69.     }
  70.     public boolean isAjax(ServletRequest request) {
  71.         HttpServletRequest httpServletRequest = (HttpServletRequest) request;
  72.         return "XMLHttpRequest".equalsIgnoreCase(httpServletRequest
  73.                 .getHeader("X-Requested-With"));
  74.     }
  75. }


其实改造也很简单,只是再加一层ajax的判断,至于如何判断ajax就是自己个人的方式,有的项目喜欢加一个标识参数,有的人喜欢直接用header里面的X-Requested-With参数,这个看自己的需求咯,我个人喜欢是ajax请求认证失败是返回一串标准的json格式字符串,页面兼容处理也方便

下面我们测试下如何效果,先写一个html,配置/test/ajax是不够权限请求的
【声明】:以下方法与原作者的不太一致,但是效果是一样的

点击(此处)折叠或打开

  1. <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
  2. <%
  3. String path = request.getContextPath();
  4. String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
  5. %>
  6. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "">
  7. <html xmlns="">
  8.     <head>
  9.         <base href="<%=basePath%>">
  10.         <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
  11.         <title>test ajax request</title>
  12.     <script type="text/javascript" src="/static/js/jquery-1.9.1.min.js"></script>
  13.     <script>
  14.         function test(){
  15.              
  16.             $.ajax({
  17.                  url: "${ctx}/test/ajax",
  18.                  data:{policyNo:$("#policyNo").text()},
  19.                  cache: false,
  20.                  complete:function(){
  21.                      alert("complete");
  22.                  },                
  23.                  dataType:"json",
  24.                  success:function (data){
  25.                      alert("success");
  26.                  },
  27.                  error:function(XMLHttpRequest,textStatus){
  28.                         
  29.                          if(XMLHttpRequest.status==401){//访问被拒绝。
  30.                              window.location.href = '${ctx}/login';
  31.                          } else {
  32.                               alert('系统正在维护中,请稍后再试');                
  33.                           }
  34.                  }
  35.             });
  36.         }
  37.     </script>
  38.     </head>
  39. <body>
  40.     <input type="button" value="click" onclick="test();" />
  41. </body>
  42. </html>

设置好超时时间等超时了点击输入框,自动跳转到登录页面(这里主要说ajax异步跳转)。

最后总结下扩展方案,其实shiro的所有filter都是有统一的接口方法,你们可以看看这真实过滤器都是继承了相同的父级filter,所以其他的filter也可以通过继承写onAccessDenied方法提供我们的异步请求分支处理。


阅读(2295) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~