1、用户登录生成的身份token
一般都是在登录成功后调用这个生成token的方法,并返回给前端(登录方法略,自行实现)。
/**
* @Author YuanChangLiang
* @Date 2020/11/10 20:47
*/
public class TokenUtil {
/**
* token过期时间
*/
private static final long EXPIRE_TIME = 30 * 60 * 1000;
/**
* token秘钥
*/
private static final String TOKEN_SECRET = "YuanChangLiang";
/**
* 生成签名,30分钟过期
* @param username 用户名
* @param loginTime 登录时间
* @return 生成的token
*/
public static String sign(String username, String loginTime) {
try {
// 设置过期时间
Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
// 私钥和加密算法
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
// 设置头部信息
Map<String, Object> header = new HashMap<>(2);
header.put("Type", "Jwt");
header.put("alg", "HS256");
// 返回token字符串
return JWT.create()
.withHeader(header)
.withClaim("loginName", username)
.withClaim("loginTime", loginTime)
.withExpiresAt(date)
.sign(algorithm);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 检验token是否正确
* @param token 需要校验的token
* @return 校验是否成功
*/
public static boolean verify(String token){
try {
//设置签名的加密算法:HMAC256
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
JWTVerifier verifier = JWT.require(algorithm).build();
DecodedJWT jwt = verifier.verify(token);
return true;
} catch (Exception e){
return false;
}
}
}
2、token过期自动无感知刷新,需要刷新令牌refreToken
实现思路:
实现用户无感知的刷新token值,这里需要改造登录的方法,返回我们两个token,一个身份token和一个刷新token,当身份token有效期过期时,我们判断刷新token是否过期,没有过期则携带刷新token请求接口生成新的身份token,拿到最新的token值后再重新发起刚刚因token过期的请求。从而实现无感知(我们也可以使用redis来存储身份token和刷新token,用redis存身份token可以实现单点登录,同时redis也可以设置过期时间,设置刷新token过期,刷新token过期了,则需要重新登录。)
登录后后台接口返回值要求:必须提供刷新token的令牌(接口方法自行实现略)
// 携带refresh_token重新获取新的用户token
export const updataTokenAPI = function () {
return request({
method: 'PUT',
url: '/v1_0/authorizations',
headers: {
Authorization: `Bearer ${store.state.user.refresh_token}`
}
})
}
import request from '@/utils/request'
import store from '@/store'
// 请求响应器
request.interceptors.request.use(function (config) {
// config :本次请求的配置对象
// config 里面有一个属性:headers
const { user } = store.state
//请求头未配置信息的时候才会配置
if (user.token && config.headers.Authorizatio === undefined) {
config.headers.Authorization = `Bearer ${user.token}`
}
// 这里必须将config返回出去,否则请求会停在这 里
return config
}, function (error) {
// 如果请求出错(还没发送出去,可能是代码写错了的问题),就会进入这里
return Promise.reject(error)
})
// 阻拦响应器
request.interceptors.response.use(function (response) {
return response
}, async function (error) {
if (error.response && error.response.status === 401) {
// token续签方式2: refreshToken(用户无感知)
// 将过期的token值清空
store.commit('updataToken', '')
//请求刷新token接口
const { data: res } = await updataTokenAPI()
//保存新的token值
store.commit('updataToken', res.data.token)
// 再调用一次未完成的请求啊(用户无感知)
// error.config 就是上一次axios请求的配置对象
// console.dir(error.config)
// 把新的token赋予到下一次axios请求的请求头中
error.config.headers.Authorization = 'Bearer ' + res.data.token
// return到await的地方,将未完成的请求再次发起,
return axios(error.config)
} else if (error.response.status === 500 && error.config.url === '/v1_0/authorizations') {
// 因为500的情况有很多种,refresh_token失效也是其中一种情况,所有再加上error.config.url === '/v1_0/authorizations'条件,确保是refresh_token失效情况
// 清空所有的token和refresh_toekn,并且强制跳转登录页面
store.commit('upUser', {})
router.push({ path: '/login' })
Toast.fail('身份已过期')
}
return Promise.reject(error)
})