Browse Source

feat(openiddict): Enhance session validity checks

- `UserInfoIdentitySession` 标记为已过时
- 增加 `ValidationTokenCheckIdentitySession` 用于验证过程中检查会话
- 增加 `ServerValidationTokenCheckIdentitySession` 用于授权过程中检查会话
pull/1416/head
colin 2 months ago
parent
commit
01b03d6d71
  1. 9
      aspnet-core/modules/identity/LINGYUN.Abp.Identity.Domain/LINGYUN/Abp/Identity/Session/IdentitySessionManager.cs
  2. 2
      aspnet-core/modules/openIddict/LINGYUN.Abp.OpenIddict.Application.Contracts/LINGYUN/Abp/OpenIddict/Scopes/OpenIddictScopeCreateOrUpdateDto.cs
  3. 11
      aspnet-core/modules/openIddict/LINGYUN.Abp.OpenIddict.AspNetCore.Session/LINGYUN/Abp/OpenIddict/AspNetCore/Session/AbpOpenIddictAspNetCoreSessionModule.cs
  4. 43
      aspnet-core/modules/openIddict/LINGYUN.Abp.OpenIddict.AspNetCore.Session/LINGYUN/Abp/OpenIddict/AspNetCore/Session/ServerValidationTokenCheckIdentitySession.cs
  5. 1
      aspnet-core/modules/openIddict/LINGYUN.Abp.OpenIddict.AspNetCore.Session/LINGYUN/Abp/OpenIddict/AspNetCore/Session/UserinfoIdentitySession.cs
  6. 44
      aspnet-core/modules/openIddict/LINGYUN.Abp.OpenIddict.AspNetCore.Session/LINGYUN/Abp/OpenIddict/AspNetCore/Session/ValidationTokenCheckIdentitySession.cs
  7. 62
      aspnet-core/modules/openIddict/LINGYUN.Abp.OpenIddict.AspNetCore/LINGYUN/Abp/OpenIddict/AspNetCore/Controllers/UserInfoController.cs

9
aspnet-core/modules/identity/LINGYUN.Abp.Identity.Domain/LINGYUN/Abp/Identity/Session/IdentitySessionManager.cs

@ -37,15 +37,16 @@ public class IdentitySessionManager : DomainService, IIdentitySessionManager
if (claimsPrincipal != null) if (claimsPrincipal != null)
{ {
var userId = claimsPrincipal.FindUserId(); var userId = claimsPrincipal.FindUserId();
var tenantId = claimsPrincipal.FindTenantId();
using (CurrentTenant.Change(tenantId))
{
var sessionId = claimsPrincipal.FindSessionId(); var sessionId = claimsPrincipal.FindSessionId();
if (!userId.HasValue || sessionId.IsNullOrWhiteSpace()) if (!userId.HasValue || sessionId.IsNullOrWhiteSpace())
{ {
return; return;
} }
var tenantId = claimsPrincipal.FindTenantId();
using (CurrentTenant.Change(tenantId))
{
if (await IdentitySessionStore.ExistAsync(sessionId, cancellationToken)) if (await IdentitySessionStore.ExistAsync(sessionId, cancellationToken))
{ {
return; return;

2
aspnet-core/modules/openIddict/LINGYUN.Abp.OpenIddict.Application.Contracts/LINGYUN/Abp/OpenIddict/Scopes/OpenIddictScopeCreateOrUpdateDto.cs

@ -23,5 +23,5 @@ public abstract class OpenIddictScopeCreateOrUpdateDto : ExtensibleObject
public Dictionary<string, string> Properties { get; set; } = new Dictionary<string, string>(); public Dictionary<string, string> Properties { get; set; } = new Dictionary<string, string>();
public List<string> Resources { get; set; } = new List<string>(); public HashSet<string> Resources { get; set; } = new HashSet<string>();
} }

11
aspnet-core/modules/openIddict/LINGYUN.Abp.OpenIddict.AspNetCore.Session/LINGYUN/Abp/OpenIddict/AspNetCore/Session/AbpOpenIddictAspNetCoreSessionModule.cs

@ -2,6 +2,7 @@
using LINGYUN.Abp.Identity.Session; using LINGYUN.Abp.Identity.Session;
using LINGYUN.Abp.Identity.Session.AspNetCore; using LINGYUN.Abp.Identity.Session.AspNetCore;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using OpenIddict.Validation;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using static OpenIddict.Abstractions.OpenIddictConstants; using static OpenIddict.Abstractions.OpenIddictConstants;
@ -20,7 +21,8 @@ public class AbpOpenIddictAspNetCoreSessionModule : AbpModule
builder.AddEventHandler(ProcessSignOutIdentitySession.Descriptor); builder.AddEventHandler(ProcessSignOutIdentitySession.Descriptor);
builder.AddEventHandler(ProcessSignInIdentitySession.Descriptor); builder.AddEventHandler(ProcessSignInIdentitySession.Descriptor);
builder.AddEventHandler(RevocationIdentitySession.Descriptor); builder.AddEventHandler(RevocationIdentitySession.Descriptor);
builder.AddEventHandler(UserInfoIdentitySession.Descriptor); builder.AddEventHandler(ServerValidationTokenCheckIdentitySession.Descriptor);
// builder.AddEventHandler(UserInfoIdentitySession.Descriptor);
}); });
} }
@ -36,5 +38,12 @@ public class AbpOpenIddictAspNetCoreSessionModule : AbpModule
{ {
options.PersistentSessionGrantTypes.Add(GrantTypes.Password); options.PersistentSessionGrantTypes.Add(GrantTypes.Password);
}); });
context.Services.Add(ValidationTokenCheckIdentitySession.Descriptor.ServiceDescriptor);
Configure<OpenIddictValidationOptions>(options =>
{
options.Handlers.Add(ValidationTokenCheckIdentitySession.Descriptor);
});
} }
} }

