Vue项目axios携带cookie JSESSIONID到Spring Boot服务器解决方案
0X01 问题描述
需求是要完成验证码校验进行登录后台的问题,在验证码生成由后端服务写好后将验证码图片发到前端页面上进行显示,此时把验证码字符串保存到Session域中,有关HttpSession技术简单描述如下:
/**
* HttpSession 服务端的技术
* 服务器会为每一个用户 创建一个独立的HttpSession
*
* HttpSession原理
* 当用户第一次访问Servlet时,服务器端会给用户创建一个独立的Session
* 并且生成一个SessionID,这个SessionID在响应浏览器的时候会被装进cookie中,从而被保存到浏览器中
* 当用户再一次访问Servlet时,请求中会携带着cookie中的SessionID去访问
* 服务器会根据这个SessionID去查看是否有对应的Session对象
* 有就拿出来使用;没有就创建一个Session(相当于用户第一次访问)
*
* 域的范围:
* Context域 > Session域 > Request域
* Session域 只要会话不结束就会存在 但是Session有默认的存活时间(30分钟)
*/
然后由前台携带相关数据进行发送登录请求,那么此时问题来了,后端在接收到数据后进行验证码校验、业务逻辑判断、查询数据库等操作,首先是从HttpSession域中取出对应的存入的验证码字符串,结果返回数据一直是验证码错误......然后去调试代码,结果发现此时从Session域获取的验证码字符串为null了,经过一番前后端调试,最终发现问题是axios默认发送请求是不携带cookie的
,也就是当我第一次请求验证码图片时,此时后端Servlet容器会当作一个新的客户端第一次请求数据,并携带为之生成对应的JSESSIONID存入Cookie进行返回,然后当第二次发送登录请求时,并没有携带返回的JSESSIONID数据,那么后端Servlet容器同样会将此次请求当作一个新的客户端的第一次请求数据,那么很显然,两次的请求的Session域也就不同了,通过后端调式打印的SessionID证实了这一点。
0x02 问题分析
知道了问题是因为Vue项目中axios不会自动携带cookie JSESSIONID到Spring Boot服务器
那么开始着手解决它,其实需要从两个方面进行。首先可以想到的是前端如何使Axios发送请求并且携带返回的Cookie数据,然后就是后端需要进行跨域的配置。
前端使Axios携带Cookie发送请求,只需要在Vue项目的main.js主配置文件中进行如下的设置:
// main.js
import axios from 'axios'
import VueAxios from 'vue-axios'
Vue.prototype.axios = axios;
Vue.use(VueAxios, axios);
axios.defaults.withCredentials = true; // 只需要添加这一句 允许跨域携带cookie信息
然后进行测试,发现其实并不能解决问题,还需要后端服务器进行相应的跨域请求处理,因为前端设置withCredentials = true的情况下,后端要设置Access-Control-Allow-Origin为你的源地址,例如http://localhost:8080,而且还要设置header(‘Access-Control-Allow-Credentials: true’);
0X03 问题解决
前端
使Axios发送请求并且携带返回的Cookie数据,只需要在Vue项目的main.js主配置文件中进行如下的设置:
axios.defaults.withCredentials = true; //允许跨域携带cookie信息
后端
进行相应的跨域请求处理:
package cn.imyjs.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class CorsConfig {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
// 重写父类提供的跨域请求处理的接口
@Override
public void addCorsMappings(CorsRegistry registry) {
// 添加映射路径
registry.addMapping("/**")
// 放行哪些原始域
.allowedOriginPatterns("*")
// 是否发送Cookie信息
.allowCredentials(true)
// 放行哪些原始域(请求方式)
.allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS", "PATCH")
// 放行哪些原始域(头部信息)
.allowedHeaders("*")
// 暴露哪些头部信息(因为跨域访问默认不能获取全部头部信息)
.exposedHeaders("Header1", "Header2")
// 预请求的结果有效期,默认1800分钟,3600是一小时
.maxAge(3600)
// .allowedOrigins("*"); // 报错!!
.allowedOrigins("http://localhost:8080");
}
};
}
}
注意:
- 1、
Access-Control-Allow-Origin
不能设置为*,不然cookie不会出现在http的请求头里,所以报错里说Access-Control-Allow-Origin不能是*。// .allowedOrigins("*"); /** * 报错: * Request processing failed; * nested exception is java.lang.IllegalArgumentException: * When allowCredentials is true, * allowedOrigins cannot contain the special value "*" since that cannot be * set on the "Access-Control-Allow-Origin" response header. * To allow credentials to a set of origins, list them explicitly or consider * using "allowedOriginPatterns" instead * * allowCredentials为true时, * AllowedOriginates不能包含特殊值“*”, * 因为不能在“Access Control Allow Originary”响应头上设置该值。 * 要允许凭据到一组原点,请显式列出它们,或者考虑使用“Apple Debug模式”。 * */
- 2、Access-Control-Allow-Origin为前端项目的源地址。
0X04 微信关注