vue+ssm+前后分离+jwt认证+ sessionId变动问题 或者cookie丢失问题

环境:vue/cli 4.3 +elementUI+ssm框架+node.js(代理proxyTable 方式的代理,在开发环境下)

我遇到sessionId 总是变化的问题(坑了我好久,故做引路人.....)

1.因为用到的是node.js开发的环境下的代理.所以不存在跨域问题(再说一遍不存在跨域问题).前端代理配置vue.config.js 如下图:

module.exports = {
    css: {
        sourceMap: true,  //设置浏览器调试模式下显示css文件显示路径,方便开发人员调试.
    },
    devServer: {
        port:8080,
        host:'127.0.0.1',
        https:false,
        proxy: {
            '/api': {
                target: 'http://localhost:8088/api',
                changeOrigin: true,
                ws: false,
                pathRewrite: { '^/api': '/' }
            }
        }
    },

};

 

如果你也用上面的代理,那么后台代码不用动..因为代理不会涉及跨域问题,代理顾名思义,你可以想象成前端模拟后台接口访问后台,所以不涉及跨域,项目开发完是放到ngnix 上的.如果不是采用代理方式.请参考其他跨域配置(网上一搜一大堆)

注意要说明的是8080后的api是后端项目名称.必须和 '/api'代理名称一致,否则会导致sessionId 一致变换 .我不清楚为什么,知道的朋友请留言.. 

2.axios 中不需要改变 ..  至于网上说 withCredentials: true 可传cookie 信息,, 仅限于跨域.我们这里采用的是代理,[vue开发环境下的node.js 安装提供的代理],  true或false 无所谓..不会对sessionid有影响.或者说.cookie都能被后端接收..

3.下面代码是生成jwt 生成 token , 拦截token认证即返回值等等一系列代码如下:

前端axios 封装如下 require.js

import axios from 'axios';
import {MessageBox, Message} from 'element-ui';
import store from '../js/store/store';
import {getToken} from '@/utils/auth';
import qs from 'qs';
// axios 封装
const service = axios.create({
    withCredentials: true,// 指定某个请求应该发送凭据。允许客户端携带跨域cookie,也需要此配置
    timeout: 20000, // request timeout
});
service.interceptors.request.use(config => {
        //1.只拦截以*.do 结尾的url ,其他不拦截.  ;
        if (!config.url.endsWith(".do")) {
            return config;
        }
        if (config.method === 'post') { // post请求时,处理数据
            config.data = qs.stringify({
                ...config.data //后台数据接收这块需要以表单形式提交数据,而axios中post默认的提交是json数据,所以这里选用qs模块来处理数据.
            });
        }
        //3.将token 放入axios header 里面,进入后台进行处理判断
        if (store.getters.token) {
            let token = getToken();
            config.headers['X-Token'] = token;
        }
        return config;
    }, error => {
        // axios出现请求异常
        return Promise.reject(error);
    }
);
// axios响应拦截器[这里对后台相应的非200状态拦截,并显示详细信息]
service.interceptors.response.use(response => {
    const res = response.data;
    if (response.status === 200) {  //成功
        //对自定义resultVO状态码做判断[即非运行异常]
        if (res['code'] === undefined) {
            return res;
        }
        if (res != null && res['code'] === 200) { //自定义状态码
            return res;
        } else if (res['code'] === 100000) {
            MessageBox.confirm('您必须重新登录, 或者点击取消保持当前页', '确认退出', {
                confirmButtonText: '重新登录',
                cancelButtonText: '取消',
                type: 'warning'
            }).then(() => {
                store.dispatch('userModule/actionResetToken').then(() => {
                    location.reload();
                });
            });
            return Promise.reject(new Error(res.message || 'Error'));
        } else {
            Message({
                message: res.message || 'Error',
                type: 'error',
                duration: 5 * 1000
            });
            return Promise.reject(new Error(res.message || 'Error'));
        }
    } else {
        Message({
            message: res.message || 'Error',
            type: 'error',
            duration: 5 * 1000
        });
        return Promise.reject(new Error(res.message || 'Error'));
    }
});

export default service;

后端获取token

public class TokenManager {

    public static Logger logger = Logger.getLogger(TokenManager.class);

    /**
     * 获取token
     * by lw on 2020年5月9日 21:45:22
     */
    public static String getToken(User user, HttpServletRequest request) {
        String sessionId = request.getSession().getId();
        long currentTime = System.currentTimeMillis() + 60 * 60 * 1000;//一小时有效时间
        Date end = new Date(currentTime);
        String token = JWT.create().withAudience(user.getId().toString()).withIssuedAt(new Date()).
                withExpiresAt(end).withKeyId(sessionId).sign(Algorithm.HMAC256(user.getPassword()));
        System.out.println("token===================="+token);
        return token;
    }

}

拦截器拦截token

package com.authentication;

import cn.gjing.result.ResultVo;
import com.alibaba.fastjson.JSON;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.system.auto_generator.SysUser;
import com.system.dao.UserMapper;
import org.apache.log4j.Logger;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;

