参考链接
koa2-cors设置允许指定单个域名、多个域名、所有域名跨域
当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域.
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
中获取到用户信息,获取到的值是undefined
登录成功后设置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
.
在项目中,情况是这样的localhost:8080
站点取发送localhost:3000
的请求,两者端口不一样,构成跨域,跨域默认是不会携带 localhost:3000
域下的 cookie
.
问题解决
要解决这个问题,我们就要让这个跨域请求可以携带上cookie
,做法很简单:
前端处理
在前端我是使用axios发起网络请求
axios.defaults.withCredentials = true
后端处理
在后端,我们也要否允许浏览器携带cookie
访问进行设置,也就是说我们要将响应头的credentials
设置为true
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: *
的响应。