feat: 后端完成双token刷新功能

This commit is contained in:
陈淳
2024-01-24 11:26:44 +08:00
parent c18334002c
commit c9e01e0782
7 changed files with 51 additions and 12 deletions

View File

@@ -1,7 +1,6 @@
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Lazy.Captcha.Core; using Lazy.Captcha.Core;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@@ -10,7 +9,6 @@ using Volo.Abp;
using Volo.Abp.Application.Services; using Volo.Abp.Application.Services;
using Volo.Abp.Authorization; using Volo.Abp.Authorization;
using Volo.Abp.Caching; using Volo.Abp.Caching;
using Volo.Abp.EventBus.Local;
using Volo.Abp.Guids; using Volo.Abp.Guids;
using Volo.Abp.Uow; using Volo.Abp.Uow;
using Volo.Abp.Users; using Volo.Abp.Users;
@@ -43,7 +41,7 @@ namespace Yi.Framework.Rbac.Application.Services
ICaptcha captcha, ICaptcha captcha,
IGuidGenerator guidGenerator, IGuidGenerator guidGenerator,
IOptions<RbacOptions> options, IOptions<RbacOptions> options,
IAliyunManger aliyunManger) IAliyunManger aliyunManger )
{ {
_userRepository = userRepository; _userRepository = userRepository;
_currentUser = currentUser; _currentUser = currentUser;
@@ -64,6 +62,7 @@ namespace Yi.Framework.Rbac.Application.Services
/// <summary> /// <summary>
/// 效验图片登录验证码,无需和账号绑定 /// 效验图片登录验证码,无需和账号绑定
/// </summary> /// </summary>
[AllowAnonymous]
private void ValidationImageCaptcha(LoginInputVo input) private void ValidationImageCaptcha(LoginInputVo input)
{ {
if (_rbacOptions.EnableCaptcha) if (_rbacOptions.EnableCaptcha)
@@ -83,6 +82,7 @@ namespace Yi.Framework.Rbac.Application.Services
/// </summary> /// </summary>
/// <param name="input"></param> /// <param name="input"></param>
/// <returns></returns> /// <returns></returns>
[AllowAnonymous]
public async Task<object> PostLoginAsync(LoginInputVo input) public async Task<object> PostLoginAsync(LoginInputVo input)
{ {
if (string.IsNullOrEmpty(input.Password) || string.IsNullOrEmpty(input.UserName)) if (string.IsNullOrEmpty(input.Password) || string.IsNullOrEmpty(input.UserName))
@@ -99,11 +99,24 @@ namespace Yi.Framework.Rbac.Application.Services
//获取token //获取token
var accessToken = await _accountManager.GetTokenByUserIdAsync(user.Id); var accessToken = await _accountManager.GetTokenByUserIdAsync(user.Id);
var refreshToken = _accountManager.CreateRefreshToken(user.Id);
return new { Token = accessToken, RefreshToken = refreshToken };
return new { Token = accessToken };
} }
/// <summary>
/// 刷新token
/// </summary>
/// <param name="refresh_token"></param>
/// <returns></returns>
[Authorize(AuthenticationSchemes = TokenTypeConst.Refresh)]
public async Task<object> PostRefreshAsync([FromQuery] string refresh_token)
{
var userId = CurrentUser.Id.Value;
var accessToken = await _accountManager.GetTokenByUserIdAsync(userId);
var refreshToken = _accountManager.CreateRefreshToken(userId);
return new { Token = accessToken, RefreshToken = refreshToken };
}
/// <summary> /// <summary>
/// 生成验证码 /// 生成验证码

View File

@@ -25,5 +25,7 @@ namespace Yi.Framework.Rbac.Domain.Shared.Consts
public const string Permission = nameof(Permission); public const string Permission = nameof(Permission);
public const string RoleInfo=nameof(RoleInfo); public const string RoleInfo=nameof(RoleInfo);
public const string Refresh=nameof(Refresh);
} }
} }

View File

@@ -35,5 +35,11 @@ namespace Yi.Framework.Rbac.Domain.Extensions
return roleOrNull is null ? null : JsonConvert.DeserializeObject<List<RoleTokenInfoModel>>(roleOrNull); return roleOrNull is null ? null : JsonConvert.DeserializeObject<List<RoleTokenInfoModel>>(roleOrNull);
} }
public static bool IsRefreshToken(this ICurrentUser currentUser)
{
var refreshOrNull = currentUser.FindClaims(TokenTypeConst.Refresh).Select(x => x.Value).FirstOrDefault();
return refreshOrNull is null ? false : bool.Parse(refreshOrNull);
}
} }
} }

View File

