關於withCredentials和CORS[項目筆記]

參考連接

koa2-cors設置容許指定單個域名、多個域名、全部域名跨域javascript

當一個資源從與該資源自己所在的服務器不一樣的域、協議或端口請求一個資源時,資源會發起一個跨域.html

CORS 跨域資源共享

cross origin resource sharing (CORS,跨域資源共享) 是一種機制,它使用額外的HTTP 頭來告訴瀏覽器 讓運行在一個 origin (domain)上的Web應用被准許訪問來自不一樣源服務器上的指定的資源。前端

cors配置[koa2]

容許全部域名跨域訪問

const cors = require('koa2-cors');
app.use(cors()); 
複製代碼

指定單個域名跨域

app.use(
     cors({
         origin: function(ctx) { //設置容許來自指定域名請求
            return 'http://localhost:8080'; //只容許http://localhost:8080這個域名的請求
        },
         maxAge: 5, //指定本次預檢請求的有效期,單位爲秒。
        credentials: true, //是否容許發送Cookie
         allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], //設置所容許的HTTP請求方法
        allowHeaders: ['Content-Type', 'Authorization', 'Accept'], //設置服務器支持的全部頭信息字段
        exposeHeaders: ['WWW-Authenticate', 'Server-Authorization'] //設置獲取其餘自定義字段
    })
);
複製代碼

設置多個域名可跨域

app.use(
     cors({
         origin: function(ctx) { //設置容許來自指定域名請求
            const whiteList = ['http://weipxiu.com','http://localhost:8081']; //可跨域白名單
            let url = ctx.header.referer.substr(0,ctx.header.referer.length - 1); 
             if(whiteList.includes(url)){
                 return url //注意,這裏域名末尾不能帶/,不然不成功,因此在以前我把/經過substr幹掉了
            }
             return 'http://localhost::3000' //默認容許本地請求3000端口可跨域
        },
         maxAge: 5, //指定本次預檢請求的有效期,單位爲秒。
         credentials: true, //是否容許發送Cookie
         allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], //設置所容許的HTTP請求方法
        allowHeaders: ['Content-Type', 'Authorization', 'Accept'], //設置服務器支持的全部頭信息字段
        exposeHeaders: ['WWW-Authenticate', 'Server-Authorization'] //設置獲取其餘自定義字段
    })
);
複製代碼

withCredentials在項目中遇到的問題

問題描述

在實際項目開發中,遇到這樣一個問題:在用戶登陸成功後爲用戶設置cookie,當用戶再次訪問該網頁時直接從cookie中獲取用戶信息。在項目中,我遇到的問題是,沒法從cookie中獲取到用戶信息,獲取到的值是undefinedjava

登陸成功後設置cookie

router.post('/smslogin', async(ctx,next) => {
    // 1. 獲取數據
    const qqEmail = ctx.request.body.qqEmail;
    const pin = ctx.request.body.pin;
    console.log(sms)
    console.log(qqEmail,pin)
    // // 2. 驗證驗證碼是否正確
    if (pin != sms) {
        ctx.body={err_code: 0, message: '驗證碼不正確!'};
        sms = ''
        return;
    }
    // 3. 查詢數據
    let sqlStr = `SELECT * FROM users WHERE qqEmail = '${qqEmail}'`;
    let res = await query(sqlStr)
    if(res[0]){
      ctx.cookies.set('userId', res[0].userId, {
            domain: 'localhost', // 寫cookie所在的域名
            path: '/', // 寫cookie所在的路徑
            maxAge: 2 * 60 * 60 * 1000, // cookie有效時長
            expires: new Date('2018-02-08'), // cookie失效時間
            httpOnly: false, // 是否只用於http請求中獲取
            overwrite: false // 是否容許重寫
        })

    }else{
      ctx.body={err_code:200,message:'請註冊後登陸!'}
    }
});
複製代碼

獲取cookie中的數據

router.get('/userinfo',async(ctx,next) => {
    // 1.0 獲取參數
    
    let userId = ctx.cookies.get('userId')  // 這裏獲取參數失敗
    
    let res = await query(`SELECT * FROM users WHERE id = '${userId}'`)
    delete res[0].password
    ctx.body={
      success_code:200,
      message:res[0]
    }
});
複製代碼

問題分析

在同域的狀況下,咱們發送請求會默認攜帶當前域下的cookie,可是在跨域的狀況下,默認是不會攜帶請求域下的cookie 的,好比http://domain-a.com 站點發送一個 http://api.domain-b.com/get 的請求,默認是不會攜帶 api.domain-b.com 域下的 cookie.ios

在項目中,狀況是這樣的localhost:8080站點取發送localhost:3000的請求,二者端口不同,構成跨域,跨域默認是不會攜帶 localhost:3000 域下的 cookie.sql

問題解決

要解決這個問題,咱們就要讓這個跨域請求能夠攜帶上cookie,作法很簡單:axios

前端處理

在前端我是使用axios發起網絡請求後端

axios.defaults.withCredentials = true
複製代碼

後端處理

在後端,咱們也要否容許瀏覽器攜帶cookie訪問進行設置,也就是說咱們要將響應頭的credentials設置爲trueapi

credentials: true, //是否容許發送Cookie
複製代碼

此外,咱們還要對cors進行配置。咱們不能容許全部域名進行跨域訪問:跨域

也就是說,咱們對cors不能這樣配置.

const cors = require('koa2-cors');
app.use(cors()); 
複製代碼

比較推薦的作法是

設置多個域名可跨域

app.use(
     cors({
         origin: function(ctx) { //設置容許來自指定域名請求
            const whiteList = ['http://weipxiu.com','http://localhost:8081']; //可跨域白名單
            let url = ctx.header.referer.substr(0,ctx.header.referer.length - 1); 
             if(whiteList.includes(url)){
                 return url //注意,這裏域名末尾不能帶/,不然不成功,因此在以前我把/經過substr幹掉了
            }
             return 'http://localhost::3000' //默認容許本地請求3000端口可跨域
        },
         maxAge: 5, //指定本次預檢請求的有效期,單位爲秒。
         credentials: true, //是否容許發送Cookie
         allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], //設置所容許的HTTP請求方法
        allowHeaders: ['Content-Type', 'Authorization', 'Accept'], //設置服務器支持的全部頭信息字段
        exposeHeaders: ['WWW-Authenticate', 'Server-Authorization'] //設置獲取其餘自定義字段
    })
);
複製代碼

至此,問題已經解決。

雖然問題已經解決了,咱們還應該思考一下,在上面的狀況中,爲何不能將cors設置爲容許所有域名跨域?

如下是我的理解: 從安全方面考慮,將cors設置爲容許所有域名跨域可讓咱們在後端獲取到cookie的值,後端處理是沒有問題的,可是服務器考慮到安全性會攔截Access-Control-Allow-Origin: *的響應。

相關文章
相關標籤/搜索