最近在学习shiro时遇到一个问题,在ajax请求时,如果session失效时,无法正确的跳转登录页面。在之前的项目中处理的方法是通过自定义一个过滤器来处理,session失效时返回错误码来处理。但因为使用shiro后,会先执行shiro定义的过滤器,才会执行自定义的过滤器,所以之前的方法行不通,参考了很多大大的博客,把我的处理方法贴上。
1.自定义拦截器LoginFormFilter拦截器,继承FormAuthenticationFilter类,在需要登录而未登录的请求都会执行onAccessDenied请求。
package com.xxfy.demo.filter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.xxfy.demo.util.HttpUtils;
public class LoginFormFilter extends FormAuthenticationFilter {
private static final Logger log = LoggerFactory.getLogger(LoginFormFilter.class);
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest)request;
HttpServletResponse httpServletResponse = (HttpServletResponse)response;
if (isLoginRequest(request, response)) {
if (isLoginSubmission(request, response)) {
if (log.isTraceEnabled()) {
log.trace("Login submission detected. Attempting to execute login.");
}
return executeLogin(request, response);
} else {
if (log.isTraceEnabled()) {
log.trace("Login page view.");
}
//allow them to see the login page ;)
return true;
}
} else {
if (log.isTraceEnabled()) {
log.trace("Attempting to access a path which requires authentication. Forwarding to the " +
"Authentication url [" + getLoginUrl() + "]");
}
//如果是Ajax请求,不跳转登录
if (HttpUtils.isAjax(httpServletRequest)){
System.out.println("ajax");
httpServletResponse.setStatus(401);
} else {
saveRequestAndRedirectToLogin(request, response);
}
return false;
}
}
}
此处的onAccessDenied方法跟FormAuthenticationFilter基本类似,只是加了一段判断是ajax请求的代码 ,如果是ajax请求的话,直接返回错误码,而不是跳转登录,因为如果是ajax请求的话,也无法跳转。
2.shiro的配置类中配置拦截器。(此处使用了spring boot)
package com.xxfy.demo.config;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.servlet.Filter;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.xxfy.demo.filter.LoginFormFilter;
/**
* shiro配置类
* @author Administrator
*
*/
@Configuration
public class ShiroConfiguration {
@Bean
public SecurityManager securityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm());
return securityManager;
}
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
//拦截器.
Map filterChainDefinitionMap = new LinkedHashMap();
//配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了
filterChainDefinitionMap.put("/logout", "logout");
//静态资源可以不登录访问
/* filterChainDefinitionMap.put("/static/**", "anon");*/
filterChainDefinitionMap.put("/assets/**", "anon");
filterChainDefinitionMap.put("/js/**", "anon");
filterChainDefinitionMap.put("/css/**", "anon");
filterChainDefinitionMap.put("/img/**", "anon");
filterChainDefinitionMap.put("/unauth", "anon");
//filterChainDefinitionMap.put("/admin/index", "roles['admin']");
//所有请求都需要登录才可以访问
filterChainDefinitionMap.put("/**", "authc");
//登录请求
shiroFilterFactoryBean.setLoginUrl("/login");
//登录成功跳转的页面
shiroFilterFactoryBean.setSuccessUrl("/admin/index");
Map filters = new HashMap();
Filter loginFilter = new LoginFormFilter();
filters.put("authc", loginFilter); //此处使用自定义的拦截器,autho默认使用FormAuthenticationFilter拦截器
shiroFilterFactoryBean.setFilters(filters);
//没有权限的页面
shiroFilterFactoryBean.setUnauthorizedUrl("/unauth");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
@Bean
public AuthorizingRealm myShiroRealm() {
AuthorizingRealm myRealm = new MyShiroRealm();
myRealm.setCredentialsMatcher(customCredentialsMatcher());
return myRealm;
}
@Bean
public CustomCredentialsMatcher customCredentialsMatcher() {
CustomCredentialsMatcher customCredentialsMatcher = new CustomCredentialsMatcher();
customCredentialsMatcher.setHashAlgorithmName("MD5");
return customCredentialsMatcher;
}
}
此处的关键就是代码就是这个:filters.put("authc", loginFilter);这里使用第一步的过滤器,shiro默认使用FormAuthenticationFilter过滤器。
3.前台js中全局捕获这个aja求返回的错误码。
$.ajaxSetup({
type: "POST",
complete: function(jqXHR, textStatus){
switch (jqXHR.status){
case(500):
alert("服务器系统内部错误");
break;
case(401):
$("#loginModal").modal("show");
break;
case(403):
alert("无权限执行此操作");
break;
case(408):
alert("请求超时");
break;
default:
alert("未知错误");
}
},
success: function(data){
alert("操作成功");
}
});
4.增加一步无关的一步,作为自己的记录,项目使用了jquery.datatable作为数据展示,使用这种方法处理ajax请求时,如果session失效,进行查询返回的肯定就不会是json格式的数据,datatable会有一个自身的提示信息,需要把它去掉,此时只要在请求中增加error即可。
var table = $('#example').DataTable({
//开启服务器模式
serverSide: true,
//禁用搜索
searching: false,
//禁用排序
ordering: false,
//数据来源(包括处理分页,排序,过滤) ,即url,action,接口,等等
ajax: {
url : $("#contextPath").val() + "/admin/permissions/user/list",
type:"post",
data : function(d){
d.userId=$("#userId").val();
d.userName=$("#userName").val();
d.sex=$("#sex").val();
d.tel=$("#tel").val();
},
error:function(data) {
}
},
5.效果图: