vite vue3 下配置 history 模式路由+解决chrome94解决私有网络请求
vite 下,打包后采用history模式详解
dev 模式
-
利用vite 配置代理,可以解决前端浏览器限制跨域问题(当然后端要自己配置好)
export default defineConfig({ // 配置在打包时,保障所有css\js能被找到 base: './', //自带配置 plugins: [vue()], resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) } }, // 配置/api代理 server: { proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true, rewrite: (path) => path.replace(/^\/api/, '') } } }, //打包前先清空原有打包文件 build: { emptyOutDir: true, } })
配置.env
在dev 环境,默认会读取这个里面的内容
# .env.development VITE_BASE_API=/api VITE_BASE_URL=/vaccinationInfo VITE_BASE_ENV=dev
配置axios
import axios from "axios"; const service = axios.create({ baseURL: import.meta.env.VITE_BASE_API as string, timeout: 3600 })
这样在dev环境下,就可以使用代理来访问后端了
pro模式
打包时,会自动识别 .env.production
# .env.production
VITE_BASE_API=http://localhost:8080
VITE_BASE_URL=/vaccinationInfo
VITE_BASE_ENV=pro
由于生产模式时,代理配置时不生效的,所以VITE_BASE_API直接指向服务器地址
history模式 时 还要进行以下配置
router\index.ts
history: createWebHistory(import.meta.env.VITE_BASE_URL as string),
用一个指定的url地址
nginx 配置
当然,打包后放到nginx也需要配置
location /vaccinationInfo {
alias /usr/share/nginx/html/vaccinationInfo;
index index.html index.htm;
try_files $uri $uri/ /vaccinationInfo/index.html; # 解决页面刷新404
}
然后在html中新建vaccinationInfo文件夹,把打包好的文件丢进去就行
hash模式
如果不想每写一个项目都配置一次nginx后端,则可以用hash模式
- 子项目情况
例如把项目放在nginx的html/test/文件夹下
vite.config.ts
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))
}
},
// 配置为要放的文件夹
base: '/test/'
})
router文件夹下index.ts
import { createRouter, createWebHashHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
const router = createRouter({
// 把createWebHistory替换为createWebHashHistory即可,其他什么都不用配置
// 同样也适用于createWebHistory模式,即只修改vite的base为nginx文件夹,而不修改createWebHistory()括号里的内容
// 如果vite的base改为/test/,createWebHistory('kk'),访问http://localhost/test/会自动跳转为http://localhost/kk/,首页内容不会加载,不要这么写
// import.meta.env.BASE_URL指向的就是vite配置中的base,默认是'/',配置为''或者'./'才可以在打包的index.html中指向./相对路径
history: createWebHashHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/about',
name: 'about',
component: () => import('../views/AboutView.vue')
}
]
})
export default router
hash模式下,
访问about,网页为 http://localhost/test/#/about
后端配置
写一个配置类
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
/**
* 允许跨域,以及自行配置
*
* @param registry 跨域注册器
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*") // SpringBoot2.4.0以上[allowedOriginPatterns]代替[allowedOrigins]
.allowedMethods("*")
.maxAge(3600)
.allowedHeaders("*")
.allowCredentials(true);
}
}
如果需要配置拦截器拦截JWT,可以采取以下操作
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
private JWTTokenInterceptor jwtTokenInterceptor;
private InterceptorPathBean interceptorPathBean;
@Autowired
public void setJwtTokenInterceptor(JWTTokenInterceptor jwtTokenInterceptor) {
this.jwtTokenInterceptor = jwtTokenInterceptor;
}
@Autowired
public void setInterceptorPathBean(InterceptorPathBean interceptorPathBean) {
this.interceptorPathBean = interceptorPathBean;
}
/**
* 允许跨域,以及自行配置
*
* @param registry 跨域注册器
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*") // SpringBoot2.4.0以上[allowedOriginPatterns]代替[allowedOrigins]
.allowedMethods("*")
.maxAge(3600)
.allowedHeaders("*")
.allowCredentials(true);
}
/**
* 添加API拦截器,对所有数据API进行拦截
*
* @param registry 拦截器注册器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册拦截规则
InterceptorRegistration interceptorRegistration = registry.addInterceptor(jwtTokenInterceptor);
// 拦截配置
interceptorRegistration.addPathPatterns(interceptorPathBean.getInclude());
}
}
重写addInterceptors 方法
配置JWT拦截器
@Component
public class JWTTokenInterceptor implements HandlerInterceptor {
@Resource
private JWTResult jwtResult;
/**
* 拦截对数据API的请求,判断jwt令牌是否有效,没有则返回401
*
* @param request 请求
* @param response 响应
* @param handler 处理器
* @return 是否继续执行,true继续执行,false不继续执行
* @throws Exception 异常
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//非简单请求会预先使用OPTIONS方法请求一次,这里直接返回true
if (request.getMethod().equals("OPTIONS")) {
response.setStatus(200);
//在拦截器中设置允许跨域
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Headers", "*");
response.setHeader("Access-Control-Allow-Methods", "*");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Max-Age", "3600");
return true;
}
//业务逻辑,自行发挥
//这才是真正的请求,需要验证jwt令牌
//请求数据API时的目标url
String path = request.getRequestURI();
String jwt = request.getHeader("Authorization");
//对每次数据API请求进行拦截,如果jwt令牌不合法,则返回401;通过则继续放行,因此数据controller不需要再次判断jwt令牌是否合法
boolean verify = jwtResult.verify(jwt,path);
//如果校验不通过
if (!verify) {
response.setContentType("application/json;charset=utf-8");
response.getWriter().write("{\"code\":401,\"msg\":\"jwt校验失败\"}");
}
return verify;
}
}
以上是重点处理 OPTIONS 预先请求,这个在非简单请求时会预先发出,典型场景就是打包后的前端工程,在请求后端是就会发出这个OPTIONS请求。
后面那些就是业务逻辑,自行发挥即可
补充几个文件
InterceptorPathBean
@Data
@Component
@ConfigurationProperties(prefix = "interceptor-config.path") // 配置文件的前缀
public class InterceptorPathBean {
/*
* 需要拦截的路径
*/
private List<String> include;
}
application.yml
# 拦截器路径拦截
interceptor-config:
path:
#该路径下任何类型请求均拦截
include:
- /telInfo/**
- /vaccinationInfo/**
以上,就可以在vue中站着用history模式了
解决私有网络请求
chrome升级94版本以后,禁止私有网络请求
-
私有网络请求是其目标服务器的 IP 地址比获取请求发起者的 IP 地址更私有的请求。例如,从公共网站 ( http://example.com) 到私有(private)网站 (http://192.168.0.1) 的请求,或从私有网站到 localhost 的请求
-
具体划分,不在下面表格的都属于public网站,等级由public-》private-》local
Address block Name Reference Address space 127.0.0.0/8
IPv4 Loopback [RFC1122] local 10.0.0.0/8
Private Use [RFC1918] private 100.64.0.0/10
Carrier-Grade NAT [RFC6598] private 172.16.0.0/12
Private Use [RFC1918] private 192.168.0.0/16
Private Use [RFC1918] private 198.18.0.0/15
Benchmarking [RFC2544] local 169.254.0.0/16
Link Local [RFC3927] private ::1/128
IPv6 Loopback [RFC4291] local fc00::/7
Unique Local [RFC4193] private fe80::/10
Link-Local Unicast [RFC4291] private ::ffff:0:0/96
IPv4-mapped [RFC4291] see mapped IPv4 address -
不受影响的请求方式
- public请求public
- private、local请求public
- local请求public、private
-
受影响的请求方式
- public请求private、local
- private请求local
-
解决方案
-
将发起请求的页面升级为https
-
发起请求页面升级方式,利用nginx,单独创建一个
conf
#default_ssl.conf server { listen 443 ssl; server_name localhost; # ssl证书地址 ssl_certificate /etc/nginx/cert/68.1.1.1.crt; ssl_certificate_key /etc/nginx/cert/68.1.1.1.key; # ssl配置 # 客户端重用会话缓存时间 ssl_session_timeout 12h; # 协议类型 ssl_protocols TLSv1.2 TLSv1.3; #请按照以下套件配置,配置加密套件,写法遵循 openssl 标准。 ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE; # 用服务端协议优先 ssl_prefer_server_ciphers on; # 共用ssl缓存的大小 ssl_session_cache shared:SSL:5m; # 监听的前端页面 location /smpki { alias /usr/share/nginx/html/smpki; index index.html index.htm; try_files $uri $uri/ /smpki/index.html; # 解决页面刷新404 } }
-
生成自签证书
gencert.sh
#!/bin/sh # create self-signed server certificate: read -p "Enter your domain [www.example.com]: " DOMAIN echo "Create server key..." openssl genrsa -des3 -out $DOMAIN.key 2048 echo "Create server certificate signing request..." SUBJECT="/C=US/ST=Mars/L=iTranswarp/O=iTranswarp/OU=iTranswarp/CN=$DOMAIN" openssl req -new -subj $SUBJECT -key $DOMAIN.key -out $DOMAIN.csr echo "Remove password..." mv $DOMAIN.key $DOMAIN.origin.key openssl rsa -in $DOMAIN.origin.key -out $DOMAIN.key echo "Sign SSL certificate..." openssl x509 -req -days 3650 -in $DOMAIN.csr -signkey $DOMAIN.key -out $DOMAIN.crt echo "TODO:" echo "Copy $DOMAIN.crt to /etc/nginx/ssl/$DOMAIN.crt" echo "Copy $DOMAIN.key to /etc/nginx/ssl/$DOMAIN.key" echo "Add configuration in nginx:" echo "server {" echo " ..." echo " listen 443 ssl;" echo " ssl_certificate /etc/nginx/ssl/$DOMAIN.crt;" echo " ssl_certificate_key /etc/nginx/ssl/$DOMAIN.key;" echo "}"
- linux 服务器中,
sh gencert.sh
即可,随便属于域名都可以,密码重复4次,,用 D O M A I N . c r t , DOMAIN.crt, DOMAIN.crt,DOMAIN.key这两个
- linux 服务器中,
-
-
调整chrome的
Block insecure private network requests
(不推荐)
-
-
注意禁止混合请求
-
由于chrome的
block:mixed-content
存在,禁止https请求http网站。因此即使不受私有网络请求
的限制,也需要把被请求的网站升级为https -
http->http、http->https不受限制
-
并不是所有的http资源都受到这个禁止混合请求的影响,主要是以下几种
类型 常用的几个使用场景 mixed passive content <img>
、<audio>
、<video>
、<object>
mixed active content <script>
、<link>
、<iframe>
、XMLHttpquest
、fetch()
- 大部分浏览器直接阻止mixed active content,有部分浏览器会阻止mixed passive content
-
解决方案
-
将被请求的网站升级https
-
方案一:springboot直接升级为https
-
明显缺陷就是在自签SSL证书的情况下,证书不被浏览器信任,由于没有点击浏览器继续浏览这一步,axios发送的请求会报
net::ERR_CERT_AUTHORITY_INVALID
错误,无论http->https,https->https,都无法发送成功 -
无论是配置axios忽略证书校验
如图
-
同时在vite下,还不能用node中的模块https,还必须polyfill, 参考,但是在vite4.4.1版本下,dev环境成功过,打包还出现问题,放弃
-
或者是配置vue信任本地证书
-
都无法成功,究其原因是证书不能在本地主机上工作,所有的ssl证书都必须在域名上工作
-
-
方案二:用一个https页面来做代理
-
如https://68.1.1.1:443的nginx前端(就是原本发起页的同域,共用一个端口443),代理spring boot http://68.1.1.1:8080
# default_ssl.conf server { listen 443 ssl; server_name localhost; # ssl证书地址 ssl_certificate /etc/nginx/cert/68.1.1.1.crt; ssl_certificate_key /etc/nginx/cert/68.1.1.1.key; # ssl配置 # 客户端重用会话缓存时间 ssl_session_timeout 12h; # 协议类型 ssl_protocols TLSv1.2 TLSv1.3; #请按照以下套件配置,配置加密套件,写法遵循 openssl 标准。 ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE; # 用服务端协议优先 ssl_prefer_server_ciphers on; # 共用ssl缓存的大小 ssl_session_cache shared:SSL:5m; # 监听的前端页面 location /smpki { alias /usr/share/nginx/html/smpki; index index.html index.htm; try_files $uri $uri/ /smpki/index.html; # 解决页面刷新404 } # 代理后端的监听页面 location ^~ /proxy_8080/ { proxy_pass http://68.1.1.1:8080/; } }
-
请求 https://68.1.1.1:443/proxy_8080/就是请求http://68.1.1.1:8080/
-
这样后端spring boot就不用改造为http,打包时只需要判断是https还是http,就知道用https代理,还是直接http请求
# 在.env.production中新增 VITE_BASE_API_HTTPS = https://68.1.1.1:443/proxy_8080 # axios可以如下操作 let isHttps = 'https:' == document.location.protocol let url = import.meta.env.VITE_BASE_API as string // dev环境肯定不是https,所以.env.dev即使没有定义VITE_BASE_API_HTTP也没关系 // pro环境如果不是https,就直接走.env.pro中预定义的http后端 if (isHttps){ url = import.meta.env.VITE_BASE_API_HTTPS as string }
-
注意,同一个页面用代理,必须在 location 后面加上
^~
-
适用于springboot后端接口
或者单独开一个端口来监听代理,创建新的
.conf
,监听441端口# default_ssl_proxy.conf server { listen 441 ssl; server_name localhost; # ssl证书地址 ssl_certificate /etc/nginx/cert/68.50.10.87.crt; ssl_certificate_key /etc/nginx/cert/68.50.10.87.key; # ssl配置 # 客户端重用会话缓存时间 ssl_session_timeout 12h; # 协议类型 ssl_protocols TLSv1.2 TLSv1.3; #请按照以下套件配置,配置加密套件,写法遵循 openssl 标准。 ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE; # 用服务端协议优先 ssl_prefer_server_ciphers on; # 共用ssl缓存的大小 ssl_session_cache shared:SSL:5m; # 用根路径代理后端8080端口 location / { proxy_pass http://68.1.1.1:8080/; } # 代理后端8080端口 # location /proxy_8080/ { # proxy_pass http://68.1.1.1:8080/; # } }
- 请求 https://68.1.1.1:441/就是请求http://68.1.1.1:8080/
- 请注意,新代理的ssl证书不要又用其他SSL证书,因为只有用与前端相同的证书,才能让浏览器信任此自签证书,否则AXIOS时又因为新的自签证书而导致
net::ERR_CERT_AUTHORITY_INVALID
- 如果时新的端口代理,location不需要加
^~
- 如果是代理普通网页最好用根域名做代理,虽然请求内容是没问题。但可以原网页有些静态资源如JS、css走相对路径,会加载不完全。
-
-
-
-
-
更多推荐
所有评论(0)