聊一聊 Oauth 吧

前言

最近要给一个小白讲解一下 Oauth, 小白看了辣么多教程,结果都大同小异,关键点都没有讲清,越绕越糊涂。所以还是自己来写写吧。

先来一段标准的 wiki 定义

OAuth is an open standard for authorization, commonly used as a way for Internet users to authorize websites or applications to access their information on other websites but without giving them the passwords

也就是说 Oauth 是一个开放式的协议。通过这个协议,用户可以让第三方 B (web or app) 可以以用户的名义去获取用户在网站 A 上面的一些资源,但是却不需要把用户在 A 网站上的username password 给 B 。

OAuth

为什么需要 OAuth

比如有个很火的一个第三方服务 PRINT,只要用微博账户登录它的网站,它就可以把你过去所有的状态都打印成相册再寄给你。

那问题产生了, 这个 PRINT 怎么才能访问我在微博里面的状态呢? 如果直接把账户密码给了PRINT, 那么就意味着 PRINT 不仅可以访问我的状态,还可以访问我的好友联系人,甚至还可以访问我的私密相册。PRINT 作为一个有情怀的公司当然不会做这些龌龊的事情, 但怕的是 PRINT 安全没有做好,被人hack 了,那么我们小白用户所有相关联的信息都会被黑客拿到。

OK, 有了需求,才会有技术被发明去解决它

我们来看看 Oauth 是如何尝试解决上述问题的吧。

OAuth 的抽象模型

根据上述情况,Oauth 做了下抽象处理,定义了 4 个角色

  1. Resource Owner, 就是 我,用户本人

  2. Client, 就是第三方服务 PRINT

  3. Resource Server, 微博存放关于用户信息的服务器

  4. Authorization Server, 微博 Oauth 认证服务器

下面是整个过程的抽象流程图:

图片描述

  1. User 登录 Application(Client, 这里也就是PRINT公司的网站), Application(Client) 向 User 发出 授权请求

  2. User 同意了这个请求。

  3. Application(Client) 拿着用户同意请求的凭证 去找 Authorization Server 说: 看, 我是这个 User 亲派的, 并出示凭证

  4. Authorization Server 看了看,哎哟 还真是 某某 User 的亲笔签名,墨还没干呢(still valid)。得了,给你调用禁军的令牌,啊, 不是,调用该 User状态的令牌(Access Token)

  5. Application(Client) 拿着 Access Token 去请求 Resource Server 上的资源,Resource Server只认令牌不认人。 一看令牌是有效的,就把令牌上描述的资源给了 Application(Client)

但是要注意的是,这只是抽象的流程,其中有的步骤根据具体情况的不同有着不同的实现,具体参见下文。

Oauth 具体实现

现在我们从 Application(Client, 也就是 PRINT 公司) 的角度来讲怎么去具体实现上面的 abstract flow。

不知道为啥我打不开 微博 Oauth 页面。 我就拿 github 来类比吧。比如我们想打印 github 用户的 repo 信息 ,并以此盈利上市 :)

首先我们需要去 register new app 注册我们的 PRINT 应用, 让 github 知道我这个 app 的存在

图片描述

  • Application name: 我们的 app 名字

  • Homepage URL: 因为我们是本地develop, 就协商 http://localhost:8080 就行了,如果产品上线了,就要换成你的域名

  • Application description: blabla...

  • Authorization callback URL: 这个是在用户同意授权后,authorization server经过验证后要把用户的页面带回到我们的App里。

点击 Register application 后面就会得到一个 client Id 和一个 client secret

clipboard.png

  • client Id 这个是 public 的,谁都可以看到。因为当用户要用 github 登陆的时候会把 client Id 发送给 github 的 authorization server 来确定是哪个 app 要用户的github 信息

  • client secret 这个是 secret 的。 这个一定要保管好。这个是 authorization server 来确认要来访问资源的应用是不是真的是用户同意授权的应用,在这个例子中就是 PRINT, 要不然谁都可以拿 client id 去访问 resources server 了。 所以这个信息只有我们 PRINT 和 authorization server 才能知道。

