在写shiro登录验证的时候,按照预估的想法,在没有进行登录的情况下,是无法进入到页面的并且会给他转发到登录页面。
但是很明显效果跟现实差距太大了。
这时发现在没有登录的情况下进入到了页面,并没有进行转发,但是页面确黑了变成302。
但是使用postman却是另一种情况。
在postman没有登录的情况下直接进页面,发现被shiro阻止了并且返回到我想要的登录页面了。可是前台。。。。。
其实这种情况呢因为采用的是前后端分离的情况,所以前台会首先发送一个options请求,但是这个请求,我后台在配置跨域的时候已经配置放行这个请求了,但是shiro不知道shiro还是会给我拦截。并且在之前的跨域并不完全拦截跨域了,因为首先进行访问的是shiro。
解决方案:
1、编写彻底的解决跨域问题。
2、判断前台发送请求是否是ajax,如果是ajax请求我们需要配置前后台一起来重定向页面,因为我们都知道ajax是无法进行重定向的。
@Component
public class CORSFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) servletResponse;
HttpServletRequest request = (HttpServletRequest) servletRequest;
System.out.println(">======================================");
// 允许哪些Origin发起跨域请求
String orgin = request.getHeader("Origin");
// response.setHeader( "Access-Control-Allow-Origin", config.getInitParameter( "AccessControlAllowOrigin" ) );
response.setHeader( "Access-Control-Allow-Origin", orgin );
// 允许请求的方法
response.setHeader( "Access-Control-Allow-Methods", "POST,GET,OPTIONS,DELETE,PUT" );
//多少秒内,不需要再发送预检验请求,可以缓存该结果
response.setHeader( "Access-Control-Max-Age", "3600" );
// 表明它允许跨域请求包含xxx头
response.setHeader( "Access-Control-Allow-Headers", "x-auth-token,Origin,Access-Token,X-Requested-With,Content-Type, Accept,token" );
//是否允许浏览器携带用户身份信息(cookie)
response.setHeader( "Access-Control-Allow-Credentials", "true" );
//白名单
response.addHeader("Access-Control-Expose-Headers","REDIRECT,CONTEXTPATH");
//prefight请求
if (request.getMethod().equals( "OPTIONS" )) {
response.setStatus( 200 );
return;
}
chain.doFilter( servletRequest, response );
}
}
这个白名单非常重要,是跟ajax跳转有关的。
我们通过配置这个我们的页面就解决了跨域问题。
@Component
public class ShiroUserFormAuthenticationFilter extends FormAuthenticationFilter {
/**
* *判断是否登录
* * @param request
* * @param response
* * @return true-继续往下执行,false-该filter过滤器已经处理,不继续执行其他过滤器
* * @throws Exception
*
*/
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
HttpServletRequest req = (HttpServletRequest) request;
//判断是否为ajax请求,默认不是 ,
boolean isAjaxRequest = false;
if (!StringUtils.isBlank(req.getHeader("Accept")) && req.getHeader("Accept").equals("application/json, text/javascript, */*; q=0.01")) {
isAjaxRequest = true;
}
String port = "", contextPath = "";
if (request.getServerPort() != 80) {
port = ":" + request.getServerPort();
String loginPath ="自己的跳转地址";
if (isAjaxRequest) {
httpServletResponse.setCharacterEncoding("UTF-8");
httpServletResponse.setContentType("application/json");
httpServletResponse.getWriter().write(JSONObject.toJSON("登录认证失效,请重新登录!").toString());
httpServletResponse.setStatus(403);
httpServletResponse.setHeader("REDIRECT", "REDIRECT");//告诉ajax这是重定向
httpServletResponse.setHeader("CONTEXTPATH", loginPath);//上面定义的重定向地址 ,这里的两个REDIRECT/CONTEXTPATH需要注意他在上面配置跨域的白名单里是要保持一致的。
httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
} else {
saveRequestAndRedirectToLogin(request, response);
}
}
return false;
}
}
配置完了这个以后我们需要再shiro的拦截器上注册下这个类
shiroFilterFactoryBean.getFilters().put("authc",new ShiroUserFormAuthenticationFilter());
剩下的前台就比较简单了。我们只需要定义全局ajax跳转就行。
$.ajaxSetup({
xhrFields:{withCredentials:true},//必加
//设置ajax请求结束后的执行动作
complete : function(XMLHttpRequest, textStatus) {
// 通过XMLHttpRequest取得响应头,REDIRECT
var redirect = XMLHttpRequest.getResponseHeader("REDIRECT");//若HEADER中含有REDIRECT说明后端想重定向
console.log(">+"+redirect)
if (redirect == "REDIRECT") {
var win = window;
while (win != win.top){
win = win.top;
}
//将后端重定向的地址取出来,使用win.location.href去实现重定向的要求
layer.alert("您的登录信息已超时,请重新登录");
win.location.href= XMLHttpRequest.getResponseHeader("CONTEXTPATH");
}
},
});
最终页面
未登录的情况下:
会弹出一个框然后点击确定后就跳转到自己设定的路径,
需要注意的是:那个路径一定要是绝对路径.