springboot+vue实现细粒度的session处理

本文介绍了一种使用SpringBoot和Vue实现细粒度session管理的方法,通过自定义过滤器检查session状态,有效处理session过期情况,前端对接收的session过期信息进行处理并重定向至登录页面。

springboot+vue实现细粒度的session处理

编写逻辑

1.使用自定义的过滤器去实现servlet的Filter接口,重写doFilter方法
2. 在springboot的入口类上使用@ServletComponentScan注解引入自定义的过滤器
3. 在实现doFilter方法的时候,如果session没有过期,则放行,否则则返回session过期的提示信息给前端
4. 前端接收到session过期(一般都会在请求响应时做统一处理),则提示用户session过期,然后重定向到登录页面即可;

代码示例

  1. 以下代码参考了部分网友的写法:spring boot使用过滤器(以session校验为例)

  2. 后端代码:

    A.自定义的Filter:

import com.alibaba.fastjson.JSONObject;
import com.itsource.base.common.SdmConst;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;

@WebFilter(filterName = "sessionFilter",urlPatterns = {"/*"})
public class SessionFilter implements Filter {

    //标示符:表示当前用户未登录(可根据自己项目需要改为json样式)
    String EXPIRE_MSG = "{\"data\":{\"code\": \"100003\"}}";

    //不需要登录就可以访问的路径(比如:注册登录等)
    String[] includeUrls = new String[]{"/tsyslogin/actionTSysLogin.do","/tsysuser/queryTSysMenuTreeByUser.do"};


    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        HttpSession session = request.getSession(false);
        String uri = request.getRequestURI();

        System.out.println("filter url:"+uri);
        //是否需要过滤
        boolean needFilter = isNeedFilter(uri);


        if (!needFilter) { //不需要过滤直接传给下一个过滤器
            filterChain.doFilter(servletRequest, servletResponse);
        } else { //需要过滤器
            // session中包含user对象,则是登录状态
            if(session!=null&&session.getAttribute(SdmConst.LOGIN_SESSION_KEY) != null){
                // System.out.println("user:"+session.getAttribute("user"));
                filterChain.doFilter(request, response);
            }else{
                String requestType = request.getHeader("X-Requested-With");
                //判断是否是ajax请求
                if(requestType!=null && "XMLHttpRequest".equals(requestType)){
                }else{
                    response.setCharacterEncoding("UTF-8");
                    response.setContentType("application/json; charset=utf-8");
                    PrintWriter out = response.getWriter();
                    JSONObject res = new JSONObject();
                    res.put("code", "100003");
                    out.append(res.toString());
                }
            }
        }
    }

    /**
     * @Author: xxxxx
     * @Description: 是否需要过滤
     * @Date: 2018-03-12 13:20:54
     * @param uri
     */
    public boolean isNeedFilter(String uri) {

        for (String includeUrl : includeUrls) {
            if(includeUrl.equals(uri)) {
                return false;
            }
        }

        return true;
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void destroy() {

    }
}
B.入口程序:
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.boot.web.servlet.ServletComponentScan;

/**
 *  注:为了避免扫描路径不一致,请将启动类放在Root Package 即 com.itsource
 */
@SpringBootApplication(exclude = MongoAutoConfiguration.class)
@MapperScan("**.itsource.**.mapper")
@ServletComponentScan
public class WebApplication {
    public static void main(String[] args) {
        SpringApplication.run(WebApplication.class, args);
    }
}

3.前端代码

import axios from 'axios';
// import qs from 'qs';
import router from '@/router';
import ErrorHandling from './error-handling';
import {Modal} from 'ant-design-vue';
import Loading from './loading-toast'
import {crypto} from '../utils';
import Vue from 'vue';
import {merge} from 'lodash';

const Error = new ErrorHandling();

/**
 * Axios构造函数
 * @method CreatAxios
 * @param {Object} config
 * @constructor
 */
function CreatAxios(config = {}) {
  merge({
    // baseURL: '/', // 因为我本地做了反向代理
    timeout: 60 * 1000, // 超时处理
    responseType: 'json', // 响应数据类型
    withCredentials: true, // 是否允许带cookie这些
    headers: {
      // json 或者 x-www-form-urlencoded 自行选择
      // 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8',
      'Content-Type': 'application/json;charset=utf-8'
    }
  }, config);

  const Axios = axios.create(config);

  //POST传参序列化(添加请求拦截器)
  Axios.interceptors.request.use(
    config => {
      Loading.open();
      // 在发送请求之前做某件事
      // 温馨提示,若提交能直接接受json 格式,可以不用 qs 来序列化的
      // if (config.method === 'post') {
      //   // 序列化
      //   config.data = qs.stringify(config.data);
      // }

      // 开启上送数据加密
      if ((config.hasOwnProperty('encrypt') ? config.encrypt : Vue.config.encrypt) && config.data) {
        config.data = crypto.Encrypt(config.data)
      }

      return config;
    },
    error => {
      Loading.close();
      // error 的回调信息
      Modal.error({
        title: '请求失败',
        cancelText: '确定',
        content: error && error.data.error.message
      });
      return Promise.reject(error.data.error.message);
    }
  );

  //返回状态判断(添加响应拦截器)
  Axios.interceptors.response.use(
    res => {
      Loading.close();
      //对响应数据做些事
      
      if (res.data) {
        //判断session是否过期
        if (res.data.code === '100003') {
          //
          Modal.error({
            title: '访问错误',
            cancelText: '确定',
            content: '用户登录过期,请重新登录!',
            onOk: () => {
              router.push({
                path: '/login'
              })
            }})
            return
        }
        //请求错误
        if (res.data.code !== '000000') {
          const err_msg = Error.business(res.data);
          // 错误处理
          Modal.error({
            title: '请求错误',
            cancelText: '确定',
            content: err_msg,
            onOk: () => {
              // router.push({
              //   path: '/login'
              // });
            }
          })
        }
        // 响应解密
        if ((res.config.hasOwnProperty('encrypt') ? res.config.encrypt : Vue.config.encrypt) && res.config.data) {
          res.data = crypto.Decrypt(res.data)
        }
      }
      if (res.hasOwnProperty('data')) {
        res = res.data
      }
      return res;
    },
    error => {
      Loading.close();
      const errMsg = Error.server(error.response);

      Modal.error({
        title: '服务器错误',
        okText: '确定',
        content: errMsg
      });

      return Promise.reject(error);
    }
  );

  return Axios;
}

// 对axios的实例重新封装成一个plugin ,方便 Vue.use(xxxx)
export default CreatAxios;

以上只是个人的用法,在此做一个记录,仅供参考!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值