概要
跨域请求
(Cross-Origin Requests)指的是在一个网页中加载的资源来自与当前网页不同的域、协议或端口。浏览器出于安全考虑,默认会限制这些跨域请求,这种限制称为同源策略
(Same-Origin Policy)。
同源策略
同源策略要求一个网页只能与其源相同的资源进行交互,即:
- 域名相同
- 协议相同(如 HTTP 和 HTTPS)
- 端口相同
注意:只要域名
端口
协议
一个不同就跨域了
跨域问题复现
前端:
<template>
<div>
<button @click="getData">点击发送get请求</button>
<h1>{{ data }}</h1>
</div>
</template>
<script setup lang="ts" name="Child">
import http from "axios";
import { ref } from 'vue'
let baseUrl = "http://localhost:8899/api"
let data = ref("")
function getData() {
http.get(baseUrl + "/get").then(response => {
data.value = response.data;
}).catch(error => {
console.log(error);
})
}
</script>
后端:
@RestController
@RequestMapping(value = "/api")
public class TestController {
@GetMapping(value = "/get")
public String get() {
return "get method";
}
}
解决跨域
方法1
类或方法上添加@CrossOrigin(value="http://localhost:5173/")
应用级别
:将 @CrossOrigin 注解放在类级别,意味着该类中的所有端点都允许来自指定来源的跨域请求。
方法级别
:可以将 @CrossOrigin 注解放在特定方法上,以便仅允许某些特定方法的跨域请求。
@RestController
@RequestMapping(value = "/api")
@CrossOrigin(value="http://localhost:5173/")
public class TestController {
@GetMapping(value = "/get")
public String get() {
return "get method";
}
要是每一个类都写挺麻烦的。
方法2
写一个配置类放入容器
@Configuration
public class CrossConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") //指定允许跨域请求的路径模式
.allowedOrigins("http://localhost:5173/")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.allowCredentials(true); //指示是否允许发送凭据(如 Cookies、Authorization 头等)
}
}
方法3
过滤器
@Component
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse res = (HttpServletResponse) response;
HttpServletRequest req = (HttpServletRequest) request;
res.setHeader("Access-Control-Allow-Origin", "http://localhost:5173");
res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
res.setHeader("Access-Control-Allow-Headers", "Origin, Content-Type, Accept, Authorization");
res.setHeader("Access-Control-Allow-Credentials", "true");
// OPTIONS 预检请求,服务器会返回 200 状态码
if ("OPTIONS".equalsIgnoreCase(req.getMethod())) {
res.setStatus(HttpServletResponse.SC_OK);
return;
}
chain.doFilter(request, response);
}
}
jwt拦截器验证token
可能会遇到jwt验证token失败
可以加上预检请求
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
return true;
}
@Component
public class InterceptorHandleConfig implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
return true;
}
String token = request.getHeader("Authorization");
if (token == null) {
response.setStatus(401);
return false;
}
boolean verify = JWTUtil.verify(token, "123456".getBytes());
if (!verify) {
response.setStatus(401);
return false;
}
return true;
}
}
当然可以直接使用filter
Filter 是属于 Servlet 规范的一部分,用于对请求和响应进行底层处理,而 Interceptor 是 Spring 框架提供的用于增强 Controller 功能的机制。Filter 的执行优先于 Interceptor
。
防止请求存在缓存
防止请求存在缓存的策略对于保证数据的实时性和避免旧数据的获取非常重要。
解决方法,请求参数加一个时间戳,当然其他方法自己百度啦
function getData() {
http.get(baseUrl + "/get?timestamp=" + new Date().getTime(), {
headers: {
'Authorization': token
}
}).then(response => {
data.value = response.data;
}).catch(error => {
console.log(error);
})
}