概要

  • 背景
    在开发过程中完成session共享时测试session中断过期页面响应而遇到了问题。
  • 技术使用
    springboot+redis+shiro+静态html分离式前端

解决办法

首先要弄懂一个问题,为什么服务端写了重定向,前端页面没有去跳转?并且重定向里的请求location也指向清楚了。
因为ajax请求里涉及返回两种类型,一种是返回json,一种是返回页面内容,如果纯粹的返回内容的话就走304,页面去读缓存了,然后重定向在阻塞,导致页面没有刷新,当手工刷新才加载304要显示的页面内容。


手动再现一下之前的场景,下图为综合过程结果:

axios重定向 axios302重定向不自动跳转_json

  • ajax请求,发送/findList查询到后台服务端
$.ajax({
    url:serverUrl+'/departments/findList',
    type:'post',
    data: row,
    async:false,
    success: function (data) {
        if (data.success) {
            $.messager.alert("错误");
        }
    }
});
  • 接着服务端拦截器处理请求,博主使用的shiro管理会话和权限,所以会拦截请求做对应的的校验,若校验失败(本例以session失效演示),则进入filter设置好的重定向目录/sessionFailure
  • axios重定向 axios302重定向不自动跳转_ajax_02

  • 由于session失效或权限不足等情况引起的框架(或用户自己使用redirect之类的跳转)重定向,会直接在ServletResponse里设置head的location值,告诉前端请求,向这个新地址重新请求,那么之前请求的/findList,压根就没有到具体业务运行的代码上就已经去执行新的请求了/sessionFailure,如最开始的第一张过程图
  • 之后我们在请求/sessionFailure中做对应业务处理,有2种返回方式:页面返回和json返回。从现在的开发来看,如果使用的页面返回,那么是适用于伪前后端分离和不分离的项目架构,这里只需要使用脚本语言即可,会自动帮你处理好response的页面展示;
  • 从前后端分离式开发看,服务端是不做页面返回的,所以统一以json返回,前端自行处理返回的状态,根据状态自行处理静态页面的跳转,但现在前端大多都采用框架结构了,并且前端语言和库更加丰富,实际上前端也在做mvc、mvv开发,具体使用什么语言博主不做说明(因为只会vue2),博主做这个文章是建立在纯静态html+jquery的前端上实现的,也就是说会用ajax+jquery做到全局处理的事情
/**
     * 会话过期 -- 用户处于未登录状态
     * @param response
     */
    @RequestMapping("/sessionFailure")
    public ModelAndView sessionFailure(HttpSession session, HttpServletResponse response) {
        System.out.println(SecurityUtils.getSubject().getSession().getAttribute(SessionStatus.STATUS));
        if (null == getCurrentUser()) {
            response.setHeader(SessionStatus.STATUS, SessionStatus.EXPIRED);
        }
        //方法1:返回页面内容跳转
        ModelAndView model = new ModelAndView();
        model.addObject("result", Result.result(1, false, StatusCode.LOGINOUT, ResultType.LOGINOUT));
        //model.setViewName("forward:/user/login.html");
        model.setViewName("redirect:/user/login.html");

        //方法2:ajax-json返回,由前端捕获自行操作代码
        Map<String, Object> result = new HashMap<String, Object>();
        result.put("sessionStatus", "expired");
        result.put("message", ResultType.LOGINOUT);
        result.put("code", StatusCode.LOGINOUT);

        //方法2包装
        doResult(response, flag, StatusCode.DEFAULT, null, flag ? "通过" : "失败");
        return model;
    }
  • 如果你使用的是跟博主类似的环境,并且是前后端分离式开发,那么建议使用上述的方法2,自己包装返回json,不要返回页面。创建一个js,加入如下引用,在index页面引入一下
$.ajaxSetup({ 
    asycn: false,
    xhrFields: {
        withCredentials: true
    },
    error: function (XMLHttpRequest, textStatus, errorThrown){
        if(XMLHttpRequest.status==403){
            alert('您没有权限访问此资源或进行此操作');
            return false;
        }
    }, 
    contentType:"application/x-www-form-urlencoded;charset=utf-8",
    complete: function (XMLHttpRequest,textStatus) {
        debugger;
        //通过XMLHttpRequest取得响应头,sessionstatus
        var sessionstatus = XMLHttpRequest.getResponseHeader("sessionStatus"); 
        //通过XMLHttpRequest取得返回的json
        var json = XMLHttpRequest.responseJSON;
        //通过XMLHttpRequest取得返回的text
        var text = XMLHttpRequest.responseText;
    }
});
  • 不懂ajaxSetup可以看一下,ajaxSetup是一个类似前端ajax父级的东西,也就是说如果有了这个,你的页面里写的其他$.ajax就是它的子集,会默认带上ajaxSetup上设置的属性,另外ajaxSetup的一些方法如dataFilter、complete、success都是
    在你的ajax请求时会去执行的方法(至于先执行还是后执行,不是本文讨论的方法,后续做详细介绍)。
  • 这里博主是利用的complete监听所有的ajax请求,在请求执行完成后进入方法,执行后续动作,若服务端在重定向执行我上述的/sessionFailure请求时返回了json,那么就使用XMLHttpRequest.responseText去读取服务端返回的json内容,判断你返回的状态,上面代码服务端的例子是用的sessionStatus,所以现在前端例子上也是用这个办法,博主列举了可行的三种方式,任选。

总结

有问题留言交流,博主比较懒,草稿箱里有四十多篇没写完的博客,只能趁现在工作不忙,一段时间一篇慢慢还原环境去补充了,累!!!!