JWT
JWT(json web token): 头部、载荷、签名
头部 是一个 包含 加密算法和类型的 json字符串, 将字符串进行 base64 编码之后得到,一个 新的 字符串A
载荷 是一个json字符串, 包含 用户的身份信息, 将字符串进行 base64 编码之后得到,一个 新的 字符B, 不建议 存放用户的 敏感信息,因为 字符串可以被解密
将 字符串A 和字符串B用 . 链接, 得到一个新字符串,将新字符串使用密钥 和 第一部分声明的加密算法,进行加密,得到字符串C, 而C是不可逆的加C
最后,使用 . 链接 每一部分 字符串, 得到 字符串A.字符串B.字符串C
-
服务器 只需要保留 密钥和算法,当 收到 客户端发送的 jwt后,使用 同样的算法和密钥进行 解密,解密成功,说明 用户是合法的;不成功,说明 用户身份被伪造
-
本身避免session共享问题
-
为了避免 csrf 攻击问题,将 jwt字符串存放到 请求头中
pip install flask-jwt-extended # flask项目中使用jwt插件
流程
配置项
import datetime
import os
class Config(object):
DEBUG = True # 调试模式
SQLALCHEMY_DATABASE_URI = 'mysql://root:mysql@127.0.0.1:3306/day08' # 数据库的地址
SQLALCHEMY_TRACK_MODIFICATIONS = False # 默认不追踪 mysql修改的信号量
SECRET_KEY = b'\xe6O$\xe8\t\xa5\x7f\xf4\x01#7K\x03\x91\x94d' # 指明加密的密钥
# 构建项目所在的 绝对路径,也就是 day08 的绝对路径
BASE_DIR = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
# 静态资源存放路径
STATIC_ROOT = os.path.join(BASE_DIR, 'static/')
# 自定义的 图片上传路径
MEDIA_ROOT = os.path.join(BASE_DIR, 'media/')
JWT_ACCESS_TOKEN_EXPIRES = datetime.timedelta(days=1) # 指明token的过期时间
# JWT_HEADER_TYPE = 'JWT' # 可以修改 请求头中 ,token字符串的 前缀
# 默认 Authorization: Bearer <token>
# 修改: Authorization: JWT <token>
注册
class RegisterView(Resource):
def post(self):
# 1. 创建解析参数对象
parser = reqparse.RequestParser()
# 2. 指明需要解析的参数
parser.add_argument('username', type=str, location=['form', 'json'], required=True, help='输入合法的用户名')
parser.add_argument('password', type=str, location=['form', 'json'], required=True, help='输入安全的密码')
# 3. 获取解析后的参数
username = parser.parse_args().get('username')
password = parser.parse_args().get('password')
# 3.1 校验 用户名规则: 字母、数字、_组成, 字母开头,6-20位
if not re.match(r'^[a-zA-Z]\w{5,19}$', username):
return {
'msg': '用户名不合法'
}, 400
# 3.2 校验用户名是否重复
if User.query.filter_by(username=username).count():
return {
'msg': '该用户名已存在'
}
# 3.3 校验 密码规则: 必须存在 数字、 大写字母、小写字母, 8-20位
if not re.match(r'^(?=.*[A-Z])(?=.*[a-z])(?=.*\d).{8,20}$', password):
return {
'msg': '密码不安全'
}, 400
# 4. 对密码进行加密
new_password = generate_password_hash(password)
# 5. 创建用户对象
user = User(username=username, password=new_password)
# 6. 将用户对象添加到 事务中
db.session.add(user)
# 7. 提交事务
db.session.commit()
# 8. 注册成功
return {
'msg': '用户注册成功'
}
登录
登录成功,需要 生成 jwt字符串
在 flask扩展项中,挂载
flask_jwt_extend
插件在 项目的 配置中,指明
SECRET_KEY
或者JWT_SECRET_KEY
, 密钥可以使用os.urandom(n)
, 指明生成固定长度的密钥在 项目的 配置中,指明
JWT_ACCESS_TOKEN_EXPIRES
, 也就是 token的过期时间
class LoginView(Resource):
def post(self):
# 1. 创建解析参数对象
parser = reqparse.RequestParser()
# 2. 指明需要解析的参数
parser.add_argument('username', type=str, location=['form', 'json'], required=True, help='输入合法的用户名')
parser.add_argument('password', type=str, location=['form', 'json'], required=True, help='输入安全的密码')
# 3. 获取解析后的参数
username = parser.parse_args().get('username')
password = parser.parse_args().get('password')
# 4. 需要从数据库中查询 得到 用户对象
user = User.query.filter_by(username=username).first()
# 5. 如果用户不存在,返回错误信息
if not user:
return {
'msg': '用户名或密码错误'
}, 401
# 6. 校验 密码
if not check_password_hash(user.password, password):
return {
'msg': '用户名或密码错误'
}, 401
# 7. 生成token
token = create_access_token(identity={'id': user.id, 'username': user.username})
# 8. 返回token
return {
'token': token,
'usernamne': user.username
}
用户中心 (身份认证)
通过 请求头中 的
token
进行身份认证, 使用 默认提供的 装饰器进行身份认证
from flask_restful import Resource, reqparse
from flask_jwt_extended import jwt_required, get_jwt_identity
class UserInfoView(Resource):
method_decorators = [jwt_required()]
def get(self):
# 1. 获取token中的 载荷信息, 也就是 上一步登录成功。生成token时,存入 token的信息
user = get_jwt_identity()
return user
用户评论新闻 (实用例)
创建评论表,外键关联 新闻表、用户表
评论添加时,需要先认证用户,认证成功,可以获取用户id,可以正常发表评论
模型类
# 评论表:
class Comment(db.Model):
__tablename__ = 'tb_comment'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
content = db.Column(db.String(256))
news_id = db.Column(db.Integer, db.ForeignKey('tb_news.id'))
news = db.relationship('News', backref='comments')
user_id = db.Column(db.Integer, db.ForeignKey('tb_user.id'))
user = db.relationship('User', backref='comments')
评论添加
from flask_jwt_extended import jwt_required, get_jwt_identity
from flask_restful import Resource, reqparse
# 新闻评论
class NewsCommentView(Resource):
# 指明当前视图类所需的装饰器,也就是 该视图类的所有方法都需要先 执行装饰器进行用户认证
method_decorators = [jwt_required()]
def post(self):
# 1. 创建解析参数对象
parser = reqparse.RequestParser()
# 2. 指明需要解析的参数
parser.add_argument('content', type=str, location=['form', 'json'], required=True, help='输入评论内容')
parser.add_argument('news_id', type=int, location=['form', 'json'], required=True, help='请确定评论的新闻')
# 3. 获取解析后的参数
content = parser.parse_args().get('content')
news_id = parser.parse_args().get('news_id')
# 从 token的 载荷中获取用户id
user_id = get_jwt_identity().get('id')
# 4. 创建评论对象
comment = Comment(content=content, user_id=user_id, news_id=news_id)
# 6. 将用户对象添加到 事务中
db.session.add(comment)
# 7. 提交事务
db.session.commit()
# 8. 注册成功
return {
'msg': '评论成功'
}