• 关于JWT 和Token(转)


    转自关于JWT 和Token

    关于 Token

    token 即使是在计算机领域中也有不同的定义,这里我们说的token,是指访问资源的凭据。例如当你调用Google API,需要带上有效 token 来表明你请求的合法性。这个 token 是 Google 给你的,这代表 Google 给你的授权使得你有能力访问 API 背后的资源。
    请求 API 时携带 token 的方式也有很多种,通过 HTTP Header 或者 url 参数 或者 google 提供的类库都可以:

    // HTTP Header:
    GET /drive/v2/files HTTP/1.1
    Authorization: Bearer <token>
    Host: www.googleapis.com/
    
    // URL query string parameter
    GET https://www.googleapis.com/drive/v2/files?token=<token>
    
    // Python:
    from googleapiclient.discovery import build
    drive = build('drive', 'v2', credentials=credentials)
    

    更具体的说,上面用于调用 API 的 token 我们称为细分为 access token。通常 access token 是有有效期限的,如果过期就需要重新获取。那么如何重新获取?现在我们要让时光倒流一会,回顾第一次获取 token 的流程是怎样的:

    首先你需要向 Google API 注册你的应用程序,注册完毕之后你会拿到认证信息(credentials)包括
    ID 和 secret。不是所有的程序类型都有 secret。
    接下来就要向 Google 请求 access token。这里我们先忽略一些细节,例如请求参数(当然需要上面申请到的 secret)以及不同类型的程序的请求方式等。重要的是,如果你想访问的是用户资源,这里就会提醒用户进行授权。
    如果用户授权完毕。Google 就会返回 access token。又或者是返回授权代码(authorization code),你再通过代码取得 access token
    token 获取到之后,就能够带上 token 访问 API 了

    OAuth

    SSO (Single sign-on)

    通常公司内部会有非常多的工具平台供大家使用,比如人力资源,代码管理,日志监控,预算申请等等。如果每一个平台都实现自己的用户体系的话无疑是巨大的浪费,所以公司内部会有一套公用的用户体系,用户只要登陆之后,就能够访问所有的系统。这就是单点登录(SSO: Single Sign-On)
    SSO 是一类解决方案的统称,而在具体的实施方面,我们有两种策略可供选择:1) SAML 2.0 ; 2) OAuth 2.0。接下来我们区别这两种授权方式有什么不同。
    但是在描述不同的策略之前,我们先叙述几个共有的,并且相当重要的概念。
    Authentication VS Authorisation

    Authentication: 身份鉴别,以下简称认证
    Authorisation: 授权

    认证的作用在于认可你有权限访问系统,用于鉴别访问者是否是合法用户;而授权用于决定你有访问哪些资源的权限。大多数人不会区分这两者的区别,因为站在用户的立场上。而作为系统的设计者来说,这两者是有差别的,这是不同的两个工作职责,我们可以只需要认证功能,而不需要授权功能,甚至不需要自己实现认证功能,而借助 Google 的认证系统,即用户可以用 Google 的账号进行登陆。

    OAuth 2.0

    我们先简单了解 SSO 下的 OAuth 2.0 的流程。
    用户通过客户端(可以是浏览器也可以是手机应用)想要访问 SP 上的资源,但是 SP 告诉用户需要进行认证,将用户重定向至 IdP
    IdP 向用户询问 SP 是否可以访问用户信息,如果用户同意,IdP 向客户端返回 access code
    客户端拿 code 向 IdP 换 access token,并拿着 access token 向 SP 请求资源
    SP 接受到请求之后拿着附带 token 向 IdP 验证用户的身份

    那么 OAuth 是如何避免 SAML 流程下无法解析 POST 内容的信息的呢?用户从 IdP 返回客户端的方式是通过 URL 重定向,这里的 URL 允许自定义schema,所以即使在手机上也能拉起应用;另一方面因为 IdP 向客户端传递的是 code,而不是 XML 信息,所以 code 可以很轻易的附着在重定向 URL 上进行传递。

    JWT

    首先我们需要从感性上认识 JWT。本质上来说 JWT 也是 token,正如我们第一小节学习到的,它是访问资源的凭证
    Google 的一些 API 诸如 Prediction API 或者 Google Cloud Storage,是不需要访问用户的个人数据的,因而不需要经过用户的授权这一步骤,应用程序可以直接访问。就像上一节 OAuth 中没有 Client 没有参与的流程类似。这就要借助 JWT 完成访问了, 具体流程如下:
    首先你需要再 Google API 上创建一个服务账号(service account)
    获取服务账号的认证信息(credential),包括邮箱地址,client ID,以及一对公钥/私钥
    使用 client ID 和私钥创一个签名的 JWT,然后将这个 JWT 发送给 Google 交换 access token
    Google 返回 access token
    程序通过 access token 访问 API

    JWT 顾名思义,它是 JSON 结构的 token,由三部分组成:1) header 2) payload 3) signature
    header
    header 用于描述元信息,例如产生 signature 的算法:

    {
        "typ": "JWT",
        "alg": "HS256"
    }
    

    其中alg关键字就指定了使用哪一种哈希算法来创建 signature
    payload
    payload 用于携带你希望向服务端传递的信息。你既可以往里添加官方字段(这里的“字段” (field) 也可以被称作“声明” claims),例如iss(Issuer), sub(Subject), exp(Expiration time),也可以塞入自定义的字段,比如 userId:

    {
        "userId": "b08f86af-35da-48f2-8fab-cef3904660bd"
    }
    

    signature
    signature 译为「签名」
    创建签名要分以下几个步骤:

    你需要从接口服务端拿到密钥,假设为secret
    将header进行 base64 编码,假设结果为headerStr
    将payload进行 base64 编码,假设结果为payloadStr
    将headerStr和payloadStr用.字符串拼装起来成为字符data
    以data和secret作为参数,使用哈希算法计算出签名
    如果上述描述还不直观,用伪代码表示就是:

    // signature algorithm
    data = base64urlEncode( header ) + “.” + base64urlEncode( payload )
    signature = Hash( data, secret );
    

    by the why
    Python 有一个 jwt 包,可以直接根据一个json生成 jwt token

    import jwt
    token = jwt.encode(meta, '').decode()
    

    假设我们的原始 JSON 结构是这样的:

    // Header
    {
      "typ": "JWT",
      "alg": "HS256"
    }
    // Payload:
    {
      "userId": "b08f86af-35da-48f2-8fab-cef3904660bd"
    }
    

    如果密钥是字符串secret的话,那么最终 JWT 的结果就是这样的

    eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiJiMDhmODZhZi0zNWRhLTQ4ZjItOGZhYi1jZWYzOTA0NjYwYmQifQ.-xN_h82PHVTCMA9vdoHrcZxH-x5mb11y1537t3rGzcM
    

    JWT 究竟带来了什么

    JWT 的目的不是为了隐藏或者保密数据,而是为了确保数据确实来自被授权的人创建的(不被篡改)
    回想一下,当你拿到 JWT 时候,你完全可以在没有 secret 的情况下解码出 header 和 payload,因为 header 和 payload 只是经过了 base64 编码(encode)而已,编码的目的在于利于数据结构的传输。虽然创建 signature 的过程近似于加密 (encrypt),但本质其实是一种签名 (sign) 的行为,用于保证数据的完整性,实际上也并且并没有加密任何数据
    用于接口调用
    接下来在 API 调用中就可以附上 JWT (通常是在 HTTP Header 中)。又因为 SP 会与程序共享一个 secret,所以后端可以通过 header 提供的相同的 hash 算法来验证签名是否正确,从而判断应用是否有权力调用 API
    有状态的对话
    因为 HTTP 是无状态的,所以客户端和服务端需要解决的如何让之间的对话变得有状态。例如只有是登陆状态的用户才有权限调用某些接口,那么在用户登陆之后,需要记住该用户是已经登陆的状态。常见的方法是使用 session 机制
    常见的 session 模型是这样工作的:

    • 用户在浏览器登陆之后,服务端为用户生成唯一的 session id,存储在服务端的存储服务(例如 MySql, Redis)中
    • 该 session id 也同时返回给浏览器,以 SESSION_ID 为 KEY 存储在浏览器的 cookie 中
    • 如果用户再次访问该网站,cookie 里的 SESSION_ID 会随着请求一同发往服务端
      服务端通过判断 SESSION_ID 是否已经在 Redis 中判断用户是否处于登陆状态

    相信你已经察觉了,理论上来说,JWT 机制可以取代 session 机制。用户不需要提前进行登陆,后端也不需要 Redis 记录用户的登陆信息。客户端的本地保存一份合法的 JWT, 当用户需要调用接口时,附带上该合法的 JWT,每一次调用接口,后端都使用请求中附带的 JWT 做一次合法性的验证。这样也间接达到了认证用户的目的。

    转自关于JWT 和Token

  • 相关阅读:
    变量在函数内外的作用域 3
    php中用大括号把?>和<?php框起来的作用
    变量在函数内外的作用域 2
    变量在函数内外的作用域
    字母大小写对变量和函数的区别
    require()和include()代码重用
    str_place()替换函数
    【开源框架】Android之史上最全最简单最有用的第三方开源库收集整理,有助于快速开发,欢迎各位网友补充完善
    android SQLite使用SQLiteOpenHelper类对数据库进行操作
    tomcat设置IP地址或者域名访问
  • 原文地址:https://www.cnblogs.com/hi3254014978/p/16559523.html
Copyright © 2020-2023  润新知