空间设计工作室网站,期末成绩管理网站开发背景,网站推广有哪些方案,学而思网校官网在之前的课程中,介绍过 Flask-Login 框架#xff0c;它是基于 Session 和 Cookie 技术来实现用户授权和验证的#xff0c;不过 Session 有很多的局限性#xff0c;这一节介绍一种基于 token 的验证方式 —— JWT (JSON Web Token)#xff0c;除了对 JWT 的概念讲解之外它是基于 Session 和 Cookie 技术来实现用户授权和验证的不过 Session 有很多的局限性这一节介绍一种基于 token 的验证方式 —— JWT (JSON Web Token)除了对 JWT 的概念讲解之外还有在 Flask 中简单实践
session 的局限性
基于 Session 的验证过程大体是服务器端有一个 Session 词典当用户验证登录后在词典中为该用户创建一个 Session 对象在响应 response 中返回一个 Session id当用户下次请求时携带 Session id服务器从 Session 词典中可以恢复出 Session 对象以完成用户的验证在用 Session id 从恢复出认证实体。
从 Session 验证过程可以看出一些局限性 服务器横向扩展很困难因为 Session 只能存活在一个服务实例中将用户请求引导到其他服务器将丢掉用户的登录状态 携带信息量少恢复会话信息比较耗时Session 认证后客户端得到 Session ID, 服务器无法从 Session ID 中得到更多信息需要从数据库、文件系统或缓存中取得用户信息比较耗时 没有统一标准Session 由各个服务器框架自己实现没有统一标准存在应用扩展困难的问题特别加密方式五花八门有很大的安全隐患
token 简介
为了解决 Session 的问题有了 token 的验证方式。
token 可以理解成票据或者凭证当用户得到服务器的认证后由服务器颁发在之后的请求时携带免去频繁登录。
token 不同于 Session 的地方 可以独立于具体的服务器框架生成和校验 可以携带更多的信息避免对持久层的查询操作 基于标准的算法可以由不同的节点完成验证
为了利用好 token 的验证机制IEIT (互联网工程任务组)制定了基于 JSON 数据结构的网络认证方式 JWAJSON Web Algorithms还针对不同应用场景提出了具体协议如 JWS、JWE、JWK 等他们可以统称为 JWT即 Javascript Web Token。
理解 JWA
JWA 的全称是 JSON Web Algorithms
JSON 是 Javascript 的语言的文本对象表示法是一种独立语言环境的数据结构表示可以用网络数据传输在前面 RESTful 章节中对 API 调用的返回数据格式就是 JSON。
Algorithms 本义是算法的意思这里特指加密算法也就是用 JSON 表示的数据经过加密后在在服务器端和客户段之间传输。
有了数据结构和加密算法的基础根据不同的应用场景定义出了具体实现 JWSJSON Web Signature对数据进行签名的用于防止数据被篡改传输不敏感数据的情况 JWEJSON Web Encryption对数据做了加密的用于传输敏感数据具有更好的安全性 JWKJSON Web Key是通过密钥对数据进行加密的方法规定了相应的加密算法
JWTJSON Web Token上面 JWS、JWE 和 JWK 的总称。
JWT 简介
JWT Wiki 上的定义是: JSON Web Token is an Internet standard for creating JSON-based access tokens that assert some number of claims. 大致意思是JWT 是用基于 JSON 数据结构的生成包含了一些权限声明的网络访问凭证的网络标准
数据结构
JWT 由 Header、Payload 和Signature三部分组成像这样的形式
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJBdXRobGliIiwic3ViIjoiMTIzIiwibmFtZSI6ImJvYiJ9.cBo6e7Uss5__16mlqZECjHJSKJDdyisevDP5cUGvJms 换行符只是为了展示用实际 token 中不包括换行符 Header
用于指定采用的加密算法以及 JWT 采用的形式类型例如
{ alg : HS256, typ : JWT} alg 指定前面所用的算法默认为 HmacSHA256 简写为 HS256还有 HS384、RS256 等 typ 是指令牌的类型JWT 令牌的类型为 JWT
Payload
用于携带一些信息例如用户名过期时间 等等例如:
{ sub: 1234567890, name: John Doe, admin: true}
JWT 标准定义了 7 个字段
字段说明iss(issuer)签发人exp(expiration time)过期时间sub(subject)主题aud(audience)受众nbf(Not Before)生效时间iat(Issued At)签发时间jti(JWT ID)编号
这些字段有实现这自由选取也可以加入其他自定义字段 Signature
首先需要指定一个密钥secret。密钥很重要需要严格保密
然后使用 Header 里面指定的签名算法默认是 HMAC SHA256按照下面的公式产生签名:
HMACSHA256( base64UrlEncode(header) . base64UrlEncode(payload), secret)
即先将 header 和 payload 分别做 base64url 编码 然后用 . 将他们连接成一个字符串用加密算法使用密钥 secret 得到的加密结果就算签名 Base64URL 编码字符集是 Base64 字符集的子集 被省略、 替换成 -/ 替换成_ 因为 token 可能通过 URL 进行传输而、、/ 在 URL 中有特殊含义 验证
当客户端发送请求时将 token 送到服务器端可以用和签名同样的方式重新计算一次签名如果和客户端送过来的签名一致说明 token 没有被篡改如果不一致说明 token 已被篡改不安全了。
由此可见用于做签名的密钥 secret 很重要一旦泄漏将无法鉴别 token 的真伪
JWT 应用
关于 Python 的 JWT 实现不止一个不同的库不同的实现方式层出不穷今天要讲解的是 Python 的 Authlib 库它是一个大而全的 Python Web 验证库支持多种 Python 框架
Authlib 的 JWT
Authlib 是构建 OAuth 和 OpenID 安全连接服务器的终极 Python 库包括了 JWS, JWE, JWK, JWA, JWT
Authlib 功能强大而丰富今天我们只了解他的 JWT 部分之后在介绍基于第三方认证的 OAuth 技术时还会进一步讲解
安装
使用 pip 安装
pip install Authlib
如果一切正常可以导入 Authlib 模板例如引入 jwt : from authlib.jose import jwt
小试牛刀
JWT 是服务器端的机制所以可以在命令行中做测试
生成 token from authlib.jose import jwt header {alg: HS256} payload {iss: Authlib, sub: 123, name: bob} secret 123abc. token jwt.encode(header, payload, secret) print(token)beyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJBdXRobGliIiwic3ViIjoiMTIzIiwibmFtZSI6ImJvYiJ9.cBo6e7Uss5__16mlqZECjHJSKJDdyisevDP5cUGvJms 导入 jwt 模块 定义 header并且设置签名算法为 HS256 定义 payload作为传输信息 定义 secret注意这里只是方便演示实际项目中最好是随机生成并妥善保存 使用 jwt 的 encode 方法生成 tokenencode 方法一次性实现了所有关于 JWT 协议的定义 打印出 token可见被 . 分隔为三部分前两部分是 header 和 payload的 Base64Url 编码最后一部分是 签名
解码 token
接上面的环境 claims jwt.decode(token, secret) print(claims){iss: Authlib, sub: 123, name: bob} print(claims.header){alg: HS256, typ: JWT} claims.validate() 用 jwt 模块的 decode 方法利用 secret 对 token 进行解码如果签名正确就会得到解码内容解码对象是 authlib.jose.JWTClaims 类的实例 打印出解码内容可以看到和生成 token 时的 payload 内容一致 打印出 header可以看到 typ 为JWT即使用默认值 validate 方法用于检验 token 的有效性比如是否过期、主题是否一致是否没到生效时间等等也可以针对每种情况单独做验证例如validate_exp 可用检验是否过期
虽然 JWT 理论很繁琐但 Authlib 库提供了简洁的方法让开发应用变得更高效
与客户端交互
JWT 之所有流行有个重要原因是可以支持多种客户端例如 浏览器和 appJWT 标准规定一般情况下客户端需要将 token 放在 Http 请求的 Header 中的 Authorization 字段中举个例子
GET /resource HTTP/1.1 Host: server.example.com Authorization: Bearer mF_9.B5f-4.1JqM 用 GET 方式请求 /resource 在 Header 中添加了 Authorization 字段 不能直接将 token 作为 Authorization的值必须有类型声明这里是Bearer Bearer 表示这个 token 是由认证服务器生成的用来做身份识别的除此之外IEIT 还定义了其他 认证类型如 Bisic, Digest可以简单理解成 Bearer 就是 JWT 的认证类型 除了通过 Http Header 类携带 token 之外还可以通过 POST 请求主体以及 URL 中的 querystring 来向服务器发送 token这两种情况下需要使用 access_token 字段来表示 token JWT 标准建议使用 Header 方式除非 Header 无法使用时才考虑其他方式 Flask JWT
Authlib 主要的用途在打造一个 OAuth 应用对于单独做 JWT 的实践有些麻烦因此我们用 flask-jwt 框架做 JWT 的实践。
flask-jwt 和之前讲述的 flask-login 用法很像是基于 JWT 的认证的框架提供和很多方便实践的特性
安装 flask-jwt
pip install Flask-JWT
创建应用
为了简单将所有代码放在 app.py 中:
from flask import Flaskfrom flask_jwt import JWT, jwt_required, current_identityfrom werkzeug.security import safe_str_cmp# User 类用于模拟用户实体class User(object): def __init__(self, id, username, password): self.id id self.username username self.password password def __str__(self): return User(id%s) % self.id# User 实体集合用于模拟用户对象的缓存users [ User(1, user1, abcxyz), User(2, user2, abcxyz),]username_table {u.username: u for u in users}userid_table {u.id: u for u in users}# 获取认证的回调函数从 request 中得到登录凭证返回凭证所代表的 用户实体def authenticate(username, password): user username_table.get(username, None) if user and safe_str_cmp(user.password.encode(utf-8), password.encode(utf-8)): return user# 通过 token 获得认证主体的回调函数def identity(payload): user_id payload[identity] return userid_table.get(user_id, None)app Flask(__name__)app.debug Trueapp.config[SECRET_KEY] super-secretjwt JWT(app, authenticate, identity) # 用 JWT 初始化应用app.route(/protected, methods [GET, POST]) # 定义一个 endpointjwt_required() # 声明需要 token 才能访问def protected(): return %s % current_identity # 验证通过返回 认证主体if __name__ __main__: app.run()
运行:
$ python app.py * Serving Flask app app (lazy loading) * Environment: production WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. * Debug mode: on * Running on http://127.0.0.1:5000/ (Press CTRLC to quit) * Restarting with stat * Debugger is active! * Debugger PIN: 566-326-511
获取 access_token
flask-jwt 默认的获取 token 的路由是/auth请求方式是 POST用 JSON 传送用户名密码给服务器例如:
$ curl -X POST -H Content-Type: application/json localhost:5000/auth -d {username:user1,password:abcxyz}{ access_token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9. eyJleHAiOjE...省略...VudGl0eSI6MX0. M-shnDPAVdu...省略...LaH1EMIbrWjPto}
如果登录凭证正确则返回 access_token可以看到被 . 分隔成三部分即 JWT 的结构
使用 access_token
flask-jwt 默认通过 Header 传送 token为了和 OAuth 生成的 JWT 做区分默认使用JWT 作为 token 的类型例如用上面生成的 JWT 请求 /protected
curl -H Authorization: jwt eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE...省略...VudGl0eSI6MX0.M-shnDPAVdu...省略...LaH1EMIbrWjPto localhost:5000/protectedUser(id1)
如果 token 有效则返回 token 对应的认证实体这个例子中打印出了 user 实体
总结
本节课程讲解了基于 token 验证的 JWT使用 Authlib 库对 JWT 做了实践练习期望能帮助您更好的理解 JWT最后通过 flask-jwt 模块实践了 JWT 的验证方式和使用方式。在后续的课程中还会对目前流行的第三方认证框架 OAuth 做介绍敬请期待。