首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Javascript跨域后台设置拦截

Javascript跨域后台设置拦截

作者头像
Ryan-Miao
发布于 2018-03-14 02:22:40
发布于 2018-03-14 02:22:40
1.2K00
代码可运行
举报
文章被收录于专栏:Ryan MiaoRyan Miao
运行总次数:0
代码可运行

子域名之间互相访问需要跨域

结论放在开头:

  1. 服务端必须设置允许跨域
  2. 客户端带cookie需要设置withCredentials
  3. 无论服务端是否允许跨域,该request都会完整执行
  4. options预请求需要设置返回空,不然requestMapping没有支持该方法则出错

环境搭建

需求

首先需要搭建两个环境。一个是提供API的server A,一个是需要跨域访问API的server B。

Server A提供了一个api。完整的请求request是:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
https://local.corstest.com.net:8443/contentmain/getDepositsRoomAndRatePlanInfo.json?htid=759&_=1490855801818

Server B有个页面page:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
http://cros.corstest.com.net:3001/test.html

并且这个page需要请求server A的api。

但由于跨域保护,请求失败:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'xxxxx' is therefore not allowed access.

修改host

首先本地配置两个指向127.0.0.1的host,方便互相跨域。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
127.0.0.1   local.corstest.com.net 
127.0.0.1   cros.corstest.com.net

启动项目A,方便提供API。 至于项目B,测试跨域只要写个html静态页面即可。那么就写一个test.html,并通过一个工具发布:

browser-sync

安装

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
npm install -g browser-sync

本地启动一个test.html

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
browser-sync start --server --files "*.html" --host "cros.corstest.com.net"  --port 3001

关于跨域CORS

ruanyifeng的文章里说浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。

其中同时满足一下2种标准即为简单跨域:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1) 请求方法是以下三种方法之一:
HEAD
GET
POST
2HTTP的头信息不超出以下几种字段:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain

而其他情况,非简单请求是那种对服务器有特殊要求的请求,比如请求方法是 PUTDELETE,或者Content-Type字段的类型是application/json。非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight),即options请求。

关键

跨域的关键是浏览器获得服务器的认可,而服务器的认可就是header里的Access-Control-Allow-Origin。浏览器通过比较服务端返回的response中是否包含这个字段,以及包含这个字段的内容是否是当前网址来确定是否跨域。也就是说绕过浏览器是可以不用跨域的。

有个问题,看好多文章并没有指出。 第一点,带cookie问题。浏览器设置withCredentialstrue则会带cookie发送给服务端。而服务端设置Access-Control-Allow-Credentialstrue则接收,false则不接受。关键是到filter里的时候才会决定是否设置response,那么这时候cookie已经存在request里了吧。(待验证)

验证:server端确实已经接受了cookie,即使设置为false,服务端仍旧接受cookie。而客户端也仍旧可以发送cookie。

第二点,简单跨域中,浏览器的请求直接发送给服务器,服务器返回是否支持跨域(即是否header加origin), 那么简单跨域究竟是请求了服务端几次?如果是1次,那么如果服务端不支持跨域,即没有设置allow,还会不会继续走下去,会不会继续request得到结果后放入response?就是不论跨域不跨域服务器是否都会执行这个request对应的计算。因为所有的设置header都是给浏览器告知的,和服务端限制无关。(待验证)

验证:即使服务端没有设置允许跨域,当客户端请求过来时,服务端仍旧完整执行了请求并返回,只是客户端没有接收。

服务端需要做点工作

针对上述两种跨域。server A需要写一个filter。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<filter>
    <filter-name>cors</filter-name>
        <filter-class>com.test.filter.CorsFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>cors</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</filter>

