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': '評論成功'
}