首先,HTTP 是无状态的协议(对于事务处理没有记忆能力,每次客户端和服务端会话完成时,服务端不会保存任何会话信息)——每个请求都是完全独立的,服务端无法确认当前访问者的身份信息,无法分辨上一次的请求发送者和这一次的发送者是不是同一个人。所以服务器与浏览器为了进行会话跟踪(知道是谁在访问自己),就必须主动的去维护一个状态,这个状态用于告知服务端前后两个请求是否来自同一浏览器。为此,前端开发者便加入了Cookie来实现有状态的HTTP连接。而后实现授权的方式就有cookie、session、token和JWT。

什么是 JWT?

JWT.IO 解释:JSON Web Token (JWT) 是一个开放标准 ( RFC 7519 ),它定义了一种紧凑且自包含的方式,用于在各方之间作为 JSON 对象安全地传输信息。该信息可以被验证和信任,因为它是经过数字签名的。JWT 可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对进行签名。

案例

由于网上许多案例都为HS256(对称加密),所以这里我使用RSA256(非对称加密)作为补充。

  1. 首先需要生成私钥和公钥

    • 查阅《Generate OpenSSL RSA Key Pair using genpkey》得到了带密码的pem文件, 但是在使用中会出现TypeError: Password was not given but private key is encrypted的错误。

    • 从《How to generate JWT RS256 key》找到了解决办法

        ssh-keygen -t rsa -b 4096 -m PEM -f jwtRS256.key
      # Don't add passphrase
      openssl rsa -in jwtRS256.key -pubout -outform PEM -out jwtRS256.key.pub
      cat jwtRS256.key
      cat jwtRS256.key.pub
  2. 选择Python的JWT库,我这里选择了两个库

    • PyJWT(需要cryptography库)

        >>> import jwt
      >>> with open('jwtRS256.key', 'rb') as f:
      ... private_key = f.read()
      ...
      >>> with open('jwtRS256.key.pub', 'rb') as f:
      ... public_key = f.read()
      ...
      >>> print(encoded)
      eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzb21lIjoicGF5bG9hZCJ9.4twFt5NiznN84AWoo1d7KO1T_yoc0Z6XOpOVswacPZg
      >>> decoded = jwt.decode(encoded, public_key, algorithms=["RS256"])
      {'some': 'payload'}
    • Authlib

        >>> from authlib.jose import jwt
      >>> header = {'alg': 'RS256'}
      >>> payload = {'iss': 'Authlib', 'sub': '123', ...}
      >>> with open('jwtRS256.key', 'rb') as f:
      ... private_key = f.read()
      ...
      >>> s = jwt.encode(header, payload, private_key)
      >>> with open('jwtRS256.key.pub', 'rb') as f:
      ... public_key = f.read()
      ...
      >>> claims = jwt.decode(s, public_key)
      >>> print(claims)
      {'iss': 'Authlib', 'sub': '123', ...}
      >>> print(claims.header)
      {'alg': 'RS256', 'typ': 'JWT'}
      >>> claims.validate()
  3. 工作原理

Using JWT for user authentication in Flask》中的代码参考:

