跨域与同源策略

By | 2021年12月31日

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 信息,但是发现没有携带了!

发表评论

您的电子邮箱地址不会被公开。