自从搞清楚了跨域原理后一直自鸣得意,感觉跨域没啥问题了。而事实上对关于跨域的几个header的理解也有限,但那又如何,我能做到跨域就行了。今天想把博客背景图改成bing的每日一图,发现遇到跨域问题。首先想到的就是自己写一个web,请求bing,然后传出结果,把自己的接口允许跨域。确实做到了,但是。我找了一台阿里云服务器,我安装了java,我编写了一个基于dropwizard的webservice。我需要写脚本去部署,确保系统稳定,挂了自动重启。我要写一堆的java代码来完成这件事。忽然想到nginx,于是一发不可收拾。
安装好Nginx参阅
找到配置文件/usr/local/nginx/nginx.conf
新增代理路由 location ^~/proxy/bing/ { add_header 'Access-Control-Allow-Origin' 'http://localhost:8088'; add_header 'Cache-Control' 'public, max-age=604800'; add_header 'Access-Control-Allow-Credentials' 'true'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; rewrite ^/proxy/bing/(.*)$ /$1 break; proxy_pass https://cn.bing.com/; } 浏览器访问自动代理 ?format=js&idx=0&n=1 代理对象为: https://cn.bing.com/HPImageArchive.aspx?format=js&idx=0&n=1这是最简单的实现方案,但缺点是只能指定一个域名跨域。
如果我想增加多个origin怎么办不要想用逗号隔开,这个不行,浏览器不允许。那么只能自己判断比较后插入一个合适的值。
server { listen 80; server_name localhost; #charset koi8-r; #access_log logs/host.access.log main; location / { root html; index index.html index.htm; } location ^~/proxy/bing/ { set $cors "local"; if ( $http_referer ~* "(https?://www\.cnblogs\.com/woshimrf[^\s]*)|(https?://api.rmiao.top[^\s]*)|(https?://blog.rmiao.top[^\s]*)|([^\s]*)" ) { set $cors "allow"; } if ( $request_method = "OPTIONS" ) { set $cors "${cors}options"; } if ( $cors = "allowoptions" ) { add_header 'Access-Control-Allow-Origin' "$http_origin"; add_header 'Access-Control-Allow-Credentials' "true"; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, DELETE'; add_header 'Access-Control-Allow-Headers' 'reqid, nid, host, x-real-ip, x-forwarded-ip, event-type, event-id, accept, content-type'; add_header 'Access-Control-Max-Age' 2592000; add_header 'Content-Length' 0; add_header 'Content-Type' 'text/plain, charset=utf-8'; # indicate successful return with no content return 204; } if ($cors = "allow") { rewrite ^/proxy/bing/(.*)$ /pub_cors/$1 last; } if ($cors = "local") { return 403; } } location ^~/pub_cors/ { internal; # Tells the browser this origin may make cross-origin requests add_header 'Access-Control-Allow-Origin' "$http_origin"; # in a preflight response, tells browser the subsequent actual request can include user credentials (e.g., cookies) add_header 'Access-Control-Allow-Credentials' "true"; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, DELETE'; add_header 'Access-Control-Allow-Headers' 'reqid, nid, host, x-real-ip, x-forwarded-ip, event-type, event-id, accept, content-type'; add_header 'Access-Control-Max-Age' 2592000; add_header 'Cache-Control' "public, max-age=604800"; rewrite ^/pub_cors/(.*)$ /$1 break; proxy_pass https://cn.bing.com/; } #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } }语法要点就不推测了。后面有机会认真学习下。不过,这里还是可以有几个语法参考的。
设置变量 set $cors "local";
正则表达式 location ^~/proxy/bing/ {
获取request的refer $http_referer
获取request的method $request_method
获取request的origin $http_origin
变量的读取,包裹在引号里也可以, add_header 'Access-Control-Allow-Origin' "$http_origin";
变量的读取,可以用大括号包裹, set $cors "${cors}options";
if 里的判断可以用正则, ~* 表示不区分大小写,匹配正则, 取反!~*
~ 区分大小写,匹配正则, 取反 !~
添加一个header, add_header 'Access-Control-Max-Age' 2592000;
设置option的预检请求为204
跳转, rewrite ^/proxy/bing/(.*)$ /pub_cors/$1 last;, 分3部分,第一部分是正则,是匹配当前location的url的正则。 第二部分是映射的值,在第二部分里可以使用$1来获得匹配第一个括号匹配的内容。
if 里的判断可以用等号, if ($cors = "allow") {
internal;是不是只能内部访问?
对于这种代理,尤其是bing这个,完全可以缓存掉。 add_header 'Cache-Control' "public, max-age=604800";
proxy_pass https://cn.bing.com/; 代理host,看样子下一步请求的host就是它,对于rewrite ^/pub_cors/(.*)$ /$1 break;则是把匹配的$1拼接到host之后。即,完成了转发操作。
确实比自己写Java web来做转发的好。
TODO 研究Nginx 配置文件的语法