缘起
香港的服务器到期了,续费要2K,新购买一个1K.反正我是用docker部署的,很快就可以转移完了。
于是我就购买了一个新的香港服务器,转移完,把域名解析到新的服务器上。
一切正常访问,我把原来的服务器停机了,仍然正常访问。大概持续了一周,直到前天下午,我刷着刷着就error了。
缘孽
当时在上班,没有太多时间,最简单的方式就是重启程序。重启后还是不行,我只好查看log,发现log正常,也没有什么报错之类的。前端500,肯定是前端的问题。看前端的log是一些字段没有,再看后台还是没有错误。
f12调试一看,原来是跨域的问题。api.sunofbeach.net从xxx.sunofbeache.net访问被拦截了。所以数据返回不了,导致前端某些字段没有,因此error.
为什么会突然跨域呢?我单独把连接拷贝出来,放到浏览器上访问,提示如下:
也就是说,api.sunofbeach.net被拦截了,所以访问不了服务器,无法获取数据。
缘因
2014年的时候购买的域名是sunofbeaches.com(已经备案)目前用于博客。后来觉得这个太长了,就购买了一个sunofbeach.net,这个没有备案.sunofbeach.com被人购买了。
想用www.sunofbach.net的域名,并且把这个做大做强。个人网站嘛,肯定是各种限制的,于是就使用中国香港的服务器,这样子可以不用备案,正常使用。而www.sunofbeaches.com的服务器呢,在中国大陆的,备案的了。想不浪费资源,就把后台程序跑在了中国大陆的服务器上,而前端的程序跑在中国香港的服务器上。服务器之间通过ip访问。这样子,就可以不浪费资源,又符合我们国家的法律规定了。
一直这样子正常使用,几年了。
真到最近换了服务器,同样的方式,被腾讯拦截了。原因是api.sunofbeach.net这个网址有流量流向中国大陆的服务器。
所以也就有了前面访问不了的结果了。
缘灭
找腾讯肯定是解决不了的,怎么办呢?换一个已经备案的域名。
换完就可以访问了,但是登录不了。登录的时候,验证码会自动写到cookie里一个key,作为与当前验证码对应,而域名的不一样,写cookie失败,所以就有了大家登录验证码一直错误了。
怎么办呢?改代码,把图片的key通过response的header写到前端,当前端接收到图片以后同时要把response里header的验证码key保存下来,提交验证码时,要随着header提交到服务器,这样子才对就在得上。
登录完了,后台也是通过response的header返回token,前端在每次向后台访问的时候要携带此token在header上。
但是,这种对于客户端的应用是可以的,对于服务端渲染的应用就不行了,因为服务端渲染拿不到客户端存储的token呀。只好在登录完以后,在客户端直接创建cookie,这个coookie是可以共用于xxx.sunofbeach.net的程序的。服务端渲染也可以获取到此cookie,这样子就解决了浏览器访问的问题。
劫起
以上是理论,实践开始。
替换了域名,的确可以正常访问了,但是登录有问题。
解决登录问题服务端渲染又有问题。加上白天要上班,所以拖了那么几天,哈哈!
渡劫
首先是验证码,往下写captchaKey写不了,所以一直是验证码错误。因为后台找不到对应的key去获取验证码的值。
怎么办呢?
不写到cookie里了,直接写到response的头部。
所以在浏览器上的img标签就不直接显示图片了,否则获取不到response里的头部内容。当然,移动端的写法也不一样的,核心内容就是拿到返回的response的header内容。
loadCaptcha() {
fetch(this.captchaSrc).then(function (response) {
//获取到key
let captchaKey = response.headers.get("l_c_i");
cache.setLocal("captchaKey", captchaKey);
response.blob().then(function (myBlob) {
const urlCreator = window.URL || window.webkitURL;
document.getElementById("captcha").src = urlCreator.createObjectURL(myBlob);
});
});
}
当我提交数据的时候,我再携带captchaKey在request的header上。
// 添加请求拦截器
axios.interceptors.request.use(function (request) {
// 在发送请求之前做些什么
let captchaKey = cache.getLocal("captchaKey");
request.headers.l_c_i = captchaKey;
console.log("captchaKey ==> " + captchaKey);
return request;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
这样子,就完成了。
当然,后台也要允许这个header字段,否则前端获取不到的。
response.setHeader(key, value);
response.addHeader("Access-Control-Expose-Headers", key);
response.addHeader("Access-Control-Allow-Headers", key);
跨域配置也修改了
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(Boolean.TRUE);
config.addAllowedMethod("*");
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addExposedHeader("sob_token");
config.addExposedHeader("l_c_i");
config.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
我还遇到什么问题呢?
我配置完以上内容,还是拿不到header,原因是因为我的字段是l_c_i
,sob_token
这样子的。有下划线的。
我还走了一层nginx的代理。如果不配置以下内容,后台是无法获取到的
underscores_in_headers on;
当然,也可以改成中划线。
有了以上的内容,同学们应该知道客户端怎么修改了。
sob_token也是,登录时会在response的header上返回,保存下来,要监听这个变化,如果sob_token不为空,就替换旧的。在访问的时候,携带在request的header上。
这样子就可以完成了携带和过期的更新了。