feat: 完成双token刷新

This commit is contained in:
ccnetcore
2025-06-29 15:18:30 +08:00
parent d4f00eb89f
commit 6a58af8dfb
7 changed files with 344 additions and 326 deletions

View File

@@ -14,6 +14,6 @@ namespace Yi.Framework.Rbac.Domain.Shared.Options
public string SecurityKey { get; set; } = "892u4j1803qj23jro0fjkf8bmsdf9nb9mf92834u23jdf923jrnmvasbceqwt347562tgdhdnsv9wevbnop";
public long ExpiresMinuteTime { get; set; } = 120;
public long ExpiresSecondTime { get; set; } = 600;
}
}

View File

@@ -19,9 +19,9 @@ namespace Yi.Framework.Rbac.Domain.Authorization
public class RefreshTokenMiddleware : IMiddleware, ITransientDependency
{
private AccountManager _accountManager;
public RefreshTokenMiddleware(AccountManager accountManager)
{
_accountManager = accountManager;
}
@@ -29,6 +29,10 @@ namespace Yi.Framework.Rbac.Domain.Authorization
{
var refreshToken = context.Request.Headers["refresh_token"].ToString();
if (!string.IsNullOrEmpty(refreshToken))
{
//Jwt鉴权失败过期了再去找刷新token进行刷新处理不用每次都去刷新
var bearerAuthResult = await context.AuthenticateAsync("Bearer");
if (!bearerAuthResult.Succeeded)
{
//每个用户的token刷新频率可以进行控制防止刷新token当访问token使用
var authResult = await context.AuthenticateAsync(TokenTypeConst.Refresh);
@@ -45,7 +49,15 @@ namespace Yi.Framework.Rbac.Domain.Authorization
//请求头替换,补充后续鉴权逻辑
context.Request.Headers["Authorization"] = "Bearer " + access_Token;
}
//刷新token 与 access_token都失效了
// else
// {
//context.Response.StatusCode = StatusCodes.Status401Unauthorized;
//return;
// }
}
}
await next(context);
}
}
@@ -57,8 +69,6 @@ namespace Yi.Framework.Rbac.Domain.Authorization
{
app.UseMiddleware<RefreshTokenMiddleware>();
return app;
}
}
}

View File

@@ -106,7 +106,7 @@ namespace Yi.Framework.Rbac.Domain.Managers
issuer: _jwtOptions.Issuer,
audience: _jwtOptions.Audience,
claims: claims,
expires: DateTime.Now.AddMinutes(_jwtOptions.ExpiresMinuteTime),
expires: DateTime.Now.AddSeconds(_jwtOptions.ExpiresSecondTime),
notBefore: DateTime.Now,
signingCredentials: creds);
string returnToken = new JwtSecurityTokenHandler().WriteToken(token);
@@ -127,7 +127,7 @@ namespace Yi.Framework.Rbac.Domain.Managers
issuer: _refreshJwtOptions.Issuer,
audience: _refreshJwtOptions.Audience,
claims: claims,
expires: DateTime.Now.AddMinutes(_refreshJwtOptions.ExpiresMinuteTime),
expires: DateTime.Now.AddSeconds(_refreshJwtOptions.ExpiresSecondTime),
notBefore: DateTime.Now,
signingCredentials: creds);
string returnToken = new JwtSecurityTokenHandler().WriteToken(token);

View File

