“token是计算机术语令牌,令牌是一种能够控制站点占有媒体的特殊帧,以区别数据帧及其他控制帧;token其实说的更通俗点可以叫暗号,在一些数据传输之前,要先进行暗号的核对,不同的暗号被授权不同的数据操作。
前言
小编一直使用的是服务器的session,最近的项目,小编做成了前后端分离,遇到了一些问题:
所以这时候,小编就想到了使用token,来验证用户的合法性。
Token和session的区别
以上是小编认为Token和session之间的一些不同之处,所以小编觉得使用token比较方便。
JWT Token(JSON WEB TOKEN)
小编对于token的使用,是基于JWT的,因为这个标准也是比较主流的一种使用规范。JWT主要由三部分组成:
头部(header)
JWT的头部主要由两部分信息组成:
下边的json,就是一个完整的头部信息:
{ "typ": "JWT", "alg": "HS256" }
我们将这个头部信息使用base64加密,就得到了我们JWT的第一部分信息了:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
载荷(payload)
主要有三部分组成:标准中注册的声明、公共的声明、私有的声明。
标准中注册的声明:
自定义的声明
用户自己添加的一些信息,比如用户姓名、手机号等一些不敏感信息。
我们来看看一个完整的载荷(payload)的json数据:
{ "iss": 'jwt' "sub": "13011912019", "exp": "1530000000", "iat": "1529000000", "jti": "638069ab7a97771edcb91180f491d01e", "nickname": "kafei", "avatar": "http://oy98jbaeo.bkt.clouddn.com/avatar-1532401501" }
我们继续将这个载荷(payload)信息使用base64加密,就得到了我们JWT的第二部分信息了:
eyJpc3MiOiJqd3QiLCJzdWIiOiIxMzAxMTkxMjAxOSIsImV4cCI6IjE1MzAwMDAwMDAiLCJpYXQiOiIxNTI5MDAwMDAwIiwianRpIjoiNjM4MDY5YWI3YTk3NzcxZWRjYjkxMTgwZjQ5MWQwMWUiLCJuaWNrbmFtZSI6ImthZmVpIiwiYXZhdGFyIjoiaHR0cDovL295OThqYmFlby5ia3QuY2xvdWRkbi5jb20vYXZhdGFyLTE1MzI0MDE1MDEifQ
签证(sign)
前边我们已经获取到了头部信息的base64和载荷的base64的信息了,现在我们需要用这两个信息,生成一个签证(sign),这就是我们需要的第三部分信息了:
最后,我们将这三部分连接起来,就是我们需要的JWT了:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqd3QiLCJzdWIiOiIxMzAxMTkxMjAxOSIsImV4cCI6IjE1MzAwMDAwMDAiLCJpYXQiOiIxNTI5MDAwMDAwIiwianRpIjoiNjM4MDY5YWI3YTk3NzcxZWRjYjkxMTgwZjQ5MWQwMWUiLCJuaWNrbmFtZSI6ImthZmVpIiwiYXZhdGFyIjoiaHR0cDovL295OThqYmFlby5ia3QuY2xvdWRkbi5jb20vYXZhdGFyLTE1MzI0MDE1MDEifQ.eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
前端请求
将token放入到header里边的Authorization中,作为验证,如:
fetch('api/user/1', { headers: { 'Authorization': 'Bearer ' + token } })
优点
不足
建议
以上参考所写,参考网址什么是token机制?(JWT主要由三部分组成) - 秦志强笔记_网络新媒体营销策划、运营、推广知识分享
Django
#settings.py中配置redis库
# 配置redis库
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://:[email protected]:6379/0",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"PASSWORD": "mysecret"
}
},
"session": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://:[email protected]:6379/1",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"PASSWORD": "mysecret"
}
},
# 验证码信息: 存到 2 号库
"verify_code": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://:[email protected]:6379/2",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
},
}
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "session"
#utils/jwt_utils
import time
from django.core import signing
import hashlib
from django.core.cache import cache
HEADER = {'typ': 'JWP', 'alg': 'default'}
KEY = 'CHEN_FENG_YAO' #
SALT = 'www.lanou3g.com' #
TIME_OUT = 30 * 60 # 30min
def encrypt(obj):
"""加密"""
value = signing.dumps(obj, key=KEY, salt=SALT)
value = signing.b64_encode(value.encode()).decode()
return value
def decrypt(src):
"""解密"""
src = signing.b64_decode(src.encode()).decode()
raw = signing.loads(src, key=KEY, salt=SALT)
print(type(raw))
return raw
def create_token(username,user_id):
"""生成token信息"""
# 1. 加密头信息
header = encrypt(HEADER)
# 2. 构造Payload
payload = {"username": username,'user_id':user_id, "iat": time.time()}
payload = encrypt(payload)
# 3. 生成签名
md5 = hashlib.md5()
md5.update(("%s.%s" % (header, payload)).encode())
signature = md5.hexdigest()
token = "%s.%s.%s" % (header, payload, signature)
# 存储到缓存中
cache.set(username, token, TIME_OUT)
return token
def get_payload(token):
"""获取有效载荷"""
payload = str(token).split('.')[1]
payload = decrypt(payload)
return payload
# 通过token获取用户名
def get_username(token):
payload = get_payload(token)
return payload['username']
pass
def check_token(token):
username = get_username(token)
last_token = cache.get(username)
if last_token:
return last_token == token
return False
#view
调用
from .utils.jwt_utils import create_token,get_payload
token = create_token(user.username,user.id)
Flask
utils/jwt_util
import traceback
from datetime import datetime, timedelta
from flask import g #临时存储对象
import jwt #下载pip install pyjwt
from flask import current_app #当前应用程序文件的程序实例
# 生成token
def generate_jwt(payload, expiry, secret=None):
"""
:param payload: dict 载荷
:param expiry: datetime 有效期
:param secret: 盐
:return: token
"""
_payload = {
'exp': expiry
}
try:
_payload.update(payload)
if not secret:
#获取盐
secret = current_app.config['JWT_SECRET']
token = jwt.encode(_payload, secret, algorithm='HS256')
except:
error = traceback.format_exc()
print('error......', error)
return {'msg': 'not value'}
return token
def verify_jwt(token, secret=None):
"""
校验jwt
:param token: token值
:param secret: 盐
:return: payload 载荷
"""
if not secret:
secret = current_app.config['JWT_SECRET']
try:
payload = jwt.decode(token, secret, algorithms='HS256')
except:
error = traceback.format_exc()
print('error..', error)
payload = None
return payload
# 更新token 接口实现:
def _generate_token(account, user_id, refresh=True):
"""
生成token
:param self:
:param account:
:param refresh:
:return:
"""
# 获取盐
secret = current_app.config.get('JWT_SECRET')
# 定义过期时间
expiry = datetime.utcnow() + timedelta(hours=2) # 过期时间设置2小时
# 生成token
token = 'Bearer ' + generate_jwt({'account': account, 'user_id': user_id}, expiry, secret)
if refresh:
expiry = datetime.utcnow() + timedelta(days=15) # 过期时间重设为15天
# is_refresh作为更新token的信号
refresh_token = 'Bearer' + generate_jwt({'account': account, 'user_id': user_id, 'is_refresh': True}, expiry,
secret)
else:
refresh_token = None
return token, refresh_token
# (4).实现put刷新接口
def refresh_token():
"""
刷新token
"""
if g.account is not None and g.is_refresh is True:
token, refresh_token = _generate_token(g.account, g.user_id, refresh=False)
return {'message': 'ok', 'data': {'token': token}, 'code': 200}
else:
return {'message': 'Invalid refresh token', 'code': 500}
utils/login_util.py
# 强制登陆装饰器
from flask import g
from functools import wraps
def login_required(func):
"""
强制登录的装饰器
"""
@wraps(func)
def wrapper(*args, **kwargs):
# if not getattr(g, 'account'):
# return {'code': 401, 'message': 'Invalid token not account'}
if g.user_id is not None:
return func(*args, **kwargs)
return {'code': 401, 'message': 'Invalid token account is none'}
return wrapper
utils/middleware.py
from flask import request, g
from common.utils.jwt_util import verify_jwt
# def jwt_authentication():
# g.user_id = None
# # 获取请求头中的token
# token = request.headers.get('Authorization')
# if token is not None and token.startswith('Bearer '):
# token = token[7:]
# # 验证token
# payload = verify_jwt(token)
# if payload is not None:
# # 保存到g对象中
# g.user_id = payload.get('user_id')
# (3).改写验签代码: 将更新token信号放在g对象中
def jwt_authentication():
g.account = None
g.user_id = None
g.is_refresh = False
# 获取请求头中的token
token = request.headers.get('Authorization')
print('token>>>>>>>>>>>>>>>>>', str(token)[4:])
if token is not None and token.startswith('JWT Bearer '):
token = token[11:]
# print(token)
# 验证token
payload = verify_jwt(token)
# print(payload)
# print('payload', payload)
if payload is not None:
# 保存到g对象中
g.account = payload.get('account') # 用户名
g.user_id = payload.get('user_id') # 用户id
g.is_refresh = payload.get('is_refresh', False)
#最后配置请求前的钩子函数
# flask_create 项目内层目录
# 4.创建工厂函数封装APP
from flask import Flask
from flask_restful import Api
from flask_cors import CORS
from apscheduler.schedulers.background import BackgroundScheduler # 调度器
from apscheduler.executors.pool import ThreadPoolExecutor # 执行器 进程方式
import flask_whooshalchemyplus
from flask_whooshalchemyplus import index_all
from flask_create.resources.bptest.bpt import index_bp
from common.models import db, cache
from flask_create.resources.users.register import register_bp
from flask_create.resources.bptest.cloud_info import cloud_info_bp
from flask_create.news.news import news_bp
from flask_create.account.account_user import account_bp
from flask_create.channels.channel import channel_bp
from common.utils.middleware import jwt_authentication
from common.cron_task.news_task import update_recommend_list
from flask_create.book_manage.book import book_bp
from flask_create.account.user import user_bp
from flask_create.account.super_user import super_bp
from flask_create.course.course import course_bp
from flask_create.account.oauth_user import oauth2_bp
from flask_create.account.VerificationCode import verification_bp
from flask_create.goods.good import good_bp
from flask_create.pay.pay import pay_bp
from flask_create.path.path import path_bp
from flask_create.tests.test import test_bp
# 测试定时任务的函数:
def create_cron_task():
print('这是测试定时任务>>>>>>')
def create_flask_app(config):
app = Flask(__name__)
# 加载配置
app.config.from_object(config)
app.before_request(jwt_authentication) # 保存token
# db对象绑定app:
db.init_app(app)
# 初始化redis缓存:
cache.init_app(app)
# 注册蓝图:
app.register_blueprint(index_bp)
app.register_blueprint(register_bp)
app.register_blueprint(cloud_info_bp)
app.register_blueprint(channel_bp)
app.register_blueprint(news_bp)
app.register_blueprint(account_bp)
app.register_blueprint(book_bp)
app.register_blueprint(user_bp)
app.register_blueprint(super_bp)
app.register_blueprint(course_bp)
app.register_blueprint(oauth2_bp)
app.register_blueprint(verification_bp)
app.register_blueprint(good_bp)
app.register_blueprint(pay_bp)
app.register_blueprint(path_bp)
app.register_blueprint(test_bp)
# 跨域配置:
CORS(app, supports_credentials=True)
# 配置定时任务:
executors = {
'default': ThreadPoolExecutor(20)
}
app.scheduler = BackgroundScheduler(executors=executors)
# app.scheduler.add_job(create_cron_task,trigger='interval',seconds=10) # 定时缓存任务测试
# trigger是触发的意思,interval是间隔的意思
app.scheduler.add_job(update_recommend_list, trigger='interval', seconds=10, args=[cache, app])
app.scheduler.add_job(update_recommend_list, trigger='date', args=[cache, app]) # date的立即执行的意思
# app.scheduler.start() # 启动定时任务
# 创建api对象
api = Api(app)
# 配置全文索引
flask_whooshalchemyplus.init_app(app)
# with app.app_context():
# index_all(app)
return app