我遇到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 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());
}
最后是上的类的拦截器注册