Filter:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class CorsFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        URL requestURL = new URL(request.getRequestURL().toString());
        String hostName = requestURL.getHost();
        String origin = request.getHeader("Origin");

        int index = hostName.indexOf(".");

        if(index > -1) {
            String domainHost = hostName.substring(index, hostName.length());
            if(!StringUtils.isEmpty(origin) && origin.contains(domainHost)) {
                response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
                response.addHeader("Access-Control-Allow-Origin", origin);
                response.addHeader("Access-Control-Allow-Credentials", "true");
                response.setHeader("Access-Control-Max-Age", "3600");
                response.addHeader("Access-Control-Allow-Headers", "Content-Type, Cookie, " +
                        "Accept-Encoding, User-Agent, " +
                        "Host, Referer, " +
                        "X-Requested-With, Accept, " +
                        "Accept-Language, Cache-Control, Connection");

                if (request.getHeader("Access-Control-Request-Method") != null && "OPTIONS".equals(request.getMethod())) {
                    // CORS "pre-flight" request
                    response.setStatus(200);
                    return;
                }
            }
        }

        filterChain.doFilter(request, response);
    }
}

上述filter是为了同一个domain下,不同子域名可以跨域访问,而其他domain则不可以,因为我们需要共享cookie,所以设置Access-Control-Allow-Credentialstrue. 如果设置为false则不接受cookie。

客户端,即server B如果想要发送cookie则需要设置withCredentialstrue.

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//原生
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
//jquery
$.ajax({
    ...
    xhrFields: {
        withCredentials: true
    }
    ...
}); 

注意,针对非简单跨域的时候发送options请求,服务端A需要告诉浏览器是否支持跨域即可,不要往下走了,不然到指定的requestMapping发现不支持这个方法就会很尴尬了,所以直接返回。

下面针对简单跨域和非简单跨域做测试:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<!DOCTYPE html>
<html lang="en">

    <meta charset="UTF-8">
    <title>test</title>

    <script src="jquery-1.11.3.js"></script>
</head>
<body>

<input type="button" value="GET_Default" onclick="testGetDefault()">
<input type="button" value="GET_JSON" onclick="testGetJSON()">
<input type="button" value="POST_Default" onclick="testPostDefault()">
<input type="button" value="POST_JSON" onclick="testPostJson()">
<input type="button" value="PUT" onclick="testPUT()">

<script>
    var getUrl = "https://local.corstest.com.net:8443/contentmain/getDepositsRoomAndRatePlanInfo.json?htid=759";
    var postUrl = "https://local.corstest.com.net:8443/contentmain/saveReservationDeposits.json?htid=759";

    function testGetDefault(){
        sendAjax("GET",getUrl, "json", "application/x-www-form-urlencoded");
    }
    function testGetJSON(){
        sendAjax("GET",getUrl, "json", "application/json; charset=utf-8");
    }
    function testPostDefault(){
        sendAjax("POST",postUrl, "json", "application/x-www-form-urlencoded");
    }

    function testPostJson(){
        sendAjax("POST",postUrl, "json", "application/json; charset=utf-8");
    }

    function testPUT(){
        sendAjax("PUT",postUrl, "json", "application/json; charset=utf-8");
    }

    
    function sendAjax(type, url, dataType, contentType){
        $.ajax( { 
            type: type,
            url:  url,
            xhrFields: {
                withCredentials: true
            },
            dataType : dataType, // accept type
            contentType: contentType,  //request type, default is application/x-www-form-urlencoded
            success: function(result){
                console.log(result);
            },
            error: function (xhr) {
                console.log(xhr);
            }
        });
    }


</script>
</body>
</html>
结果:

GET default: 只发送一个正常的get请求。

GET json:

先发送一个options如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
General:
Request URL:https://local.corstest.com.net:8443/contentmain/getDepositsRoomAndRatePlanInfo.json?htid=759
Request Method:OPTIONS
Status Code:200 OK
Remote Address:127.0.0.1:8443

Response Headers:
Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:Content-Type, Cookie, Accept-Encoding, User-Agent, Host, Referer, X-Requested-With, Accept, Accept-Language, Cache-Control, Connection
Access-Control-Allow-Methods:GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Origin:http://cros.corstest.com.net:3001
Content-Length:0
Date:Thu, 30 Mar 2017 12:47:44 GMT
Server:Apache-Coyote/1.1