@@ -41,7 +41,7 @@ namespace Yi.Framework.Rbac.Domain.Managers
, IOptions<JwtOptions> jwtOptions , IOptions<JwtOptions> jwtOptions
, ILocalEventBus localEventBus , ILocalEventBus localEventBus
, UserManager userManager , UserManager userManager
,IOptions<RefreshJwtOptions> refreshJwtOptions , IOptions<RefreshJwtOptions> refreshJwtOptions
, ISqlSugarRepository<RoleEntity> roleRepository) , ISqlSugarRepository<RoleEntity> roleRepository)
{ {
_repository = repository; _repository = repository;
@@ -50,7 +50,7 @@ namespace Yi.Framework.Rbac.Domain.Managers
_localEventBus = localEventBus; _localEventBus = localEventBus;
_userManager = userManager; _userManager = userManager;
_roleRepository = roleRepository; _roleRepository = roleRepository;
_refreshJwtOptions= refreshJwtOptions.Value; _refreshJwtOptions = refreshJwtOptions.Value;
} }
/// <summary> /// <summary>
@@ -112,11 +112,15 @@ namespace Yi.Framework.Rbac.Domain.Managers
return returnToken; return returnToken;
} }
private string CreateRefreshToken() public string CreateRefreshToken(Guid userId)
{ {
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_refreshJwtOptions.SecurityKey)); var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_refreshJwtOptions.SecurityKey));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var claims =new List<Claim> { new Claim("Refresh", "true") } ; //添加用户id及刷新token的标识
var claims = new List<Claim> {
new Claim(AbpClaimTypes.UserId,userId.ToString()),
new Claim(TokenTypeConst.Refresh, "true")
};
var token = new JwtSecurityToken( var token = new JwtSecurityToken(
issuer: _refreshJwtOptions.Issuer, issuer: _refreshJwtOptions.Issuer,
audience: _refreshJwtOptions.Audience, audience: _refreshJwtOptions.Audience,

View File

@@ -10,6 +10,7 @@ namespace Yi.Framework.Rbac.Domain.Managers
{ {
public interface IAccountManager : IDomainService public interface IAccountManager : IDomainService
{ {
string CreateRefreshToken(Guid userId);
Task<string> GetTokenByUserIdAsync(Guid userId); Task<string> GetTokenByUserIdAsync(Guid userId);
Task LoginValidationAsync(string userName, string password, Action<UserEntity> userAction = null); Task LoginValidationAsync(string userName, string password, Action<UserEntity> userAction = null);
Task RegisterAsync(string userName, string password, long phone); Task RegisterAsync(string userName, string password, long phone);

View File

@@ -21,7 +21,7 @@ namespace Yi.Framework.Rbac.SqlSugarCore
{ {
DataPermissionFilter(sqlSugarClient); DataPermissionFilter(sqlSugarClient);
} }
base.CustomDataFilter(sqlSugarClient); base.CustomDataFilter(sqlSugarClient);
} }
@@ -33,7 +33,7 @@ namespace Yi.Framework.Rbac.SqlSugarCore
protected void DataPermissionFilter(ISqlSugarClient sqlSugarClient) protected void DataPermissionFilter(ISqlSugarClient sqlSugarClient)
{ {
//获取当前用户的信息 //获取当前用户的信息
if (CurrentUser.Id == null) return; if (CurrentUser.Id == null || CurrentUser.IsRefreshToken()) return;
//管理员不过滤 //管理员不过滤
if (CurrentUser.UserName.Equals(UserConst.Admin) || CurrentUser.Roles.Any(f => f.Equals(UserConst.AdminRolesCode))) return; if (CurrentUser.UserName.Equals(UserConst.Admin) || CurrentUser.Roles.Any(f => f.Equals(UserConst.AdminRolesCode))) return;
var expUser = Expressionable.Create<UserEntity>(); var expUser = Expressionable.Create<UserEntity>();

View File

@@ -25,6 +25,7 @@ using Yi.Framework.AspNetCore.Microsoft.AspNetCore.Builder;
using Yi.Framework.AspNetCore.Microsoft.Extensions.DependencyInjection; using Yi.Framework.AspNetCore.Microsoft.Extensions.DependencyInjection;
using Yi.Framework.Bbs.Application; using Yi.Framework.Bbs.Application;
using Yi.Framework.Rbac.Application; using Yi.Framework.Rbac.Application;
using Yi.Framework.Rbac.Domain.Shared.Consts;
using Yi.Framework.Rbac.Domain.Shared.Options; using Yi.Framework.Rbac.Domain.Shared.Options;
namespace Yi.Abp.Web namespace Yi.Abp.Web
@@ -144,7 +145,7 @@ namespace Yi.Abp.Web
} }
}; };
}) })
.AddJwtBearer("Refresh", options => { .AddJwtBearer(TokenTypeConst.Refresh, options => {
options.TokenValidationParameters = new TokenValidationParameters options.TokenValidationParameters = new TokenValidationParameters
{ {
ClockSkew = TimeSpan.Zero, ClockSkew = TimeSpan.Zero,
@@ -153,6 +154,18 @@ namespace Yi.Abp.Web
ValidAudience = refreshJwtOptions.Audience, ValidAudience = refreshJwtOptions.Audience,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(refreshJwtOptions.SecurityKey)) IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(refreshJwtOptions.SecurityKey))
}; };
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["refresh_token"];
if (!string.IsNullOrEmpty(accessToken))
{
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
}) })
.AddQQ(options => .AddQQ(options =>