@@ -190,25 +190,21 @@ namespace Yi.Framework.Rbac.Domain.Managers
{
//此处优先从缓存中获取
UserRoleMenuDto output = null;
var tokenExpiresMinuteTime =
LazyServiceProvider.GetRequiredService<IOptions<JwtOptions>>().Value.ExpiresMinuteTime;
var tokenExpiresSecondTime =
LazyServiceProvider.GetRequiredService<IOptions<JwtOptions>>().Value.ExpiresSecondTime;
var cacheData = await _userCache.GetOrAddAsync(new UserInfoCacheKey(userId),
async () =>
{
var user = await _userRepository.GetUserAllInfoAsync(userId);
var data = EntityMapToDto(user);
//系统用户数据被重置,老前端访问重新授权
if (data is null)
{
throw new AbpAuthorizationException();
}
//data.Menus.Clear();
output = data;
output = data ?? throw new AbpAuthorizationException();
return new UserInfoCacheItem(data);
},
() => new DistributedCacheEntryOptions
{ AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(tokenExpiresMinuteTime) });
{ AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(tokenExpiresSecondTime) });
if (cacheData is not null)
{

View File

@@ -6,7 +6,7 @@ import { Session } from "@/utils/storage";
import useAuths from "@/hooks/useAuths";
const {VITE_APP_ENV_NAME} = import.meta.env;
const { getToken, removeToken } = useAuths();
const {getToken,getRefreshToken, removeToken, setToken, setRefreshToken} = useAuths();
const {base_url, request_timeout, pre_interface} = config;
export const PATH_URL = base_url[VITE_APP_ENV_NAME];
@@ -28,6 +28,11 @@ service.interceptors.request.use(
if (token) {
config.headers["Authorization"] = `Bearer ${token}`;
}
const refreshToken = getRefreshToken();
if (refreshToken) {
config.headers["refresh_token"] = `${refreshToken}`;
}
if (Session.get("tenantId")) {
config.headers["TenantId"] = Session.get("tenantId");
}
@@ -44,25 +49,37 @@ service.interceptors.response.use(
(response) => {
const {config} = response;
config.isFinish = true;
//后端返回双token替换
if (response.headers["refresh_token"]) {
setToken(response.headers["access_token"]);
setRefreshToken(response.headers["refresh_token"]);
// //然后修改config重新请求
// config.headers["Authorization"] = `Bearer ${getToken()}`;
// service.request(config);
}
return Promise.resolve(response);
},
(error) => {
const {config} = error;
// 对响应错误做点什么
if (error.message.indexOf("timeout") != -1) {
if (error.message.indexOf("timeout") !== -1) {
ElMessage({
type: "error",
message: "网络超时",
});
} else if (error.message == "Network Error") {
} else if (error.message === "Network Error") {
ElMessage({
type: "error",
message: "网络连接错误",
});
} else {
//处理状态码和消息
const res = error.response || {};
const status = Number(res.status) || 200;
const message = res?.data?.error?.message;
if (status === 401) {
ElMessageBox.confirm("该功能需要登陆后享有,是否立即登录?", "提示", {
confirmButtonText: "确认",
@@ -74,6 +91,7 @@ service.interceptors.response.use(
});
return;
}
//处理非200
if (status !== 200) {
if (status >= 500) {
ElMessage({

View File

@@ -14,7 +14,6 @@ export const AUTH_USER = "AUTH_USER";
export default function useAuths(opt) {
const defaultOpt = {
loginUrl: "/login", // 登录页跳转url 默认: /login
loginReUrl: "", // 登录页登陆成功后带重定向redirect=的跳转url 默认为空
@@ -178,8 +177,6 @@ const currentUserInfo=computed(()=>{
};
// 注册
const registerFun = async (params) => {
// try {

View File

@@ -1,6 +1,6 @@
import axios from 'axios';
import {getToken} from '@/utils/auth'
export let isRelogin = {show: false};
// import JsonBig from 'json-bigint'
const myaxios = axios.create({
@@ -22,7 +22,6 @@ const myaxios = axios.create({
})
// 请求拦截器
myaxios.interceptors.request.use(function (config) {
if (getToken()) {
@@ -37,8 +36,7 @@ myaxios.interceptors.request.use(function (config) {
// 响应拦截器
myaxios.interceptors.response.use(function (response) {
//业务错误
if(response.data.statusCode==403)
{
if (response.data.statusCode == 403) {
ElMessage.error(response.data.errors)
}
return response;
@@ -46,8 +44,7 @@ myaxios.interceptors.response.use(function (response) {
const code = error.response.status;
const msg = error.message;
//业务异常+应用异常,统一处理
switch(code)
{
switch (code) {
case 401:
ElMessage.error('登录已过期')
break;