唠嗑部分
在前后端分离项目中,ajax是数据交互中不可缺少的一个js库,它能够实现局部刷新,替代原生全局刷新对用户的冲击感,提升了用户体验,目前像jQuery-ajax, 基于Promise风格的axios是时代的主流,但是殊不知,ajax受同源策略的限制
同源策略是浏览器的一种保护机制,是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源,如果两个 URL 的协议、ip地址、端口都相同的话,则这两个 URL 是同源
违背同源策略就是跨域访问
例如 : 3000服务器给8080服务器发送数据请求,ajax放行,数据发送成功,8080收到请求,将数据返回给3000服务器,但是由于同源策略的影响,3000服务器将ajax带来的数据拒收了
开发环境解决方案:
- 配置代理,适用于React、Vue等工程化项目,也可以自己搭建开发代理服务器
- 服务器统一解决,一劳永逸
生产环境部署都是通过nginx解决
下面让我们一起来重温一下
案发现场
环境搭建,两个版本,分别使用Vue脚手架和非脚手架版,简单的登录功能,用户输入用户名密码实现登录逻辑
简单的login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录base版</title>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue@2.6.0"></script>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/axios/1.3.2/axios.js"></script>
</head>
<body>
<div id="root">
<form @submit.stop.prevent="login">
<input v-model="loginForm.username" type="text" required placeholder="用户名"/><br/>
<input v-model="loginForm.password" type="password" required placeholder="密码"/><br/>
<button type="submit">登录</button>
</form>
</div>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
loginForm: {
username:'',
password: ''
}
},
methods: {
login(){
console.log(this.loginForm)
axios({
url: 'http://127.0.0.1:2023/auth/login',
method: 'post',
data: this.loginForm
}).then(res => {
const { data } = res
console.log(data)
}).catch(err => {
console.log(err)
})
}
}
})
</script>
</body>
</html>
登录接口(模拟数据-不是重点)
/**
* @Project: spring-boot-cross-domain-demo
* @Author: cxs2014501@163.com
* @Create: 2023/2/9 13:27
* @Description:
**/
@RestController
@RequestMapping("/auth")
public class LoginController {
private static final String ACCOUNT = "admin";
private static final String PWD = "admin1234";
@PostMapping("/login")
public Map<String, Object> login(@RequestBody LoginDTO dto){
Map<String, Object> map = new HashMap<>();
if (ACCOUNT.equals(dto.getUsername()) && PWD.equals(dto.getPassword())) {
LoginVO vo = new LoginVO();
BeanUtils.copyProperties(dto, vo);
vo.setLoginTime(LocalDateTime.now());
map.put("data", vo);
map.put("msg", "登陆成功");
} else {
map.put("msg", "登陆失败,用户名或密码错误");
}
return map;
}
}
跨域复现 |
言归正传
非脚手架版后端统一解决跨域
SpringBoot添加Mvc配置类
/**
* @Project: spring-boot-cross-domain-demo
* @Author: cxs2014501@163.com
* @Create: 2023/2/9 15:11
* @Description:
**/
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
// 解决同源策略引起的跨域
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") //1.代表当前的哪些方法添加请求头
.allowedMethods("*") //2.设置允许的方法
.allowedHeaders("*") //3.设置请求头
.allowedOriginPatterns("*") //4.设置是否跨域
.allowCredentials(true); //5.设置是否允许携带cookie
}
}
测试非脚手架版后端统一解决跨域 |
脚手架版配置代理解决方案(将之前统一解决的代码注释掉)
代理服务器无ajax引擎,不受同源策略限制
原理图 |
创建vue.config.js文件,配置开发代理
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://127.0.0.1:2023/',
pathRewrite:{'^/api':''}, // 重写路径
ws: true, // 用于支持websocket
changeOrigin: true // 控制请求头中的host,设置服务器看到的请求来源
}
}
}
}
组件比较简单,改了默认的Helloworld组件,样式请忽略
<template>
<div class="hello">
<form @submit.stop.prevent="login">
<input v-model="loginForm.username" type="text" required placeholder="用户名"/><br/>
<input v-model="loginForm.password" type="password" required placeholder="密码"/><br/>
<button type="submit">登录</button>
</form>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'HelloWorld',
props: {
msg: String
},
data(){
return {
loginForm: {
username:'',
password: ''
}
}
},
methods: {
login(){
console.log(this.loginForm)
axios({
url: '/api/auth/login',
method: 'post',
data: this.loginForm
}).then(res => {
const { data } = res
console.log(data)
}).catch(err => {
console.log(err)
})
}
}
}
</script>
登录测试 |
我们可以看到,前端代理的方式也完美解决了跨域,十分简单,还可以配置多个代理
以上两种方式均可以解决代理,也许在你开发的时候并没有出现,因为在企业中,各个环境已经提前弄好了,你根本没有机会注意到,况且后端开发在本地根本没有机会调试页面,前端在本地也只能mock数据
生产环境跨域解决方式
1、前后端项目打包
前端项目在黑窗口执行:yarn run build |
服务端执行mvn clean package -DskipTests |
虚拟机ip为10.10.10.10
启动java项目
上传包&构建镜像 |
启动项目 |
看到上述日志即启动成功
安装nginx,配置文件
10.10.10.10机器已安装过nginx,就不在docker搭建了,将打包后的vue文件上传至nginx的html目录下
配置文件
server {
listen 80;
server_name localhost;
location / {
index index.html;
root html;
try_files $uri $uri/ /index.html;
}
location ^~ /api/ {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
proxy_pass http://127.0.0.1:2023/;
}
}
访问10.10.10.10并测试 |
部署完毕,测试通过,本文是将前后端均放在同一台服务器上,属于同源,放在不同机器也是OK的,就不单独搭建虚拟机测试了,nginx没有ajax引擎,不受同源策略限制