Request Headers:
Accept:*/*
Accept-Encoding:gzip, deflate, sdch, br
Accept-Language:zh-CN,zh;q=0.8
Access-Control-Request-Headers:content-type
Access-Control-Request-Method:GET
Connection:keep-alive
Host:local.corstest.com.net:8443
Origin:http://cros.corstest.com.net:3001
Referer:http://cros.corstest.com.net:3001/test.html
User-Agent:Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36

然后再发送正常的Get请求。

post default: 正常发送请求。

post json: 先发送一个options请求。然后再发送正常的请求。 其他同理,总之,非简单跨域会多发一次options请求来确认是否支持跨域,这时候服务端一定要返回支持跨域,并且直接返回即可。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
玩玩树莓派之配置Go环境
前言:昨晚以为下载了Gogs可以直接运行,结果出乎意料cannot execute binary file,系统cpu架构不能执行这个二进制文件,那就只好下源码编译,这又需要依赖Go,并且version>=1.4.0,那就先搭配Go的环境。 ---- Step-One:下载Go源码 对于64位Linux wget https://storage.googleapis.com/golang/go1.4.1.linux-amd64.tar.gz 对于32位Linux wget https://storage.
AlicFeng
2018/06/08
1.2K0
go的环境搭建
1.去https://storage.googleapis.com/golang/go1.9.linux-amd64.tar.gz下载已经打包好的安装文件。我这里用的最新版go1.9
公众号-利志分享
2022/04/25
4130
golang-101-hacks(1)——创建开发环境
注:本文是对golang-101-hacks中文翻译,本文的原文地址 创建Go开发环境是非常容易的,以Linux系统为例,你只需要从https://golang.org/dl/ 下载和你系统匹配的二进制包,然后解压包文件就OK了。(注意作者原文的下载的包文件版本有点旧 ,建议下载最新版本,目前最新版本是1.12了)
羊羽shine
2019/05/29
6890
ngrok+nginx实现内网穿透
写在前面: 前天在qq群里看到有人在讨论替代花生壳的工具,说到了ngrok,说是可以实现花生壳一样的内网穿透,个人认为主要有以下几个用处: 可以在公司测试服务器上搭建一个服务,实现测试站点的本地访问
lestat
2018/04/17
5K0
ngrok+nginx实现内网穿透
第7节 Go语言环境搭建
当然你也可以登录Golang的国内网站:https://golang.google.cn/
小尘哥
2019/05/28
1.2K0
【Go API 开发实战 4】Go API 开发环境配置
Go 有多种安装方式,比如 Go 源码安装、Go 标准包安装、第三方工具(yum、apt-get 等)安装。本教程 API 运行在 Linux 服务器上,选择通过标准包来安装 Go 编译环境。Go 提供了每个平台打好包的一键安装,这些包默认会安装到如下目录:/usr/local/go。当然你可以改变它们的安装位置,但是改变之后你必须在你的环境变量中设置如下两个环境变量:
腾讯技术工程官方号
2019/05/16
1.1K0
Centos 安装Golang 1.9以上版本
zhang_gq · 2018-03-27 18:33:05 · 5222 次点击 · 预计阅读时间 1 分钟 · 18分钟之前 开始浏览    
拓荒者
2019/08/02
1.5K0
GO语言介绍以及开发环境配置
1、下载二进制包:go1.13.3.linux-amd64.tar.gz 2、将下载的二进制包解压至 /usr/local目录。
小小咸鱼YwY
2020/06/19
5560
Go语言的安装与配置
先给出官网地址https://golang.org/, 传送 ---- ****Step-One:获取go的二进制包**** wget https://storage.googleapis.com/golang/go1.6.2.linux-386.tar.gz ---- ****Step-Two:解压包**** tar -C /usr/local -xzf go1.6.2.linux-386.tar.gz ---- ****Step-Three:配置环境变量**** #GO export PATH=$PA
AlicFeng
2018/06/08
1.2K0
Go运行环境搭建(Mac\Linux)
另外,十分推荐这本书: http://gopl-zh.b0.upaiyun.com/
王亚昌
2018/08/03
6440
Go语言环境搭建详解
最近写了很多Go语言的原创文章,其中Go语言实战系列30篇,近15W字,还有最近更新的Go经典库系列,不过通过大家的咨询来看,还是想要一些入门的知识,这一篇文章写于2017年初,这里再更新一下,发给大家。
飞雪无情
2018/08/28
9740
Hyperledger fabric部署
这里是根据官方文档https://docs.docker.com/install/linux/docker-ce/ubuntu/推荐的通过仓库下载的方法,注意用的源是Ubuntu16.04自带的source.list,如果更改了源很可能会安装失败。 1.首先更新apt包索引:
zhayujie
2020/04/29
2.5K0
如何在Ubuntu 16.04上安装Go 1.6
Go是Google开发的一种现代编程语言。它在许多应用程序和许多公司中越来越受欢迎,并提供了一组强大的库。本教程将指导您下载和安装Go 1.6,以及构建一个简单的Hello World应用程序。
爆栈工程师
2018/10/09
1.4K0
golang tar包和脚本安装
也可以参考官方安装文档 https://golang.org/doc/install#install
潇洒
2023/10/23
5860
golang tar包和脚本安装
1-Go快速学习入门
[TOC] 0x00 简述 0x01 安装部署 安装包下载地址: 官方:https://golang.org/dl/ 国内:https://studygolang.com/dl Go安装方式: Source: 源代码编译安装-https://dl.google.com/go/go1.14.2.src.tar.gz Binary: 二进制解压安装-https://dl.google.com/go/go1.14.2.linux-amd64.tar.gz Linux 在LINUX上安装GO语言基于二进制安装流程:
全栈工程师修炼指南
2020/10/23
3920
1-Go快速学习入门
linux golang环境安装_python环境搭建
如果出现提示:go: go.mod file not found in current directory or any parent directory.
全栈程序员站长
2022/09/27
1.7K0
【2023最新版】Linux (WSL:Ubuntu22.04)安装Go1.20.6+Win11:安装Go1.20.6+GoLand2023.1.4+配置环境
All releases - The Go Programming Language (google.cn)
Qomolangma
2024/07/29
4030
【2023最新版】Linux (WSL:Ubuntu22.04)安装Go1.20.6+Win11:安装Go1.20.6+GoLand2023.1.4+配置环境
Go语言环境搭建详解(2020版)
最近写了很多Go语言的原创文章,其中Go语言实战系列30篇,近15W字,还有最近更新的Go经典库系列,不过通过大家的咨询来看,还是想要一些入门的知识,这一篇文章写于2017年初,这3年多Go更新了很多版本,所以需要更新下这篇文章。
飞雪无情
2020/11/05
6.5K0
如何在linux搭建go和docker基本开发环境
网络配置的配置文件在/etc/sysconfig/network-scripts/下,文件名前缀为ifcfg-后面跟的就是网卡的名称,可以通过双TAB键查看然后编辑,也可以使用ifconfig查看,也可以使用命令:ls /etc/sysconfig/network-scripts/ifcfg-*列出所有的设备
陌无崖
2019/08/29
1.5K0
Go 专栏|开发环境搭建以及开发工具 VS Code 配置
我的个人电脑是 Mac,然后工作主要使用 Linux,所以在这里主要介绍在这两个系统下的安装。
AlwaysBeta
2021/09/01
5180
Go 专栏|开发环境搭建以及开发工具 VS Code 配置
相关推荐
玩玩树莓派之配置Go环境
更多 >
交个朋友
加入[数据] 腾讯云技术交流站
获取数据实战干货 共享技术经验心得
加入数据技术工作实战群
获取实战干货 交流技术经验
加入[数据库] 腾讯云官方技术交流站
数据库问题秒解答 分享实践经验
换一批
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验