什么是跨域问题
跨域本质是浏览器基于同源策略的一种安全手段,从这位大佬这里看到了两张比较生动的图,这里 copy 一下。
原因
解决方案
方案一
在后端使用 @CrossOrigin 注解
方案二
在前端使用 配置代理
解决案例
背景
在前端点击注册按钮后,会访问 http://127.0.0.1:5173/api/user/register,但是实际的后端接口为 http://127.0.0.1:8080/user/register,此时就会出现跨域问题。
解决方法一:
在对应 Controller 上添加 @CrossOrigin 注解
@Validated
@RestController
@RequestMapping("/user")
@CrossOrigin
public class UserController {
@Autowired
private UserService userService;
// 注入 redis
@Resource
private StringRedisTemplate stringRedisTemplate;
@PostMapping("/register")
public Result register(@Pattern(regexp = "^\\S{5,16}$") String username, @Pattern(regexp = "^\\S{5,16}$") String password){
// 查看用户名是否被占用
User user = userService.findByUsername(username);
if (null == user) {
userService.register(username, password);
}
// 已被占用
else{
return Result.error("用户名已被占用");
}
return Result.success();
}
@PostMapping("/login")
public Result login (@Pattern(regexp = "^\\S{5,16}$") String username, @Pattern(regexp = "^\\S{5,16}$") String password){
// 查询用户是否存在
User user = userService.findByUsername(username);
// 判断用户是否存在
if (user == null) {
return Result.error("用户名不存在");
}
// 判断密码是否正确
else {
if (Md5Util.getMD5String(password).equals(user.getPassword())) {
// 登录成功
HashMap<String, Object> claims = new HashMap<>();
claims.put("id", user.getId());
claims.put("username", user.getUsername());
String token = JwtUtil.getToken(claims);
// 将 token 存入 redis 中
ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();
opsForValue.set(token, token, 1, TimeUnit.HOURS);
return Result.success(token);
}
}
return Result.error("密码错误");
}
}
解决方法二
在前端使用配置代理的方式。
request.js 代码如下所示,该代码的作用为生成一个请求示例
//定制请求的实例
//导入axios npm install axios
import axios from 'axios';
//定义一个变量,记录公共的前缀 , baseURL
const baseURL = 'http://localhost:8080';
const instance = axios.create({baseURL})
//添加响应拦截器
instance.interceptors.response.use(
result=>{
return result.data;
},
err=>{
alert('服务异常');
return Promise.reject(err);//异步的状态转化成失败的状态
}
)
export default instance;
访问后端方法代码
// 导入 request.js 请求工具
import request from '../utils/request'
// 提供调用注册接口的函数
export const userRegisterService = (registerData) => {
const params = new URLSearchParams();
for (let key in registerData) {
params.append(key, registerData[key]);
}
return request.post('user/register', params);
}
在用户点击注册按钮后,代码会调用 userRedigterService 方法请求 http://127.0.0.1:5173/api/user/register,而现在需要使用配置代理的方法将其转为 http://127.0.0.1:8080/user/register。
第一步:修改 request.js 代码
将 const baseURL = ‘http://localhost:8080’; 这行代码修改为 const baseURL = ‘/api’;
第二步:修改 vite.config.js 文件
在里面添加对应的配置
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
// server 部分为需要添加的代码
server: {
proxy: {
'/api': { // 获取路径中包含了 /api 的请求
target: 'http://127.0.0.1:8080', // 后台服务所在的源
changeOrigin: true, // 修改源
rewrite: path => path.replace(/^\/api/, '') // api 替换为 ''
}
}
}
})
经过上述配置后,当用户访问 http://127.0.0.1:5173/api/user/register 时,系统发现请求路径中包含 ‘api’ 关键字,会根据对应配置条件中的 target 的值将其转化为 http://127.0.0.1:8080/api/user/register,然后再根据 rewriter 中定义的操作(这里是通过正则表达式将 'api’关键字转为空字符串),所以最终请求的路径为 http://127.0.0.1:8080/user/register,此时就解决了跨域问题。