美文网首页Run_架构Java学习
前后端分离下的第三方登陆处理

前后端分离下的第三方登陆处理

作者: 杰森跟班 | 来源:发表于2019-01-13 11:01 被阅读29次

    第三方登陆的种类分析

    1. 前后端不分离的情况,后端根据第三方回调返回的用户信息可以直接登陆用户,这种比较简单,后端拿到用户可以直接使用session记住并且登陆用户,跳转到相应页面。
    2. 前后端分离,但是不跨域
    3. 前后端分离,并且是跨域的,即前端域名和后端api域名不同

    github 为例子:

    首先去添加一个 Oauth App:

    github Oauth Apps

    配置相关回调地址,这里的回调地址必须必须能通过外网访问。或者你可以用内网穿透。

    配置
    配置完后把你的 IDsecret 加入到你的后端逻辑中去, 秘钥

    之后用户通过前端授权按钮登陆,首次登陆需要授权。

    授权

    授权完成后 github 会调用你的回调地址返回用户信息:

    github回调

    获取到用户信息后逻辑就开始区分了

    1. 前后端不分离的话可以直接在回调方法中登陆用户跳转
    2. 前后端分离的话比较麻烦,请继续看

    分离环境下,后端会先把 github 回调的用户信息存到数据库中,然后再做相应处理。

    因为 github 调用的是你后端 api 的地址,比如调用地址的域名为 api.server.com ,然而你web端的地址是 web.server.com ,这时候如何去登陆用户呢?

    难题:

    因为前后端分离中的后端几乎都是返回 json 格式的信息,几乎不存在直接跳前端页面的做法,并且前后端分离都是用token获取用户信息。前端只有拿到相应的token才能获取到github用户的资料。所以这里的问题变成了如何通过后端把用户的 token 给到前端页面。

    解决方案:

    本质上来讲这个问题属于跨域问题,只有同源的数据才能相互访问。

    1995年,同源政策由 Netscape 公司引入浏览器。目前,所有浏览器都实行这个政策。

    最初,它的含义是指,A 网页设置的 Cookie,B 网页不能打开,除非这两个网页“同源”。所谓“同源”指的是”三个相同“。

    • 协议相同
    • 域名相同
    • 端口相同

    举例来说,http://www.example.com/dir/page.html这个网址,协议是http://,域名是www.example.com,端口是80(默认端口可以省略),它的同源情况如下。

    • http://www.example.com/dir2/other.html:同源
    • http://example.com/dir/other.html:不同源(域名不同)
    • http://v2.www.example.com/dir/other.html:不同源(域名不同)
    • http://www.example.com:81/dir/other.html:不同源(端口不同)
    • https://www.example.com/dir/page.html:不同源(协议不同)

    那么如何解决跨域问题的,这里我采用了,跨文档通信 API(Cross-document messaging)。window.postMessage() 。(HTML5 为了解决跨域窗口通信这个问题,引入的一个全新的API)。

    下面开始实践

    前端页面:

    前端oauth入口
    点击 github logo,跳转到后端的 api.server.com/oauth/login,为了好看,你可以采用弹出小窗的形式,类似于下面这样 小窗效果图

    弹出窗口->github 授权登陆->后端接收到用户的json信息

    后端:

    后端得到github回调用户信息后,去生成一串 token,怎么实现的话,问你自己咯😀,生成token之后,后端必须跳转一个页面(但不是跳转到前端页面,这样不可行,直接把token放在url地址上也不安全),正确的方法是跳一个类似于下面这样的页面(注意这个行为是后端做的)。domain是你前端的域名 web.server.com

    <!doctype html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport"
              content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>oauth github</title>
    </head>
    <body>
    登陆中...
    <script>
        window.onload = function () {
            window.opener.postMessage("bearer {{ $token }}", "{{ $domain }}");
            window.close();
        }
    </script>
    </body>
    </html>
    
    
    后端跳转

    用户几乎是不会感觉到这个行为的,非常快!

    这个页面的意义在于和前端通信,把token传给前端,

    前端点击跳出小窗口的代码:

    href () {
            # 弹出 500 * 500 的窗口
          window.open(this.githubUrl, 'newwindow', 'height=500, width=500, top=0, left=0, toolbar=no, menubar=no, scrollbars=no, resizable=no,location=n o, status=no')
    
          # 通过监听,父页面可以拿到子页面传递的token,父(前端页面),子(小窗)
          window.addEventListener('message', function (e) {
            this.me(e.data).then(() => {
              console.log('用户信息获取成功')
              this.setToken(e.data)
            }).catch(() => {
              this.setToken('')
            })
          }, false)
    },
    

    如果不知道上面代码的实现原理或者有疑问请看这里 https://wangdoc.com/javascript/bom/same-origin.html#windowpostmessage

    我们假设有两个网站,1.com与2.com,我在1.com的页面上通过iframe或window.open或超链接打开了一个2.com的网页,此时我要在2.com上做操作的时候,给1.com传值,让1.com有所变化。这个过程就是个跨域的过程。

    如果你对window.open熟,你就会知道通过window.open打开的网页(我们称之为子网页),可以通过window.opener对象,访问到把它打开的页面(父网页),这样一来,调用父页面的函数就是非常简单的事了。但是,在跨域的条件下,window.opener就成了一个空对象,“没有权限”,浏览器会这么告诉你。
    比如,你的父页面有个函数叫callback,然后你子页面本可以这样调用:window.opener.callback(),同域时能成功,跨域时就没有权限了。
    但是,此时你调用window.opener.postMessage(),却可以成功!

    嗯,拿到token了之后怎么做上面代码也写了,聪明的你应该看懂了😁

    这种方式的安全性

    MDN 上对其这样介绍

    window.postMessage() 方法可以安全地实现跨源通信。通常,对于两个不同页面的脚本,只有当执行它们的页面位于具有相同的协议(通常为https),端口号(443为https的默认值),以及主机 (两个页面的模数 Document.domain设置为相同的值) 时,这两个脚本才能相互通信。window.postMessage() 方法提供了一种受控机制来规避此限制,只要正确的使用,这种方法就很安全。

    演示一下

    演示

    你可以来 我的博客 体验一下

    相关文章

      网友评论

        本文标题:前后端分离下的第三方登陆处理

        本文链接:https://www.haomeiwen.com/subject/pzukdqtx.html