shiro安全框架扩展教程--如何扩展异步(ajax)请求认证失败处理

来源:互联网 发布:js源代码加密 编辑:程序博客网 时间:2024/05/24 06:30

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



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


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


[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  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.     <!-- 权限资源配置 -->  
  22.     <bean id="filterChainDefinitionsService"  
  23.         class="com.silvery.security.shiro.service.impl.SimpleFilterChainDefinitionsService">  
  24.         <property name="definitions">  
  25.             <value>  
  26.                 /static/** = anon  
  27.                 /admin/user/login.do = anon  
  28.                 /test/** = role[admin]  
  29.                 /abc/** = authc  
  30.             </value>  
  31.         </property>  
  32.     </bean>  


[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public class RoleAuthorizationFilter extends AuthorizationFilter {  
  2.   
  3.     public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)  
  4.             throws IOException {  
  5.   
  6.         Subject subject = getSubject(request, response);  
  7.         String[] rolesArray = (String[]) mappedValue;  
  8.   
  9.         if (rolesArray == null || rolesArray.length == 0) {  
  10.             // no roles specified, so nothing to check - allow access.  
  11.             return true;  
  12.         }  
  13.   
  14.         Set<String> roles = CollectionUtils.asSet(rolesArray);  
  15.         for (String role : roles) {  
  16.             if (subject.hasRole(role)) {  
  17.                 return true;  
  18.             }  
  19.         }  
  20.         return false;  
  21.     }  
  22.   
  23. }  


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


[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public abstract class AuthorizationFilter extends AccessControlFilter  
  2. {  
  3.   
  4.     public AuthorizationFilter()  
  5.     {  
  6.     }  
  7.   
  8.     public String getUnauthorizedUrl()  
  9.     {  
  10.         return unauthorizedUrl;  
  11.     }  
  12.   
  13.     public void setUnauthorizedUrl(String unauthorizedUrl)  
  14.     {  
  15.         this.unauthorizedUrl = unauthorizedUrl;  
  16.     }  
  17.   
  18.     protected boolean onAccessDenied(ServletRequest request, ServletResponse response)  
  19.         throws IOException  
  20.     {  
  21.         Subject subject = getSubject(request, response);  
  22.         if(subject.getPrincipal() == null)  
  23.         {  
  24.             saveRequestAndRedirectToLogin(request, response);  
  25.         } else  
  26.         {  
  27.             String unauthorizedUrl = getUnauthorizedUrl();  
  28.             if(StringUtils.hasText(unauthorizedUrl))  
  29.                 WebUtils.issueRedirect(request, response, unauthorizedUrl);  
  30.             else  
  31.                 WebUtils.toHttp(response).sendError(401);  
  32.         }  
  33.         return false;  
  34.     }  
  35.   
  36.     private String unauthorizedUrl;  
  37. }  

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


我们开始动手改造这个方法,把这个方法也在我们自己的RoleAuthorizationFilter里重写下


[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. /**  
  2.  *   
  3.  * 1.自定义角色鉴权过滤器(满足其中一个角色则认证通过) 2.扩展异步请求认证提示功能;  
  4.  *   
  5.  * @author shadow  
  6.  *   
  7.  */  
  8. public class RoleAuthorizationFilter extends AuthorizationFilter {  
  9.   
  10.     protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {  
  11.   
  12.         HttpServletRequest httpRequest = (HttpServletRequest) request;  
  13.         HttpServletResponse httpResponse = (HttpServletResponse) response;  
  14.   
  15.         Subject subject = getSubject(request, response);  
  16.   
  17.         if (subject.getPrincipal() == null) {  
  18.             if (com.silvery.utils.WebUtils.isAjax(httpRequest)) {  
  19.                 com.silvery.utils.WebUtils.sendJson(httpResponse, JsonUtils.toJSONString(new ViewResult(false,  
  20.                         "您尚未登录或登录时间过长,请重新登录!")));  
  21.             } else {  
  22.                 saveRequestAndRedirectToLogin(request, response);  
  23.             }  
  24.         } else {  
  25.             if (com.silvery.utils.WebUtils.isAjax(httpRequest)) {  
  26.                 com.silvery.utils.WebUtils.sendJson(httpResponse, JsonUtils.toJSONString(new ViewResult(false,  
  27.                         "您没有足够的权限执行该操作!")));  
  28.             } else {  
  29.                 String unauthorizedUrl = getUnauthorizedUrl();  
  30.                 if (StringUtils.hasText(unauthorizedUrl)) {  
  31.                     WebUtils.issueRedirect(request, response, unauthorizedUrl);  
  32.                 } else {  
  33.                     WebUtils.toHttp(response).sendError(401);  
  34.                 }  
  35.             }  
  36.         }  
  37.         return false;  
  38.     }  
  39.   
  40.     public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)  
  41.             throws IOException {  
  42.   
  43.         Subject subject = getSubject(request, response);  
  44.         String[] rolesArray = (String[]) mappedValue;  
  45.   
  46.         if (rolesArray == null || rolesArray.length == 0) {  
  47.             // no roles specified, so nothing to check - allow access.  
  48.             return true;  
  49.         }  
  50.   
  51.         Set<String> roles = CollectionUtils.asSet(rolesArray);  
  52.         for (String role : roles) {  
  53.             if (subject.hasRole(role)) {  
  54.                 return true;  
  55.             }  
  56.         }  
  57.         return false;  
  58.     }  
  59.   
  60. }  

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


下面我们测试下如何效果,先写一个html,配置/test/a.do是不够权限请求的


[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">  
  2. <html xmlns="http://www.w3.org/1999/xhtml">  
  3.     <head>  
  4.         <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>  
  5.         <title>test ajax request</title>  
  6.     <script type="text/javascript" src="/static/plugin/jquery/core.js"></script>  
  7.     <script>  
  8.         function test(){  
  9.             $.post('/test/a.do','',function(result){  
  10.                 alert(result);  
  11.             });  
  12.         }  
  13.     </script>  
  14.     </head>  
  15. <body>  
  16.     <input type="button" value="click" onclick="test();" />  
  17. </body>  
  18. </html>  

html写完,就跑起项目,然后先别登录系统,直接打开html点击下这个按钮,发现alert提示{"message":"您尚未登录或登录时间过长,请重新登录!","success":false,"value":null};

然后再登录系统,再点击这个按钮请求一次看看发现alert提示{"message":"您没有足够的权限执行该操作!","success":false,"value":null};很明显尚未登录和权限不足的ajax时候提示都完美地出现,然后这章节我就可以功成身退了


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


欢迎拍砖...

0 0