转载地址: 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;
-
}
看到源码的时候我就很开心地贱笑了,果然是我想的那样,我们很明显地看到一个方法onAccessDenied,认证失败处理,逻辑就是如果登录实体为null就保存请求和跳转登录页面,否则就跳转无权限配置页面
我们开始动手改造这个方法,把这个方法也在我们自己的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"));
-
}
-
}
其实改造也很简单,只是再加一层
ajax的判断,至于如何判断
ajax就是自己个人的方式,有的项目喜欢加一个标识参数,有的人喜欢直接用header里面的X-Requested-With参数,这个看自己的需求咯,我个人喜欢是
ajax请求认证失败是返回一串标准的json格式字符串,页面兼容处理也方便
下面我们测试下如何效果,先写一个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" "">
-
<html xmlns="">
-
<head>
-
<base href="<%=basePath%>">
-
<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方法提供我们的异步请求分支处理。
阅读(2351) | 评论(0) | 转发(0) |