转载地址: http://blog.csdn.net/shadowsick/article/details/39021265
更多shiro资料:
张开涛博客:http://jinnianshilongnian.iteye.com/blog/2025656
上一个章节我们学习了如何自定义自己的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;
- }
我们开始动手改造这个方法,把这个方法也在我们自己的RoleAuthorizationFilter里重写下
【声明】:以下方法由于原作者的一些处理方法未提供,故改之,效果是一样的
- package com.aviva.agent.support.shiro;
- import java.io.IOException;
- import java.io.PrintWriter;
- import java.util.Set;
- import javax.servlet.ServletRequest;
- import javax.servlet.ServletResponse;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import org.apache.shiro.subject.Subject;
- import org.apache.shiro.util.CollectionUtils;
- import org.apache.shiro.web.filter.authz.AuthorizationFilter;
- import org.apache.shiro.web.util.WebUtils;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.util.StringUtils;
- /**
- *
- * 1.自定义角色鉴权过滤器(满足其中一个角色则认证通过) 2.扩展异步请求认证提示功能;
- *
- */
- public class RoleAuthorizationFilter extends AuthorizationFilter {
- private static final Logger logger = LoggerFactory.getLogger(RoleAuthorizationFilter.class);
-
- @Override
- 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 (isAjax(request)) {//异步请求是会出现无访问权限但是会返回给ajax一个重定向的html,导致无法重定向,且走了ajax的error行方法
- logger.info("RoleAuthorizationFilter-onAccessDenied,登录超时,ajax请求");
-
- saveRequest(request);
- WebUtils.toHttp(response).sendError(HttpServletResponse.SC_UNAUTHORIZED);//错误码401:访问受限
- } else {
- logger.info("RoleAuthorizationFilter-onAccessDenied,登录超时,非ajax请求");
- saveRequestAndRedirectToLogin(request, response);
- }
- } else {
- // If subject is known but not authorized, redirect to the unauthorized URL if there is one
- // If no unauthorized URL is specified, just return an unauthorized HTTP status code
- String unauthorizedUrl = getUnauthorizedUrl();
- //SHIRO-142 - ensure that redirect _or_ error code occurs - both cannot happen due to response commit:
- if (StringUtils.hasText(unauthorizedUrl)) {
- WebUtils.issueRedirect(request, response, unauthorizedUrl);
- } else {
- WebUtils.toHttp(response).sendError(HttpServletResponse.SC_UNAUTHORIZED);
- }
- }
- return false;
- }
-
- @Override
- 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;
- }
- public boolean isAjax(ServletRequest request) {
- HttpServletRequest httpServletRequest = (HttpServletRequest) request;
- return "XMLHttpRequest".equalsIgnoreCase(httpServletRequest
- .getHeader("X-Requested-With"));
- }
- }
下面我们测试下如何效果,先写一个html,配置/test/ajax是不够权限请求的
【声明】:以下方法与原作者的不太一致,但是效果是一样的
点击(此处)折叠或打开
- <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
- <%
- String path = request.getContextPath();
- String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
- %>
- <!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>
- <base href="">
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
- <title>test ajax request</title>
- <script type="text/javascript" src="/static/js/jquery-1.9.1.min.js"></script>
- <script>
- function test(){
-
- $.ajax({
- url: "${ctx}/test/ajax",
- data:{policyNo:$("#policyNo").text()},
- cache: false,
- complete:function(){
- alert("complete");
- },
- dataType:"json",
- success:function (data){
- alert("success");
- },
- error:function(XMLHttpRequest,textStatus){
-
- if(XMLHttpRequest.status==401){//访问被拒绝。
- window.location.href = '${ctx}/login';
- } else {
- alert('系统正在维护中,请稍后再试');
- }
- }
- });
- }
- </script>
- </head>
- <body>
- <input type="button" value="click" onclick="test();" />
- </body>
- </html>
设置好超时时间等超时了点击输入框,自动跳转到登录页面(这里主要说ajax异步跳转)。
最后总结下扩展方案,其实shiro的所有filter都是有统一的接口方法,你们可以看看这真实过滤器都是继承了相同的父级filter,所以其他的filter也可以通过继承重写onAccessDenied方法提供我们的异步请求分支处理。