43
aspnet-core/modules/openIddict/LINGYUN.Abp.OpenIddict.AspNetCore.Session/LINGYUN/Abp/OpenIddict/AspNetCore/Session/ServerValidationTokenCheckIdentitySession.cs

@ -0,0 +1,43 @@
using LINGYUN.Abp.Identity.Session;
using Microsoft.Extensions.Logging;
using OpenIddict.Server;
using System.Security.Principal;
using System.Threading.Tasks;
using Volo.Abp.MultiTenancy;
using static OpenIddict.Abstractions.OpenIddictConstants;
namespace LINGYUN.Abp.OpenIddict.AspNetCore.Session;
public class ServerValidationTokenCheckIdentitySession : IOpenIddictServerHandler<OpenIddictServerEvents.ValidateTokenContext>
{
protected ICurrentTenant CurrentTenant { get; }
protected IIdentitySessionChecker IdentitySessionChecker { get; }
public static OpenIddictServerHandlerDescriptor Descriptor { get; } =
OpenIddictServerHandlerDescriptor.CreateBuilder<OpenIddictServerEvents.ValidateTokenContext>()
.UseSingletonHandler<ServerValidationTokenCheckIdentitySession>()
.SetOrder(OpenIddictServerHandlers.Protection.ValidatePrincipal.Descriptor.Order + 2_000)
.SetType(OpenIddictServerHandlerType.Custom).Build();
public ServerValidationTokenCheckIdentitySession(
ICurrentTenant currentTenant,
IIdentitySessionChecker identitySessionChecker)
{
CurrentTenant = currentTenant;
IdentitySessionChecker = identitySessionChecker;
}
public async virtual ValueTask HandleAsync(OpenIddictServerEvents.ValidateTokenContext context)
{
var tenantId = context.Principal.FindTenantId();
using (CurrentTenant.Change(tenantId))
{
if (!await IdentitySessionChecker.ValidateSessionAsync(context.Principal))
{
context.Logger.LogWarning("The token is no longer valid because the user's session expired.");
// Errors.InvalidToken ---> 401
// Errors.ExpiredToken ---> 400
context.Reject(Errors.InvalidToken, "The user session has expired.");
}
}
}
}

1
aspnet-core/modules/openIddict/LINGYUN.Abp.OpenIddict.AspNetCore.Session/LINGYUN/Abp/OpenIddict/AspNetCore/Session/UserinfoIdentitySession.cs