# flask imports
from flask import Flask, request, jsonify, make_response
from flask_sqlalchemy import SQLAlchemy
import uuid # for public id
from werkzeug.security import generate_password_hash, check_password_hash
# imports for PyJWT authentication
import jwt
from datetime import datetime, timedelta
from functools import wraps # creates Flask object
app = Flask(__name__)
# configuration
# NEVER HARDCODE YOUR CONFIGURATION IN YOUR CODE
# INSTEAD CREATE A .env FILE AND STORE IN IT
app.config['SECRET_KEY'] = 'your secret key'
# database name
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///Database.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
# creates SQLALCHEMY object
db = SQLAlchemy(app) # Database ORMs
class User(db.Model):
id = db.Column(db.Integer, primary_key = True)
public_id = db.Column(db.String(50), unique = True)
name = db.Column(db.String(100))
email = db.Column(db.String(70), unique = True)
password = db.Column(db.String(80)) # decorator for verifying the JWT
def token_required(f):
@wraps(f)
def decorated(*args, **kwargs):
token = None
# jwt is passed in the request header
if 'x-access-token' in request.headers:
token = request.headers['x-access-token']
# return 401 if token is not passed
if not token:
return jsonify({'message' : 'Token is missing !!'}), 401 try:
# decoding the payload to fetch the stored details
data = jwt.decode(token, app.config['SECRET_KEY'])
current_user = User.query\
.filter_by(public_id = data['public_id'])\
.first()
except:
return jsonify({
'message' : 'Token is invalid !!'
}), 401
# returns the current logged in users contex to the routes
return f(current_user, *args, **kwargs) return decorated # User Database Route
# this route sends back list of users users
@app.route('/user', methods =['GET'])
@token_required
def get_all_users(current_user):
# querying the database
# for all the entries in it
users = User.query.all()
# converting the query objects
# to list of jsons
output = []
for user in users:
# appending the user data json
# to the response list
output.append({
'public_id': user.public_id,
'name' : user.name,
'email' : user.email
}) return jsonify({'users': output}) # route for loging user in
@app.route('/login', methods =['POST'])
def login():
# creates dictionary of form data
auth = request.form if not auth or not auth.get('email') or not auth.get('password'):
# returns 401 if any email or / and password is missing
return make_response(
'Could not verify',
401,
{'WWW-Authenticate' : 'Basic realm ="Login required !!"'}
) user = User.query\
.filter_by(email = auth.get('email'))\
.first() if not user:
# returns 401 if user does not exist
return make_response(
'Could not verify',
401,
{'WWW-Authenticate' : 'Basic realm ="User does not exist !!"'}
) if check_password_hash(user.password, auth.get('password')):
# generates the JWT Token
token = jwt.encode({
'public_id': user.public_id,
'exp' : datetime.utcnow() + timedelta(minutes = 30)
}, app.config['SECRET_KEY']) return make_response(jsonify({'token' : token.decode('UTF-8')}), 201)
# returns 403 if password is wrong
return make_response(
'Could not verify',
403,
{'WWW-Authenticate' : 'Basic realm ="Wrong Password !!"'}
) # signup route
@app.route('/signup', methods =['POST'])
def signup():
# creates a dictionary of the form data
data = request.form # gets name, email and password
name, email = data.get('name'), data.get('email')
password = data.get('password') # checking for existing user
user = User.query\
.filter_by(email = email)\
.first()
if not user:
# database ORM object
user = User(
public_id = str(uuid.uuid4()),
name = name,
email = email,
password = generate_password_hash(password)
)
# insert user
db.session.add(user)
db.session.commit() return make_response('Successfully registered.', 201)
else:
# returns 202 if user already exists
return make_response('User already exists. Please Log in.', 202) if __name__ == "__main__":
# setting debug to True enables hot reload
# and also provides a debuger shell
# if you hit an error while running the server
app.run(debug = True)

总结

大部分语言都已经支持了JWT,这里可以从jwt.io的类库中可以看出。目前JWT主要运用于OAuth1、OAuth2和OpenID等单点登录功能,而且将来会有更多的企业和系统开发需要使用JWT技术。而且我也非常感谢本文中引用的原作者提供了相关的材料,便于我们学习。