好了,这里我们先暂停一下,回到之前的流程图

  1. 用户来到我们的 PRINT 网站,点击了用 github 登录

  2. 我们把用户引导到 authorization server 的页面,用户点击了同意,authorization server 再次根据 redirect url 把用户引导到我们的网站。那我们怎么知道用户是否同意了呢?

重点就在这里,authorziation server 会在 redirect url 后面附上一个参数类似

http://localhost:8080/githubOauth?code=2280fc9eb2c585bf4302

我们的服务器需要这个 codeclient secret 来和 authroziation server 来个服务器之间的对话, 左手拿着 code 说 你看,这是用户的许可。右手拿着 client_secret 说 这能证明我是真正的 PRINT 应用。然后获得一个 access_token, 我们的服务器就可以拿着这个 access_tokenresources server 拿我们被许可拿的东西了。

下面我们用 nodejs 做一个简单的例子

// sever.js
const express = require('express')
const app = express()
const request = require('request')

// This credentials should be stored in environment variables
// we put it here just for demonstration
const CLIENT_ID = '676ae25300eb7ac82ba4'
const CLIENT_SECRET = 'Your secret here'

app.get('/githubOauth', (req, res) => {
    const code = req.query.code // this is the code our server need
    
    // after we get our code, we can send it along with client secret
    // in order to get ACCESS_TOKEN
    const formData = {
        client_id: CLIENT_ID,
        client_secret: CLIENT_SECRET,
        code: code,
        accept: 'json'
    }
    request.post({ url: 'https://github.com/login/oauth/access_token', formData: formData}, (err, res) => {
        const ACCESS_TOKEN = res.body.access_token
        // once we have access token, we could use this to fetch user's info
        // and redirect user to our other pages
})

    
})
app.get('/', (req, res) => {
    res.sendFile('index.html' , { root : __dirname});
})
app.listen('8080', () => console.log('listening on port 8080'))
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <!-- replace client_id of your application here-->
    <a href="https://github.com/login/oauth/authorize?scope=user:email public_repo&client_id=676ae25300eb7ac82ba4">Log in using Github</a> 
</html>

这里有一个问题,为什么 authorization server 不能直接发送 access_token 给我们呢?我们直接拿了 access_token 跟resources server 拿东西不可以吗,为什么还需要一个 grant code 来多此一举。

当然是有情况可以直接发送 access_token的。实际上,我们上面所描述的步骤是 Grant Type 中最常见的一种用 APP 的 server 去拿资源, authorization server 在 redirect_url 后面添加 code 的情况,这个叫做 Authorization code 。 而直接添加 access_token 的 grant type 叫做 implicit

Grant Type 一共有四种情况

  1. Authorization Code, 最常见用于 server side application

  2. Implicit, 最常见于 web app, mobile app. 这种是 client side 不经过 server 直接向 resources server 拿资源

  3. Resource Owner Pssword Credentials

  4. Client Credentils.

后两种情况我们一般用不到。只说前两种的区别。之所以需要多发送一步 authorization/grant code 是因为安全顾虑。

authorization server 默认不相信第三方服务器(比如我们的PRINT server) 有安装 ssl, 或者安装了ssl 但是配置不正确。而它自己是绝对正确安全的的。这样把access_token 发送到 client web/mobile 这一层是 https 链接,黑客无法窃取。 而 client web/mobile 连接到它自己的服务器这部分却不一定是https,authorization server 没办法保证是足够安全的。万一出了事故,黑客拿到了你的私密照片,背锅的肯定是 authorization server 啊。

但是 server 链接到 authorization server 这一层又是受到 https 保护的 (连接中,只有有一方支持https, 那么这个链接就是https)。所以这样就可以保护 access_token 不会被恶意方获取。

implicit 模式中,没有第三方 server 的参与,只有 client side 和 authorization/resources server 之间的 https 通信,所以直接发送 access_token 这种模式是可取的。

但是大部分情况下,我们都有server ,所以去Ouath implementation 都是 Authorization Code 模式。

有空再接着完善吧...

Ref

  1. 10分鐘理解OAuth和facebook登入原理

  2. Basics of Authentication

  3. protocol

  4. An Introduction to Oauth 2.0

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值