springboot-vue前后端分离session过期重新登录的实现
2020-12-13 03:54
标签:分离 val mvc拦截器 进入 测试过程 max pre oca use 而后我是强行塞入指定响应头达到目的的(见后端拦截器),这样做不优雅,希望知道原因的小伙伴可以不吝指教下 XD.. springboot-vue前后端分离session过期重新登录的实现 标签:分离 val mvc拦截器 进入 测试过程 max pre oca use 原文地址:https://www.cnblogs.com/noodlerkun/p/11094564.htmlspringboot-vue前后端分离session过期重新登录的实现
简单回顾cookie和session
开发环境
springboot后端提供接口
pom.xml
application.properties
server.port=8080
TestCtrller.java
@RestController
public class TestCtrller extends BaseCtrller{
//session失效化-for功能测试
@GetMapping("/invalidateSession")
public BaseResult invalidateSession(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if(session != null &&
session.getAttribute(SysConsts.Session_Login_Key)!=null) {
request.getSession().invalidate();
getServletContext().log("Session已注销!");
}
return new BaseResult(true);
}
//模拟普通ajax数据请求(待登录拦截的)
@GetMapping("/hello")
public BaseResult hello(HttpServletRequest request) {
getServletContext().log("登录session未失效,继续正常流程!");
return new BaseResult(true, "登录session未失效,继续正常流程!");
}
//登录接口
@PostMapping("/login")
public BaseResult login(@RequestBody SysUser dto, HttpServletRequest request) {
//cookie信息
Cookie[] cookies = request.getCookies();
if(null!=cookies && cookies.length>0) {
for(Cookie c:cookies) {
System.out.printf("cookieName-%s, cookieValue-%s, cookieAge-%d%n", c.getName(), c.getValue(), c.getMaxAge());
}
}
/**
* session处理
*/
//模拟库存数据
SysUser entity = new SysUser();
entity.setId(1);
entity.setPassword("123456");
entity.setUsername("Richard");
entity.setNickname("Richard-管理员");
//验密
if(entity.getUsername().equals(dto.getUsername()) && entity.getPassword().equals(dto.getPassword())) {
if(request.getSession(false) != null) {
System.out.println("每次登录成功改变SessionID!");
request.changeSessionId(); //安全考量,每次登陆成功改变 Session ID,原理:原来的session注销,拷贝其属性建立新的session对象
}
//新建/刷新session对象
HttpSession session = request.getSession();
System.out.printf("sessionId: %s%n", session.getId());
session.setAttribute(SysConsts.Session_Login_Key, entity);
session.setAttribute(SysConsts.Session_UserId, entity.getId());
session.setAttribute(SysConsts.Session_Username, entity.getUsername());
session.setAttribute(SysConsts.Session_Nickname, entity.getNickname());
entity.setId(null); //敏感数据不返回前端
entity.setPassword(null);
return new BaseResult(entity);
}
else {
return new BaseResult(ErrorEnum.Login_Incorrect);
}
}
}
MyWebMvcConfig.java
@Configuration
public class MyWebMvcConfig implements WebMvcConfigurer{
//全局跨域配置
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") //添加映射路径
.allowedOrigins("http://localhost:8081") //放行哪些原始域
.allowedMethods("*") //放行哪些原始域(请求方式) //"GET","POST", "PUT", "DELETE", "OPTIONS"
.allowedHeaders("*") //放行哪些原始域(头部信息)
.allowCredentials(true) //是否发送Cookie信息
// .exposedHeaders("access-control-allow-headers",
// "access-control-allow-methods",
// "access-control-allow-origin",
// "access-control-max-age",
// "X-Frame-Options") //暴露哪些头部信息(因为跨域访问默认不能获取全部头部信息)
.maxAge(1800);
}
//注册拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyLoginInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/login")
.excludePathPatterns("/invalidateSession");
//.excludePathPatterns("/static/**");
}
}
MyLoginInterceptor.java
public class MyLoginInterceptor implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
request.getServletContext().log("MyLoginInterceptor preHandle");
HttpSession session = request.getSession();
request.getServletContext().log("sessionID: " + session.getId());
Optional
MyCtrllerAdvice.java
@ControllerAdvice(
basePackages = {"com.**.web.*"},
annotations = {Controller.class, RestController.class})
public class MyCtrllerAdvice {
//全局异常处理-ajax-json
@ExceptionHandler(value=Exception.class)
@ResponseBody
public BaseResult exceptionForAjax(Exception ex) {
if(ex instanceof BusinessException) {
return new BaseResult((BusinessException)ex);
}else {
return new BaseResult(ex.getCause()==null?ex.getMessage():ex.getCause().getMessage());
}
}
}
vue-cli(2.x)前端
router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/components/Home'
import Login from '@/components/Login'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Login',
component: Login
},
{
path: '/home',
name: 'Home',
component: Home
}
]
})
config/index.js
module.exports = {
dev: {
// Paths
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {},
// Various Dev Server settings
host: 'localhost', // can be overwritten by process.env.HOST
port: 8081, // can be overwritten by
//...
src/main.js
import axios from 'axios'
axios.defaults.baseURL = 'http://localhost:8080'
//axios.defaults.timeout = 3000
axios.defaults.withCredentials = true //请求发送cookie
// 添加请求拦截器
axios.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
console.log('in interceptor, request config: ', config);
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
// 对响应数据做点什么
console.log('in interceptor, response: ', response);
if(!response.data.success){
console.log('errCode:', response.data.errCode, 'errMsg:', response.data.errMsg);
Message({type:'error',message:response.data.errMsg});
let code = response.data.errCode;
if('login02'==code){ //登录session失效
//window.location.href = 'http://www.mamicode.com/';
console.log('before to login, current route path:', router.currentRoute.path);
router.push({path:'/', query:{redirect:router.currentRoute.path}});
}
}
return response;
}, function (error) {
// 对响应错误做点什么
console.log('in interceptor, error: ', error);
Message({showClose: true, message: error, type: 'error'});
return Promise.reject(error);
});
sessionStorage
初级版)src/main.js
//URL跳转(变化)拦截
router.beforeEach((to, from, next) => {
//console.log(to, from, next) //
if(to.name=='Login'){ //本身就是登录页,就不用验证登录session了
next()
return
}
if(!sessionStorage.getItem('username')){ //没有登录/登录过期
next({path:'/', query:{redirect:to.path}})
}else{
next()
}
})
前端进入即是login页,用户名和密码正确则后端保存登录的Session,前端登录成功跳转home页,点击‘功能测试‘则是正常json响应(Session有效)。如何在本页中主动将Session失效,再次功能测试则会被拦截,跳转登录页。碰到的问题
header
描述:本身跨域设置在后端,所以前端所有的请求都是跨域的,但是当我主动将Session失效,然后点击功能测试触发登录拦截,拦截器抛出Session失效异常,由全局异常处理捕捉并正常地响应json
,此时响应头中就少了console
中提示的项:XMLHttpRequest cannot load http://localhost:8080/hello. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8081' is therefore not allowed access.
//PS:查看network可以看到请求是200的,但是前端不能拿到响应
拓展话题(链接坑待填)
暴脾气用户禁掉浏览器cookie?
过滤器是在servlet.service()请求前后拦截,springmvc拦截器则是在handle方法前后拦截,粒度不一样。联系&交流
文章标题:springboot-vue前后端分离session过期重新登录的实现
文章链接:http://soscw.com/essay/28477.html