最近公司在做APP项目,属于前后端分离,使用ajax请求后台传递JSON数据,但是现在有一个问题,shior默认的配置是session过期后直接跳转到Ligin请求,最后返回Login页面,但是前后端分离的项目都是通过ajax接受的,跳转也是由前台控制,这就导致了PC端与APP需要返回的数据类型不一致的问题。通过百度,找到一种可行的方案,贴出来以供参考。
通过继承 org.apache.shiro.web.filter.authc.FormAuthenticationFilter 类来重写 onAccessDenied方法可以达到这个效果。
1、重写onAccessDenied 方法
package com.mlkj.modules.sys.security;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.alibaba.druid.wall.violation.ErrorCode;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.http.HttpRequest;
import org.springframework.stereotype.Service;
import com.mlkj.common.json.AjaxJson;
import com.mlkj.common.json.PrintJSON;
import com.mlkj.common.utils.StringUtils;
import com.mlkj.modules.sys.security.SystemAuthorizingRealm.Principal;
import com.mlkj.modules.sys.utils.UserUtils;
import java.io.PrintWriter;
/**
* 表单验证(包含验证码)过滤类
*/
@Service
public class FormAuthenticationFilter extends org.apache.shiro.web.filter.authc.FormAuthenticationFilter {
/**
* onAccessDenied 方法会拦截在shior shiroFilterChainDefinitions 配置中 value=authc 的请求
* 方法说明: isLoginRequest 方法表示判断当前请求是否为登陆请求,shior会获取到当前请求并和配置文件中配置的登陆请求做比对,如果相同返回true,否则返回false;
* isLoginSubmission 方法表示判断当前请求如果当前请求URL为登录地址时,判断是否为form提交。
* executeLogin 方法表示是一个登陆方法 调用subject.login(token),如果登录成功,则调用onLoginSuccess();失败则调用onLoginFailure(),
* AuthenticatingFilter.onLoginSuccess(): return true;
* AuthenticatingFilter.onLoginFailure(): return false;
* 最终会调用realm的doGetAuthenticationInfo
*/
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
if (this.isLoginRequest(request, response)) {
if (this.isLoginSubmission(request, response)) {
return this.executeLogin(request, response);
} else {
return true;
}
} else {
String header = ((HttpServletRequest) request).getHeader("Content-Type");
if(header != null && header.equals("application/x-www-form-urlencoded")){
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
AjaxJson ajaxJson = new AjaxJson();
ajaxJson.setErrorCode("notLogin");
ajaxJson.setSuccess(false);
ajaxJson.setMsg("user not login");
out.print(ajaxJson.getJsonStr());
out.flush();
out.close();
} else {
this.saveRequestAndRedirectToLogin(request, response);
}
return false;
}
}
}2 修改shior配置文件 中的 shiroFilterChainDefinitions 将 ${adminPath}/** 改为 authc (${adminPath}为我们项目的默认路径,根据自身项目来设置)
<!-- Shiro权限过滤过滤器定义 -->
<bean name="shiroFilterChainDefinitions" class="java.lang.String">
<constructor-arg>
<value>
/static/** = anon
/userfiles/** = anon
${adminPath}/sys/user/infoCareStatus = anon
${adminPath}/sys/user/validateLoginName = anon
${adminPath}/sys/user/validateMobile = anon
${adminPath}/sys/user/validateMobileExist = anon
${adminPath}/sys/user/resetPassword = anon
${adminPath}/sys/register/** = anon
${adminPath}/codeLogin/** = anon
${adminPath}/soft/sysVersion/getAndroidVer = anon
${adminPath}/soft/sysVersion/getIosVer = anon
${adminPath}/api/** = anon
${adminPath}/pay/** = anon
${adminPath}/wechat/** = anon
${adminPath}/cas = cas
${adminPath}/login = authc
${adminPath}/heartbeat = anon
${adminPath}/logout = anon
${adminPath}/** = authc
/act/editor/** = user
/act/rest/service/editor/** = perms[act:model:edit]
/act/rest/service/model/** = perms[act:model:edit]
/act/rest/service/** = user
/ReportServer/** = user
${adminPath}/hr/hrInterview/receive = anon
${adminPath}/hr/hrOffer/receive = anon
</value>
</constructor-arg>
</bean>
onAccessDenied 方法会拦截在shior shiroFilterChainDefinitions 配置中 value=authc 的请求,当请求被拒绝时会调用此方法。
isLoginRequest 方法表示判断当前请求是否为登陆请求,shior会获取到当前请求并和配置文件中配置的登陆请求做比对,如果相同返回true,否则返回false;
isLoginSubmission 方法表示判断当前请求如果当前请求URL为登录地址时,判断是否为form提交。
executeLogin 方法表示是一个登陆方法 调用subject.login(token),如果登录成功,则调用onLoginSuccess();失败则调用onLoginFailure()。
AuthenticatingFilter.onLoginSuccess(): return true;
AuthenticatingFilter.onLoginFailure(): return false;
最终会调用realm的doGetAuthenticationInfo
如果当前请求是登陆请求,并且是form提交的话,就去进行登陆操作,否则的话直接通过。
如果当前请求非登陆请求,说明这个请求被拒绝了,这时候,通过header判断当前请求是不是ajax请求,如果是的话,说明是APP端连接,返回json格式数据,如果不是,说明是PC端连接,则调用 this.saveRequestAndRedirectToLogin(request, response); 跳转到默认的登陆页面。
写的不好请见谅!!!!!!!!!