但丁云网关跨域前端解决方案

391 阅读3分钟

跨域有多种解决方案,如jsonp 、子域跨域,设置同主域domain 、http头cors设置,反向代理等。

跨域解决方案

由于这一次,我们要做网关跨域,会碰到各种各样的情况,为了深度解决跨域的问题,我们采用cors头设置,他不会受到主域相同的限制策略,可以算是我们网关跨域的终极解决方案。

http cors头设置分为两种情况

  • 不带cookie跨域
  • 带cookie跨域

不带cookie跨域,只需要服务端设置Access-Control-Allow-Origin:*即可

带cookie跨域,需要前端与服务端同时设置

前端设置,分为jquery fetch axios三种方式

xhrFields: {withCredentials: true}  // jQuery 1.5.1 
credentials: 'include'      // ES6 fetch() 
withCredentials: true       // axios:

服务端设置

res.writeHead(200, {
    "content-type": "text/javascript",
    'Access-Control-Allow-Credentials': 'true',
    "Access-Control-Allow-Methods": "POST, GET",
    'Access-Control-Allow-Origin': 'http://npc.yang800.com', //此处必须是明确的域名,不能是*
    'Set-Cookie': [
        `login=true;` , 
        `user=333;`
    ],
});

但丁云网关设置

目前,我们的后台登陆与权限设置,都分为供应商端,和小二端。所有我们的登陆,和接口登陆判断,都需要以路径的形式分开。如/supplier/api1/supplier/api2/admin/api1/admin/api1

因此,我们接口登陆后,cookie的形式,以path的形式去设置

'Set-Cookie': [
    `login=true;Path=/supplier;` , 
    `user=333;Path=/supplier;`
]

'Set-Cookie': [
    `login=true;Path=/admin;`,
    `user=666;Path=/admin;`
],

测试用例

下面附上完整的测试用例。我们通过 host 绑定 127.0.0.1 api.yang800.com这个域名

前端

import React, { useState } from 'react';
import ReactDom from 'react-dom'
import axios from 'axios'

/**
 * 
 *  jQuery 1.5.1 xhrFields: {withCredentials: true}
    ES6 fetch() credentials: 'include'
    axios: withCredentials: true
 */


function App(){
    var [data,setData] = useState('');
    return (
        <div>
            <div style={{height:'200px' , border:'solid 1px #ddd'}}>{data}</div>
            <button onClick={async() => {
                let res = await axios.request({
                    url : 'http://api.yang800.com:3000/login1' ,
                    crossDomain: true, 
                    withCredentials: true
                })
                setData(JSON.stringify(res.data))
            }}>scm登陆</button>
            <button onClick={async() => {
                let res = await axios.request({
                    url: 'http://api.yang800.com:3000/api1/get-user',
                    crossDomain: true,
                    withCredentials: true
                })
                setData(JSON.stringify(res.data))
            }}>获取scm数据</button>
            <button onClick={async () => {
                let res = await axios.request({
                    url: 'http://api.yang800.com:3000/loginout1',
                    crossDomain: true,
                    withCredentials: true
                })
                setData(JSON.stringify(res.data))
            }}>退出scm登陆</button>


            <button onClick={async () => {
                let res = await axios.request({
                    url: 'http://api.yang800.com:3000/login2',
                    crossDomain: true,
                    withCredentials: true
                })
                setData(JSON.stringify(res.data))
            }}>npc登陆</button>
            <button onClick={async () => {
                let res = await axios.request({
                    url: 'http://api.yang800.com:3000/api2/get-user',
                    crossDomain: true,
                    withCredentials: true
                })
                setData(JSON.stringify(res.data))
            }}>获取npc数据</button>
            <button onClick={async () => {
                let res = await axios.request({
                    url: 'http://api.yang800.com:3000/logout2',
                    crossDomain: true,
                    withCredentials: true
                })
                setData(JSON.stringify(res.data))
            }}>退出npc登陆</button>

        </div>
    )
}
ReactDom.render(<App /> , document.getElementById('root'));

服务端 nodejs

var http = require("http");

http.createServer(function (req, res) {
    var origin = req.headers.origin;
    var url = req.url;
    var method = req.method;

    console.log(origin , url , method)
    if(url == '/login1'){
        res.writeHead(200, {
            "content-type": "text/javascript",
            'Access-Control-Allow-Credentials': 'true',
            "Access-Control-Allow-Methods": "POST, GET",
            'Access-Control-Allow-Origin': origin || 'http://api.yang800.com',
            'Set-Cookie': [
                `login=true;Path=/api1;` , 
                `user=333;Path=/api1;`
            ],
        });
        var data = {
            code : 0 ,
            data : 'login success'
        }
        res.write(JSON.stringify(data));
        res.end();
        return ;
    }
    if (url == '/loginout1') {
        res.writeHead(200, {
            "content-type": "text/javascript",
            'Access-Control-Allow-Credentials': 'true',
            "Access-Control-Allow-Methods": "POST, GET",
            'Access-Control-Allow-Origin': origin || 'http://api.yang800.com',
            'Set-Cookie': [
                `login=true;Path=/api1;Max-Age=-1`,
                `user=333;Path=/api1;Max-Age=-1`
            ],
        });
        var data = {
            code: 0,
            data: 'logout success'
        }
        res.write(JSON.stringify(data));
        res.end();
        return;
    }
    if (url == '/login2') {
        res.writeHead(200, {
            "content-type": "text/javascript",
            'Access-Control-Allow-Credentials': 'true',
            "Access-Control-Allow-Methods": "POST, GET",
            'Access-Control-Allow-Origin': origin || 'http://api.yang800.com',
            'Set-Cookie': [
                `login=true;Path=/api2;`,
                `user=666;Path=/api2;`
            ],

        });
        var data = {
            code: 0,
            data: 'login success'
        }
        res.write(JSON.stringify(data));
        res.end();
        return;
    }
    if (url == '/logout2') {
        res.writeHead(200, {
            "content-type": "text/javascript",
            'Access-Control-Allow-Credentials': 'true',
            "Access-Control-Allow-Methods": "POST, GET",
            'Access-Control-Allow-Origin': origin || 'http://api.yang800.com',
            'Set-Cookie': [
                `login=true;Path=/api2;Max-Age=-1`,
                `user=666;Path=/api2;Max-Age=-1`
            ],
        });
        var data = {
            code: 0,
            data: 'logout success'
        }
        res.write(JSON.stringify(data));
        res.end();
        return;
    }

    if (url.indexOf('/api1') == 0 || url.indexOf('/api2') == 0){
        var cookie = req.headers.cookie;
        if (cookie && cookie.indexOf('login=true') > -1){
            var data = {
                code : 0,
                cookie: cookie ,
                message : '登陆成功'
            };
        }else{
            var data = {
                code: -1001,
                cookie: cookie,
                message: '未登陆'
            };
        }
        res.writeHead(200, {
            "content-type": "text/javascript",
            'Access-Control-Allow-Credentials': 'true',
            "Access-Control-Allow-Methods": "POST, GET",
            'Access-Control-Allow-Origin': origin || 'http://api.yang800.com',
        });
        res.write(JSON.stringify(data));
        res.end();
        return;
    }
}).listen(3000);

后记

我们的跨域方案,都是基于第三方cookie,但目前safari高版本,默认设置,已经禁用第三方cookie ,所以,在高版本的safari上面,我们的跨域方案,会失败。

在这里,我们只有手动打开阴止跨站跟踪,才能安全的使用第三方跨域