背景
现在的项目使用了vue + flask的框架,后端flask用token来做验证,但是会面临一个token过期的问题,设置过长的token过期时间或者设置一个refresh token的时间来处理都不能满足我这个项目的需求,我们希望的是在token即将过期的时候前端发起一个刷新token的请求,后端签发一个新的token。
解决方案
后端:
在首次签发token的时候,给出token的死亡时间点以及需要刷新的刷新时间点,我这里使用的是时间戳,代码如下:
# 登录验证
if not request.form:
return ResponseUtil.response(isSuccess=False, message=MessageEnum.MISSING_FORM.value)
form = request.form.to_dict()
username = form.get('user')
password = form.get('password')
# 服务端与客户端时间偏差验证
check_time = int(form.get('checkTime'))
server_time = int(time.time())
if check_time > (server_time + server_client_time) or check_time < (server_time - server_client_time):
return ResponseUtil.response(isSuccess=False, message="服务器与客户端时间偏差超过3分钟,请调整后再尝试登陆!")
if not username:
return ResponseUtil.response(isSuccess=False, message=MessageEnum.MISSING_USERNAME.value)
if not password:
return ResponseUtil.response(isSuccess=False, message=MessageEnum.MISSING_PASSWORD.value)
user_item = GetUserApi.get(form)
if not user_item:
return ResponseUtil.response(isSuccess=False, message=MessageEnum.ACCOUNT_WRONG.value)
# 签发token
access_token = create_access_token(identity=user_item.role_id)
died_time = int(time.time()) + expire_time
refresh_time = int(time.time()) + expire_time - allow_refresh_time
data = {
"token": access_token,
"name": user_item.username,
"died_time": died_time, # token死亡时间点
"refresh_time": refresh_time, # token刷新时间点
"id": user_item.user_id
}
return ResponseUtil.response(isSuccess=True, data=data, message=MessageEnum.LOGIN_SUCCESS.value)
前端
大概思路是在获取token的时候将token、刷新时间、死亡时间存储在本地,然后用请求拦截器在每次发起请求的时候对比一下当前时间戳与存储的刷新/死亡时间戳,对于
刷新时间 < 当前时间 < 死亡时间
则发起刷新token的请求,对于
当前时间 > 死亡时间
的跳转到登录界面重新登录。代码如下:
// 存储登录的相关信息
// user login
_login({ commit }, formdatas) {
return new Promise((resolve, reject) => {
login(formdatas)
.then(res => {
if (res.success) {
Message.success(res.msg)
commit('SET_TOKEN', 'Bearer ' + res.data.token)
commit('SET_NAME', res.data.name)
commit('SET_DIEDTIME', res.data.died_time) // 存储token死亡时间
commit('SET_REFRESHTIME', res.data.refresh_time) // 存储token刷新时间
commit('SET_ID', res.data.id)
} else {
Message.error(res.msg)
}
resolve(res)
})
.catch(error => {
reject(error)
})
})
}
// 请求拦截器
$axios.interceptors.request.use(
config => {
// loading = Loading.service({ text: '拼命加载中' })
let token = store.getters.token
// 获取死亡时间与刷新时间
const diedTime = store.getters.diedTime
const refreshTime = store.getters.refreshTime
if (token) {
let tmp = Date.parse(new Date()).toString()
tmp = tmp.substr(0, 10)
// 对比时间
if (tmp >= refreshTime && tmp < diedTime && checkToken) {
checkToken = false
// getToken为发起获取新token的接口
getToken().then(res => {
checkToken = true
store.commit('user/SET_TOKEN', 'Bearer ' + res.data.token)
store.commit('user/SET_DIEDTIME', res.data.died_time)
store.commit('user/SET_REFRESHTIME', res.data.refresh_time)
token = store.getters.token
})
} else if (tmp >= diedTime) {
store.commit('user/DEL_TOKEN')
router.replace({
path: '/login',
query: {
redirect: '/dashbord'
}
})
}
config.headers.Authorization = token // 请求头部添加token
}
return config
},
error => {
return Promise.reject(error)
}
)
# 获取新token的后端代码
class GetTokenApi(Resource):
@jwt_required
def get(self):
from myapi.app import expire_time, allow_refresh_time
current_user = get_jwt_identity()
new_token = create_access_token(identity=current_user)
died_time = int(time.time()) + expire_time
refresh_time = int(time.time()) + expire_time - allow_refresh_time
data = {
"token": new_token,
"died_time": died_time,
"refresh_time": refresh_time
}
return ResponseUtil.response(isSuccess=True, data=data)
大家有什么不明白的欢迎提问,觉得代码有问题的也欢迎指正
版权声明:本文为qq_36215993原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。