1 何为同源?
所谓同源,就是协议、域名、端口都相同。
浏览器一般都使用了同源策略,因此使用浏览器进行跨域资源访问会受到限制。
同源策略限制的不同源之间的交互主要针对的是js中的XMLHttpRequest等请求,写代码的时候也常常会引用其他域名的js文件,样式文件,图片文件什么的,这些则不会收到限制。
2 后台跨域支持
这里我直接贴代码了:
package com.falsec.pom.filter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.stereotype.Component; @Component public class CrossOriginFilter implements Filter { private FilterConfig config = null; public void init(FilterConfig config) throws ServletException { this.config = config; } public void destroy() { this.config = null; } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse res = (HttpServletResponse) response; String origin = req.getHeader("Origin"); // 可以加入一个list列表,代表请求白名单 // List<String> allowedOrigins = Arrays.asList("http://localhost:8080", // "http://localhost:4200"); // origin = allowedOrigins.contains(origin) ? origin : ""; // 如果使用"*",表明它允许"http://xxx"发起跨域请求。但直接使用 * 来匹配所有域名只能适用于无需使用cookie的场景 // res.setHeader("Access-Control-Allow-Origin","*" ); res.setHeader("Access-Control-Allow-Origin", origin); // 有时候只发送OPTIONS,可能就就是这里没有匹配上 res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS"); // 这里也不能用 "*" 号,否则 POST、PUT等请求cookie也无法发送,只有GET请求可以。 res.setHeader("Access-Control-Allow-Credentials", "true"); // 是否允许请求带有验证信息 res.setHeader("Access-Control-Max-Age", "3628800"); // 表明在xxx秒内,不需要再发送预检验请求,可以缓存该结果 res.setHeader("Access-Control-Allow-Headers", "Authorization,DNT,User-Agent,Keep-Alive,Content-Type,accept,origin,X-Requested-With,Cache-Control"); chain.doFilter(request, res); } }
如果要支持文件上传的跨域,必须在 Access-Control-Allow-Headers 中写上3个配置:Authorization、X-Requested-With、Cache-Control,之前就是少了 Cache-Control 所以一直上传失败。
后台配置了浏览器的跨域支持后,浏览器无需配置跨域支持了。但是当引入Cas单点登录后,涉及302重定向,上满的代码似乎无法解决这个问题。因此实际开发中,我在chrome中做了跨域设置解决了302问题,但firefox始终无法解决,具体请看下文介绍。
3 Chrome 跨域支持
桌面添加chrome快捷键,并在快捷键的 “目标(T):” 中配置下面一段就可以实现跨域:
"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --disable-web-security --user-data-dir=C:\MyChromeDevUserData
经测试,302重定向功能没问题。
主要是因为浏览器跨域设置后,发送的请求中没有携带 Origin 信息了。如果使用非跨域前的chrome访问,就会携带 Origin 头。
4 Firefox 跨域支持
firefox的跨域设置遇到了点问题,下面说一下我的环境。开发时,前端使用ember,并使用 ember serve 命令跑前端静态页面代码,所以页面数据都通过ajax去拿。
我是通过 localhost:4200 访问ember web服务上的静态页面的,按网上说的配置好跨域支持后,还是不行,一直报错:
已拦截跨源请求:同源策略禁止读取位于 http://192.168.1.220:81/falpolicy/users/userDetail?_=1545128505135 的远程资源。(原因:不允许 CORS 请求外部重定向)。[详细了解]。点击详细了解:
原因: CORS不允许请求外部重定向。
错误详情:CORS 请求被服务器响应了一个HTTP重定向到与原始请求的Origin不同的URL上,这在CORS请求中是不允许的。
例如,如果请求 https://service.tld/fetchdata ,但服务器的HTTP响应是 “301 永久移动”, “307 暂时重定向”,或 “308 永久重定向” 并且 Location 是https://anotherservice.net/getdata,那么CORS请求将会失败。
要解决此问题,请更新代码以使用重定向报告的新URL,从而避免重定向。
请求信息如下:
OPTIONS http://192.168.1.220:81/falpolicy/users/userDetail HTTP/1.1 Host: 192.168.1.220:81 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Access-Control-Request-Method: GET Access-Control-Request-Headers: content-type Referer: http://localhost:4200/ Origin: http://localhost:4200 Connection: keep-alive
相应信息如下:
HTTP/1.1 302 Server: nginx/1.14.1 Date: Tue, 18 Dec 2018 10:21:47 GMT Content-Length: 0 Connection: keep-alive Location: http://192.168.1.220:81/cas/login
虽然支持CORS跨域了,但是CORS不允许请求外部重定向。这里CORS 请求被服务器响应了一个HTTP重定向到与原始请求的Origin不同的URL上,这句话已经说的很请清楚了,从上面的请求信息可以看到 Origin: http://localhost:4200的,但我们需要重定向到 Location: http://192.168.1.220:81/cas/login,这是两个不同的域。但是Chrome浏览器开启跨域支持后,请求中就不会有这个Origin,Firefox为什么会有呢?先不去管它了,目前我的解决办法是将 localhost:4200 也配置到 nginx 中去,这样后台接口、cas认证、前端静态页面web服务 都在一个域,配置如下:
server { listen 81; server_name 192.168.1.200; port_in_redirect off; location /falpolicy { proxy_pass http://192.168.1.200:8090/falpolicy; client_max_body_size 1000m; proxy_set_header Cookie $http_cookie; } location /cas { proxy_pass http://192.168.1.200:8080/cas; proxy_set_header Host $host:$server_port; ##重点在$server_port } location / { proxy_pass http://192.168.1.200:4200; } }
注:配置好后,本以为请求中还会携带 Origin 信息,但是发现没有携带了!