在写shiro登录验证的时候,按照预估的想法,在没有进行登录的情况下,是无法进入到页面的并且会给他转发到登录页面。

但是很明显效果跟现实差距太大了。

android 校验跨域 origins 跨域登录验证_Access


这时发现在没有登录的情况下进入到了页面,并没有进行转发,但是页面确黑了变成302。

但是使用postman却是另一种情况。

android 校验跨域 origins 跨域登录验证_ajax_02


在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");
	        }
	    }, 	
	});
最终页面
	未登录的情况下:

android 校验跨域 origins 跨域登录验证_ajax_03


会弹出一个框然后点击确定后就跳转到自己设定的路径,

需要注意的是:那个路径一定要是绝对路径.