@ -11,6 +11,7 @@ namespace LINGYUN.Abp.OpenIddict.AspNetCore.Session;
/// <summary> /// <summary>
/// UserInfoEndpoint 检查用户会话 /// UserInfoEndpoint 检查用户会话
/// </summary> /// </summary>
[Obsolete("UserInfoIdentitySession is outdated, please use the CheckIdentitySessionOnServerValidationToken")]
public class UserInfoIdentitySession : IOpenIddictServerHandler<OpenIddictServerEvents.HandleUserInfoRequestContext> public class UserInfoIdentitySession : IOpenIddictServerHandler<OpenIddictServerEvents.HandleUserInfoRequestContext>
{ {
protected ICurrentTenant CurrentTenant { get; } protected ICurrentTenant CurrentTenant { get; }

44
aspnet-core/modules/openIddict/LINGYUN.Abp.OpenIddict.AspNetCore.Session/LINGYUN/Abp/OpenIddict/AspNetCore/Session/ValidationTokenCheckIdentitySession.cs

@ -0,0 +1,44 @@
using LINGYUN.Abp.Identity.Session;
using Microsoft.Extensions.Logging;
using OpenIddict.Validation;
using System.Security.Principal;
using System.Threading.Tasks;
using Volo.Abp.MultiTenancy;
using static OpenIddict.Abstractions.OpenIddictConstants;
namespace LINGYUN.Abp.OpenIddict.AspNetCore.Session;
public class ValidationTokenCheckIdentitySession : IOpenIddictValidationHandler<OpenIddictValidationEvents.ValidateTokenContext>
{
protected ICurrentTenant CurrentTenant { get; }
protected IIdentitySessionChecker IdentitySessionChecker { get; }
public static OpenIddictValidationHandlerDescriptor Descriptor { get; }
= OpenIddictValidationHandlerDescriptor.CreateBuilder<OpenIddictValidationEvents.ValidateTokenContext>()
.UseSingletonHandler<ValidationTokenCheckIdentitySession>()
.SetOrder(OpenIddictValidationHandlers.Protection.ValidatePrincipal.Descriptor.Order + 2_000)
.SetType(OpenIddictValidationHandlerType.Custom)
.Build();
public ValidationTokenCheckIdentitySession(
ICurrentTenant currentTenant,
IIdentitySessionChecker identitySessionChecker)
{
CurrentTenant = currentTenant;
IdentitySessionChecker = identitySessionChecker;
}
public async virtual ValueTask HandleAsync(OpenIddictValidationEvents.ValidateTokenContext context)
{
var tenantId = context.Principal.FindTenantId();
using (CurrentTenant.Change(tenantId))
{
if (!await IdentitySessionChecker.ValidateSessionAsync(context.Principal))
{
context.Logger.LogWarning("The token is no longer valid because the user's session expired.");
// Errors.InvalidToken ---> 401
// Errors.ExpiredToken ---> 400
context.Reject(Errors.InvalidToken, "The user session has expired.");
}
}
}
}

62
aspnet-core/modules/openIddict/LINGYUN.Abp.OpenIddict.AspNetCore/LINGYUN/Abp/OpenIddict/AspNetCore/Controllers/UserInfoController.cs

@ -1,62 +0,0 @@
using OpenIddict.Abstractions;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Security.Claims;
using Volo.Abp.Users;
using VoloUserInfoController = Volo.Abp.OpenIddict.Controllers.UserInfoController;
namespace LINGYUN.Abp.OpenIddict.AspNetCore.Controllers;
[ExposeServices(
typeof(VoloUserInfoController),
typeof(UserInfoController))]
public class UserInfoController : VoloUserInfoController
{
protected async override Task<Dictionary<string, object>> GetUserInfoClaims()
{
var user = await UserManager.GetUserAsync(User);
if (user == null)
{
return null;
}
var claims = new Dictionary<string, object>(StringComparer.Ordinal)
{
// Note: the "sub" claim is a mandatory claim and must be included in the JSON response.
[OpenIddictConstants.Claims.Subject] = await UserManager.GetUserIdAsync(user),
[AbpClaimTypes.SessionId] = CurrentUser.FindSessionId(),
};
if (User.HasScope(OpenIddictConstants.Scopes.Profile))
{
claims[AbpClaimTypes.TenantId] = user.TenantId;
claims[OpenIddictConstants.Claims.PreferredUsername] = user.UserName;
claims[OpenIddictConstants.Claims.FamilyName] = user.Surname;
claims[OpenIddictConstants.Claims.GivenName] = user.Name;
}
if (User.HasScope(OpenIddictConstants.Scopes.Email))
{
claims[OpenIddictConstants.Claims.Email] = await UserManager.GetEmailAsync(user);
claims[OpenIddictConstants.Claims.EmailVerified] = await UserManager.IsEmailConfirmedAsync(user);
}
if (User.HasScope(OpenIddictConstants.Scopes.Phone))
{
claims[OpenIddictConstants.Claims.PhoneNumber] = await UserManager.GetPhoneNumberAsync(user);
claims[OpenIddictConstants.Claims.PhoneNumberVerified] = await UserManager.IsPhoneNumberConfirmedAsync(user);
}
if (User.HasScope(OpenIddictConstants.Scopes.Roles))
{
claims[OpenIddictConstants.Claims.Role] = await UserManager.GetRolesAsync(user);
}
// Note: the complete list of standard claims supported by the OpenID Connect specification
// can be found here: http://openid.net/specs/openid-connect-core-1_0.html#StandardClaims
return claims;
}
}
Loading…
Cancel
Save