为了账号安全,请及时绑定邮箱和手机立即绑定

【金秋打卡】第19天 前后端分离 OAuth 登录、使用 egg-cors 支持跨域

标签:
Node.js

课程名称web前端架构师
课程章节:第16周 第七章 使用 OAuth2 协议完成用户创建以及验证功能
主讲老师:张轩
课程内容: 前后端分离 OAuth 登录、使用 egg-cors 支持跨域

前后端分离 OAuth 登录

之前实现的 OAuth 都是在服务端实现的,客户端肯定无法使用,前后端分离的项目要使用,就需要做一些其他处理

postMessage 可以在两个 window 之间传递消息

登录流程

整个过程是这样的

  • 用户点击 gitee OAuth 登录, A页面
  • 使用 window.open 方法打开授权链接 B页面(服务端页面)
  • 用户点击授权后,拿到用户 token,调用 windows.opener.postMessage 向 A 页面发送消息
  • A 页面接受到消息后,就可以拿到 token 实现登录了

就这样整个前后端分离授权登录流程就完成了

代码实现

首先是用户登录页面

<body>
    <button onclick="giteeOAuth()">gitee 授权</button>
</body>
<script>
    // 用户点击授权登录后,跳转到服务端授权页面 B 页面
    function giteeOAuth(){
        window.open('http://localhost:7001/user/passport/gitee', '__blank')
    }
    // 接受 B 页面发送过来的 token
    window.addEventListener('message', (res) => {
        console.log(res.data)
    })
</script>

服务端页面,首先跳转到 gitee 授权页面进行授权

async giteeOAuth() {
  const { ctx, app } = this;
  const config = app.config.giteeConfig;
  const url = `https://gitee.com/oauth/authorize?client_id=${config.cid}&redirect_uri=${config.redirectURL}&response_type=code`;
  ctx.redirect(url);
}

授权成功后,会调用回调接口,回调接口返回一个页面, 将 token 通过 windows.opener.postMessage 发送给用户登录页面

async signByGitee() {
  const { ctx, service } = this;
  const { code } = ctx.request.query;
  const token = await service.user.signByGitee(code);
  await ctx.render('giteeOAuth.ejs', {
    token,
  });
}

这样 gitee 授权登录功能就完成了

使用 egg-cors 支持跨域

跨域的基本知识

浏览器限制脚本内发送跨域 HTTP 请求

  • XMLHttpRequest 或者 Fetch
  • drawImage 将 images/video 绘制到 canvas

何为跨域 Origin?

  • 协议
  • 主机(域名)
  • 端口

上面三个中有一个不一样就会出现跨域

// 不同的协议
http://example.com/app1
https://example.com/app2
// 不同的主机
http://example.com
http://www.example.com
http://myapp.example.com
// 不同的端口
http://example.com
http://example.com:8080
// 特别注意,下面两个是同源的,因为 http 默认使用的是 80 端口
http://example.com:80
http://example.com

CORS (Cross-Origin Resource Sharing,跨域资源共享)字段

跨源资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。

Access-Control-Allow-Origin: <origin> | *
// 只能拿到一些最基本的响应头,
Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma
Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header
Access-Control-Allow-Methods: <method>[, <method>]*

使用 egg-cors 支持跨域

npm i egg-cors

开启 egg-cors 插件

// config/plugin.js
...
 cors: {
    enable: true,
    package: 'egg-cors',
  },
...

配置 egg-cors 插件

config.cors = {
  // 允许跨域访问的地址
  origin: 'http://localhost:3000',
  // 允许请求的方式
  allowMethods: 'GET,HEAD,PUT,OPTIONS,POST,DELETE,PATCH',
};

就这样 egg程序就可以支持跨域了
我们可以在之前授权登录的接口进行请求测试

window.addEventListener('message', (res) => {
    const token = res.data
    if (token) {
        fetch('http://localhost:7001/user', {
            headers: {
                Authorization: 'Bearer ' + token
            }
        }).then(res => res.json()).then(res => {
            console.log(res)
        })
    }
})

测试可以正常运行

但是,现在我们打开控制台,会看到我们发送两次请求,其中一次请求,它的 type是 Preflight

Preflight

对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨源请求

简单请求不会发送 Preflight 请求

简单满足的条件

使用下列方法之一:

  • GET
  • HEAD
  • POST

Content-Type 的值仅限于下列三者之一

  • text/plain
  • multipart/form-data
  • application/x-www-form-urlencoded

没有自定义的 Header

代码测试

下面代码会发送两次请求 ,有 preflight。因为没有满足 Content-Type 的值

axios.post('http://localhost:7001/user/signin', {
     username: 'xxx@qq.com',
     password: '12345678'
 }).then(res => {
     console.log(res)
 })

修改后,就只会发送一次请求

axios.post('http://localhost:7001/user/signin', {
     username: 'xxx@qq.com',
     password: '12345678'
 }, {
     headers: {
         'Content-Type': 'application/x-www-form-urlencoded'
     }
 }).then(res => {
     console.log(res)
 })

图片描述

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
Python工程师
手记
粉丝
2
获赞与收藏
1

关注作者,订阅最新文章

阅读免费教程

  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消