Python开发篇——如何在Flask下编写JWT登录的更多相关文章

  1. Python开发篇——构建虚拟Python开发环境(Conda+Poetry)

    前言 之前虽略有提及Python,但是没有实际地写点料.惭愧,惭愧,所以这次先起个头,讲讲如何构建虚拟Python开发环境.相信之前看过我博客的人可能会想:博主不会又要聊聊Docker吧?放心,不会. ...

  2. Python开发程序:生产环境下实时统计网站访问日志信息

    日志实时分析系统 生产环境下有需求:要每搁五分钟统计下这段时间内的网站访问量.UV.独立IP等信息,用直观的数据表格表现出来 环境描述: 网站为Nginx服务,系统每日凌晨会对日志进行分割,拷贝到其他 ...

  3. Python开发篇——基于React-Dropzone开发上传组件

    这次我要讲述的是在React-Flask框架上开发上传组件的技巧.我目前主要以React开发前端,在这个过程中认识到了许多有趣的前端UI框架--React-Bootstrap.Ant Design.M ...

  4. 如何在linux下编写一个简单的Shell脚本程序

    在了解了linux终端和其搭配的基本Shell(默认为bash)的基础下,我们就可以在终端中用vi/vim编辑器编写一个shell的脚本程序了 Shell既为一种命令解释解释工具,又是一种脚本编程语言 ...

  5. 【神经网络与深度学习】【Python开发】Caffe配置 windows下怎么安装protobuf for python

    首先从google上下载protobuf-2.5.0.zip和protoc-2.5.0-win32.zip,然后把protoc-2.5.0-win32.zip里的protoc.exe放到protobu ...

  6. 用python开发调试器——起始篇

    首先,你得准备一套python开发环境,正常情况下,一般是在windows下开发的,因为win系统应用广泛,再则就是要有个IDE,这里我选择我熟悉的Eclipse.环境搭建,网上都有,比如:http: ...

  7. Python轻量Web框架Flask使用

    http://blog.csdn.net/jacman/article/details/49098819 目录(?)[+] Flask安装 Python开发工具EclipsePyDev准备 Flask ...

  8. linux一句话问答(网络无关篇+网络相关篇+程序开发篇+经典图书)

    一句话问答(网络无关篇+网络相关篇+程序开发篇+经典图书) --------------------------目录-网络无关篇-目录-------------------------- 0001 修 ...

  9. python开发接口

    享一段代码,开发了3个接口:            1.上传文件            2.查看所有文件            3.下载文件 使用python开发,需要安装flask模块,使用pip ...

随机推荐

  1. NX二次开发-克隆操作

    模板文件: 克隆替换字符串: 1 #include "Text.h" 2 extern DllExport void ufsta(char *param, int *returnC ...

  2. 『无为则无心』Python基础 — 8、Python中的数据类型(数值、布尔、字符串)

    目录 1.数据类型介绍 2.数值型(Number) 3.布尔型(bool) 4.None(空值) 5.常量 6.字符串(String) 1.数据类型介绍 (1)什么是数据类型 在生活中,我们日常使用的 ...

  3. Kubernetes的亲和性和反亲和性

    节点亲缘性规则可以影响pod被调度到哪个节点.但是,这些规则只影响了pod和节点之间的亲缘性.然而,有些时候也希望能有能力指定pod自身之间的亲缘性. 举例来说,想象一下有一个前端pod和一个后端po ...

  4. AnyCast技术

    在公司项目经历过DDoS攻击后,选用了一些比较成熟的DDoS防护厂商,在学习过程中,发现,许多DDoS厂商的防护技术都离不开 Anycast网络. 所以在这里整理一下AnyCast的相关资料. 1. ...

  5. 八皇后O(1)算法题解

    题目描述 在国际象棋棋盘上(8*8)放置八个皇后,使得任意两个皇后之间不能在同一行,同一列,也不能位于同于对角线上.问共有多少种不同的方法,并且按字典序从小到大指出各种不同的放法. 题解 见证奇迹的时 ...

  6. 由ASP.NET Core WebApi添加Swagger报错引发的探究

    缘起 在使用ASP.NET Core进行WebApi项目开发的时候,相信很多人都会使用Swagger作为接口文档呈现工具.相信大家也用过或者了解过Swagger,这里咱们就不过多的介绍了.本篇文章记录 ...

  7. Java:TreeMap中LinkedHashMap和Map中HashMap的区别

    一般情况下,我们用的最多的是HashMap,在Map 中插入.删除和定位元素,HashMap 是最好的选择. 但如果您要bai按自然顺序或自定义顺序遍历键,那么TreeMap会更好.如果需要输出的顺序 ...

  8. 将Acunetix与CircleCI集成

    如果要在DevSecOps中包含Acunetix ,则需要将其与CI / CD系统集成.Acunetix具有针对最受欢迎的CI / CD系统Jenkins的现成集成.但是,您可以使用Acunetix ...

  9. 如何在Apache HttpClient中设置TLS版本

    1.简介 Apache HttpClient是一个底层.轻量级的客户端HTTP库,用于与HTTP服务器进行通信. 在本教程中,我们将学习如何在使用HttpClient时配置支持的传输层安全(TLS)版 ...

  10. python正则表达式应用

    import re ab='''ms: [["", "\u7acb\u5373\u4e0b\u8f7d"], ["", "\u52 ...