前言
现在做项目,大多都是前后端分离;权限控制都是在后台实现,前端使用ajax调用后台接口。
但是ajax对接口返回的重定向是没发处理的,会出现异常(具体错误是哪个一时想不起来了);当shiro发现失效后的session时通常会将该请求重定向到loginUrl,或者是用户访问的某个资源权限不足时(会重定向到unAuthorizedUrl),这时Ajax请求基本都是出错的。
解决方案
修改重定向302为正常返回200
shiro默认实现了一些filter,使用不同的filter处理对应的权限拦截:
/**
* Enum representing all of the default Shiro Filter instances available to web applications. Each filter instance is
* typically accessible in configuration the {@link #name() name} of the enum constant.
*
* @since 1.0
*/
public enum DefaultFilter {
anon(AnonymousFilter.class),
authc(FormAuthenticationFilter.class),
authcBasic(BasicHttpAuthenticationFilter.class),
logout(LogoutFilter.class),
noSessionCreation(NoSessionCreationFilter.class),
perms(PermissionsAuthorizationFilter.class),
port(PortFilter.class),
rest(HttpMethodPermissionFilter.class),
roles(RolesAuthorizationFilter.class),
ssl(SslFilter.class),
user(UserFilter.class);
当用户没有登录或session失效后,是使用FormAuthenticationFilter进行拦截处理的,我们可以重写里面的onAccessDenied方法,从而修改重定向。
public class ShiroFormAuthenticationFilter extends FormAuthenticationFilter {
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
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 {
HttpServletRequest req = (HttpServletRequest)request;
HttpServletResponse resp = (HttpServletResponse) response;
if(req.getMethod().equals(RequestMethod.OPTIONS.name())) {
resp.setStatus(HttpStatus.OK.value());
return true;
}
if (log.isTraceEnabled()) {
log.trace("Attempting to access a path which requires authentication. Forwarding to the " +
"Authentication url [" + getLoginUrl() + "]");
}
//前端Ajax请求时requestHeader里面带一些参数,用于判断是否是前端的请求
String ajaxHeader = req.getHeader(ShiroConstant.USERID);
if (ajaxHeader != null || req.getHeader("xx") != null) {
//前端Ajax请求,则不会重定向
resp.setHeader("Access-Control-Allow-Origin", req.getHeader("Origin"));
resp.setHeader("Access-Control-Allow-Credentials", "true");
resp.setContentType("application/json; charset=utf-8");
resp.setCharacterEncoding("UTF-8");
PrintWriter out = resp.getWriter();
JSONObject result = new JSONObject();
result.put("message", "请重新登录!");
result.put("statusCode", -401);
out.println(result);
out.flush();
out.close();
} else {
saveRequestAndRedirectToLogin(request, response);
}
return false;
}
}
}
同理,用户权限不足是使用AuthorizationFilter进行拦截处理的,我们依然可以重写里面的onAccessDenied方法,如下:
public class ShiroRoleAuthorizationFilter extends AuthorizationFilter {
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
HttpServletRequest req = (HttpServletRequest)request;
HttpServletResponse resp = (HttpServletResponse) response;
if(req.getMethod().equals(RequestMethod.OPTIONS.name())) {
resp.setStatus(HttpStatus.OK.value());
return true;
}
//前端Ajax请求时requestHeader里面带一些参数,用于判断是否是前端的请求
String ajaxHeader = req.getHeader(ShiroConstant.USERID);
if (ajaxHeader != null || req.getHeader("xx") != null) {
//前端Ajax请求,则不会重定向
resp.setHeader("Access-Control-Allow-Origin", req.getHeader("Origin"));
resp.setHeader("Access-Control-Allow-Credentials", "true");
resp.setContentType("application/json; charset=utf-8");
resp.setCharacterEncoding("UTF-8");
PrintWriter out = resp.getWriter();
JSONObject result = new JSONObject();
result.put("message", "权限不足!");
result.put("statusCode", -403);
out.println(result);
out.flush();
out.close();
return false;
}
return super.onAccessDenied(request, response);
}
}
这样,前端Ajax请求被Shiro拦截后就不会出现返回302重定向的问题了。