/**
 * 拦截器,拦截请求中是否带有token ,以及token 的有效性
 */
public class AuthenticationInterceptor implements HandlerInterceptor {

    @Resource
    public UserMapper userMapper;

    private static Logger logger = Logger.getLogger(AuthenticationInterceptor.class);

    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) {
        String token = httpServletRequest.getHeader("X-Token");// 从 http 请求头中取出 token
        HttpSession session = httpServletRequest.getSession();
        // step1.如果不是映射到方法直接通过
        if (!(object instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) object;
        Method method = handlerMethod.getMethod();
        //检查是否有PassToken注释,有则跳过认证
        if (method.isAnnotationPresent(PassToken.class)) {
            PassToken passToken = method.getAnnotation(PassToken.class);
            if (passToken.required()) {
                return true;
            }
        }
        // step2.获取 token 中的 用户信息
        if (token == null || (! session.getId().equalsIgnoreCase(JWT.decode(token).getKeyId()))) {
            throwException2Axios(httpServletResponse);
            return false;
        }
        String userId;
        try {
            userId = JWT.decode(token).getAudience().get(0);

        } catch (JWTDecodeException j) {
            throwException2Axios(httpServletResponse);
            return false;
        }
        int id = Integer.parseInt(userId);
        SysUser user = userMapper.selectByPrimaryKey(id);
        if (user == null) {
            throwException2Axios(httpServletResponse);
            return false;
        }
        // step3. 验证 token 有消息
        JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
        try {
            jwtVerifier.verify(token);
        } catch (JWTVerificationException e) {
            throwException2Axios(httpServletResponse);
            return false;
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest,
                           HttpServletResponse httpServletResponse,
                           Object o, ModelAndView modelAndView) {
    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest,
                                HttpServletResponse httpServletResponse,
                                Object o, Exception e) {
    }

    /**
     * 返回给axios异常.提示重新登录
     */
    private void throwException2Axios(HttpServletResponse response){
        PrintWriter writer = null;
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        try {
            writer = response.getWriter();
            writer.write(JSON.toJSONString(ResultVo.error(100000,"无效的令牌")));
        } catch (IOException e){
            logger.error(e.getMessage(),e);
        } finally {
            if(writer != null){
                writer.close();
            }
        }
    }

}
package com.authentication;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PassToken {
    boolean required() default true;
}

登陆验证

@PostMapping(value = "/doLogin")
    public ResultVo login(SysUser sysUser, HttpServletRequest request) {
        try {
            if (ParamUtil.isEmpty(sysUser)||ParamUtil.isEmpty(sysUser.getUserName()) || ParamUtil.isEmpty(sysUser.getPassword())) {
                return ResultVo.error("提交信息错误,数据缺失");
            }
            List<User> list = userService.getUserInfoList(sysUser.getUserName());
            if (list.size() == 0) {
                return ResultVo.error("用户名:" + sysUser.getUserName() + "不存在");
            }
            if (list.size() > 1) {
                return ResultVo.error("数据库存在相同用户名:" + sysUser.getUserName());
            }
            User user = list.get(0);
            if (user.getPassword().equalsIgnoreCase(EncryptionUtil.md5(sysUser.getPassword()))) {
                String token = TokenManager.getToken(user, request);
                return ResultVo.success("登陆成功", token);
            } else {
                return ResultVo.error("你的密码错误,请重试。");
            }
        } catch (CannotGetJdbcConnectionException e) {
            e.printStackTrace();
            return ResultVo.error("数据库连接异常:" + e.getMessage());
        } catch (MyBatisSystemException e) {
            e.printStackTrace();
            return ResultVo.error("数据库连接异常:" + e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
            return ResultVo.error("异常详情:" + e.getMessage());
        }

最后是上的类的拦截器注册

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 配置包扫描器 注:不用spring容器下默认扫描(false),用其子容器下的springMVC容器扫描只扫描Controller层-->
    <context:component-scan base-package="com.*" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    <!-- 注册mvc注解驱动 -->
    <mvc:annotation-driven>
        <mvc:message-converters>
            <!--解决mybatis 延迟加载后对象属性为NULL 输出到前端报错的问题-->
            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                <property name="objectMapper" ref="jacksonObjectMapper"></property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>
    <bean id="jacksonObjectMapper" class="com.common.CustomMapper"></bean>


    <!-- 文件上传的配置 -->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="defaultEncoding" value="UTF-8"></property>
        <property name="maxInMemorySize" value="40960"></property> <!-- 内存占比,多出占用硬盘 -->
        <!-- 指定所上传文件的大小不能超过500MB -->
        <property name="maxUploadSize" value="524288000"></property>
    </bean>
    <!--拦截器处理请求token-->
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <mvc:exclude-mapping path="/loginController/doLogin.do"/> <!--排除登录的情况-->
            <mvc:exclude-mapping path="/userController/getUserInfoByToken.do"/> <!--排除登录的情况-->
            <bean class="com.authentication.AuthenticationInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>
</beans>

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值