Browse Source

fix(ids): 扩展授权验证器实现也需要记录安全日志.

pull/465/head
cKey 4 years ago
parent
commit
11feeb3f83
  1. 86
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.SmsValidator/LINGYUN/Abp/IdentityServer/SmsValidator/SmsTokenGrantValidator.cs
  2. 2
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.SmsValidator/LINGYUN/Abp/IdentityServer/SmsValidator/SmsValidatorConsts.cs
  3. 3
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/MiniProgram/WeChatMiniProgramGrantValidator.cs
  4. 3
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/Official/WeChatOfficialGrantValidator.cs
  5. 108
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/WeChatGrantValidator.cs

86
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.SmsValidator/LINGYUN/Abp/IdentityServer/SmsValidator/SmsTokenGrantValidator.cs

@ -3,19 +3,21 @@ using IdentityServer4.Events;
using IdentityServer4.Models; using IdentityServer4.Models;
using IdentityServer4.Services; using IdentityServer4.Services;
using IdentityServer4.Validation; using IdentityServer4.Validation;
using LINGYUN.Abp.Identity;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Security.Claims; using System.Security.Claims;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.Identity;
using Volo.Abp.IdentityServer;
using Volo.Abp.IdentityServer.Localization; using Volo.Abp.IdentityServer.Localization;
using Volo.Abp.Security.Claims; using Volo.Abp.Security.Claims;
using IdentityUser = Volo.Abp.Identity.IdentityUser;
using IdentityResource = Volo.Abp.Identity.Localization.IdentityResource; using IdentityResource = Volo.Abp.Identity.Localization.IdentityResource;
using IdentityUser = Volo.Abp.Identity.IdentityUser;
using IIdentityUserRepository = LINGYUN.Abp.Identity.IIdentityUserRepository;
namespace LINGYUN.Abp.IdentityServer.SmsValidator namespace LINGYUN.Abp.IdentityServer.SmsValidator
{ {
@ -25,6 +27,7 @@ namespace LINGYUN.Abp.IdentityServer.SmsValidator
protected IEventService EventService { get; } protected IEventService EventService { get; }
protected IIdentityUserRepository UserRepository { get; } protected IIdentityUserRepository UserRepository { get; }
protected UserManager<IdentityUser> UserManager { get; } protected UserManager<IdentityUser> UserManager { get; }
protected IdentitySecurityLogManager IdentitySecurityLogManager { get; }
protected IStringLocalizer<IdentityResource> IdentityLocalizer { get; } protected IStringLocalizer<IdentityResource> IdentityLocalizer { get; }
protected IStringLocalizer<AbpIdentityServerResource> IdentityServerLocalizer { get; } protected IStringLocalizer<AbpIdentityServerResource> IdentityServerLocalizer { get; }
@ -32,6 +35,7 @@ namespace LINGYUN.Abp.IdentityServer.SmsValidator
IEventService eventService, IEventService eventService,
UserManager<IdentityUser> userManager, UserManager<IdentityUser> userManager,
IIdentityUserRepository userRepository, IIdentityUserRepository userRepository,
IdentitySecurityLogManager identitySecurityLogManager,
IStringLocalizer<IdentityResource> identityLocalizer, IStringLocalizer<IdentityResource> identityLocalizer,
IStringLocalizer<AbpIdentityServerResource> identityServerLocalizer, IStringLocalizer<AbpIdentityServerResource> identityServerLocalizer,
ILogger<SmsTokenGrantValidator> logger) ILogger<SmsTokenGrantValidator> logger)
@ -40,6 +44,7 @@ namespace LINGYUN.Abp.IdentityServer.SmsValidator
EventService = eventService; EventService = eventService;
UserManager = userManager; UserManager = userManager;
UserRepository = userRepository; UserRepository = userRepository;
IdentitySecurityLogManager = identitySecurityLogManager;
IdentityLocalizer = identityLocalizer; IdentityLocalizer = identityLocalizer;
IdentityServerLocalizer = identityServerLocalizer; IdentityServerLocalizer = identityServerLocalizer;
} }
@ -76,6 +81,9 @@ namespace LINGYUN.Abp.IdentityServer.SmsValidator
{ {
Logger.LogInformation("Authentication failed for username: {username}, reason: locked out", currentUser.UserName); Logger.LogInformation("Authentication failed for username: {username}, reason: locked out", currentUser.UserName);
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, IdentityLocalizer["Volo.Abp.Identity:UserLockedOut"]); context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, IdentityLocalizer["Volo.Abp.Identity:UserLockedOut"]);
await SaveSecurityLogAsync(context, currentUser, IdentityServerSecurityLogActionConsts.LoginLockedout);
return; return;
} }
@ -97,22 +105,80 @@ namespace LINGYUN.Abp.IdentityServer.SmsValidator
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, userAccessFailedError); context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, userAccessFailedError);
await EventService.RaiseAsync(new UserLoginFailureEvent(currentUser.UserName, userAccessFailedError, false)); await EventService.RaiseAsync(new UserLoginFailureEvent(currentUser.UserName, userAccessFailedError, false));
} }
await SaveSecurityLogAsync(context, currentUser, SmsValidatorConsts.SecurityCodeFailed);
return; return;
} }
var sub = await UserManager.GetUserIdAsync(currentUser); await EventService.RaiseAsync(new UserLoginSuccessEvent(currentUser.UserName, phoneNumber, null));
// 登录之后需要更新安全令牌
(await UserManager.UpdateSecurityStampAsync(currentUser)).CheckErrors();
await SetSuccessResultAsync(context, currentUser);
}
protected virtual async Task SetSuccessResultAsync(ExtensionGrantValidationContext context, IdentityUser user)
{
var sub = await UserManager.GetUserIdAsync(user);
Logger.LogInformation("Credentials validated for username: {username}", user.UserName);
var additionalClaims = new List<Claim>(); var additionalClaims = new List<Claim>();
if (currentUser.TenantId.HasValue)
await AddCustomClaimsAsync(additionalClaims, user, context);
context.Result = new GrantValidationResult(
sub,
OidcConstants.AuthenticationMethods.ConfirmationBySms,
additionalClaims.ToArray()
);
await SaveSecurityLogAsync(
context,
user,
IdentityServerSecurityLogActionConsts.LoginSucceeded);
}
protected virtual async Task SaveSecurityLogAsync(
ExtensionGrantValidationContext context,
IdentityUser user,
string action)
{
var logContext = new IdentitySecurityLogContext
{ {
additionalClaims.Add(new Claim(AbpClaimTypes.TenantId, currentUser.TenantId?.ToString())); Identity = IdentityServerSecurityLogIdentityConsts.IdentityServer,
Action = action,
UserName = user.UserName,
ClientId = await FindClientIdAsync(context)
};
logContext.WithProperty("GrantType", GrantType);
await IdentitySecurityLogManager.SaveAsync(logContext);
} }
await EventService.RaiseAsync(new UserLoginSuccessEvent(currentUser.UserName, phoneNumber, null)); protected virtual Task<string> FindClientIdAsync(ExtensionGrantValidationContext context)
context.Result = new GrantValidationResult(sub, OidcConstants.AuthenticationMethods.ConfirmationBySms, additionalClaims.ToArray()); {
return Task.FromResult(context.Request?.Client?.ClientId);
}
// 登录之后需要更新安全令牌 protected virtual Task AddCustomClaimsAsync(
(await UserManager.UpdateSecurityStampAsync(currentUser)).CheckErrors(); List<Claim> customClaims,
IdentityUser user,
ExtensionGrantValidationContext context)
{
if (user.TenantId.HasValue)
{
customClaims.Add(
new Claim(
AbpClaimTypes.TenantId,
user.TenantId?.ToString()
)
);
}
return Task.CompletedTask;
} }
} }
} }

2
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.SmsValidator/LINGYUN/Abp/IdentityServer/SmsValidator/SmsValidatorConsts.cs

@ -9,5 +9,7 @@
public const string SmsValidatorTokenName = "phone_verify_code"; public const string SmsValidatorTokenName = "phone_verify_code";
public const string SmsValidatorPurpose = "phone_verify"; public const string SmsValidatorPurpose = "phone_verify";
public const string SecurityCodeFailed = "SecurityCodeFailed";
} }
} }

3
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/MiniProgram/WeChatMiniProgramGrantValidator.cs

@ -37,11 +37,12 @@ namespace LINGYUN.Abp.IdentityServer.WeChat.MiniProgram
IWeChatOpenIdFinder weChatOpenIdFinder, IWeChatOpenIdFinder weChatOpenIdFinder,
UserManager<IdentityUser> userManager, UserManager<IdentityUser> userManager,
IIdentityUserRepository userRepository, IIdentityUserRepository userRepository,
IdentitySecurityLogManager identitySecurityLogManager,
IStringLocalizer<Volo.Abp.Identity.Localization.IdentityResource> identityLocalizer, IStringLocalizer<Volo.Abp.Identity.Localization.IdentityResource> identityLocalizer,
IStringLocalizer<AbpIdentityServerResource> identityServerLocalizer, IStringLocalizer<AbpIdentityServerResource> identityServerLocalizer,
IStringLocalizer<WeChatResource> wechatLocalizer, IStringLocalizer<WeChatResource> wechatLocalizer,
AbpWeChatMiniProgramOptionsFactory miniProgramOptionsFactory) AbpWeChatMiniProgramOptionsFactory miniProgramOptionsFactory)
: base(eventService, weChatOpenIdFinder, userManager, userRepository, identityLocalizer, identityServerLocalizer) : base(eventService, weChatOpenIdFinder, userManager, userRepository, identitySecurityLogManager, identityLocalizer, identityServerLocalizer)
{ {
WeChatLocalizer = wechatLocalizer; WeChatLocalizer = wechatLocalizer;
MiniProgramOptionsFactory = miniProgramOptionsFactory; MiniProgramOptionsFactory = miniProgramOptionsFactory;

3
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/Official/WeChatOfficialGrantValidator.cs

@ -37,11 +37,12 @@ namespace LINGYUN.Abp.IdentityServer.WeChat.Official
IWeChatOpenIdFinder weChatOpenIdFinder, IWeChatOpenIdFinder weChatOpenIdFinder,
UserManager<IdentityUser> userManager, UserManager<IdentityUser> userManager,
IIdentityUserRepository userRepository, IIdentityUserRepository userRepository,
IdentitySecurityLogManager identitySecurityLogManager,
IStringLocalizer<Volo.Abp.Identity.Localization.IdentityResource> identityLocalizer, IStringLocalizer<Volo.Abp.Identity.Localization.IdentityResource> identityLocalizer,
IStringLocalizer<AbpIdentityServerResource> identityServerLocalizer, IStringLocalizer<AbpIdentityServerResource> identityServerLocalizer,
IStringLocalizer<WeChatResource> wechatLocalizer, IStringLocalizer<WeChatResource> wechatLocalizer,
AbpWeChatOfficialOptionsFactory weChatOfficialOptionsFactory) AbpWeChatOfficialOptionsFactory weChatOfficialOptionsFactory)
: base(eventService, weChatOpenIdFinder, userManager, userRepository, identityLocalizer, identityServerLocalizer) : base(eventService, weChatOpenIdFinder, userManager, userRepository, identitySecurityLogManager, identityLocalizer, identityServerLocalizer)
{ {
WeChatLocalizer = wechatLocalizer; WeChatLocalizer = wechatLocalizer;
WeChatOfficialOptionsFactory = weChatOfficialOptionsFactory; WeChatOfficialOptionsFactory = weChatOfficialOptionsFactory;

108
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/WeChatGrantValidator.cs

@ -1,5 +1,4 @@
 using IdentityModel;
using IdentityModel;
using IdentityServer4.Events; using IdentityServer4.Events;
using IdentityServer4.Models; using IdentityServer4.Models;
using IdentityServer4.Services; using IdentityServer4.Services;
@ -19,11 +18,13 @@ using System.Threading.Tasks;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.Guids; using Volo.Abp.Guids;
using Volo.Abp.Identity; using Volo.Abp.Identity;
using Volo.Abp.IdentityServer;
using Volo.Abp.IdentityServer.Localization; using Volo.Abp.IdentityServer.Localization;
using Volo.Abp.MultiTenancy; using Volo.Abp.MultiTenancy;
using Volo.Abp.Security.Claims; using Volo.Abp.Security.Claims;
using Volo.Abp.Settings; using Volo.Abp.Settings;
using Volo.Abp.Uow; using Volo.Abp.Uow;
using IdentityResource = Volo.Abp.Identity.Localization.IdentityResource; using IdentityResource = Volo.Abp.Identity.Localization.IdentityResource;
using IdentityUser = Volo.Abp.Identity.IdentityUser; using IdentityUser = Volo.Abp.Identity.IdentityUser;
@ -41,6 +42,7 @@ namespace LINGYUN.Abp.IdentityServer.WeChat
protected IEventService EventService { get; } protected IEventService EventService { get; }
protected IWeChatOpenIdFinder WeChatOpenIdFinder { get; } protected IWeChatOpenIdFinder WeChatOpenIdFinder { get; }
protected IIdentityUserRepository UserRepository { get; } protected IIdentityUserRepository UserRepository { get; }
protected IdentitySecurityLogManager IdentitySecurityLogManager { get; }
protected UserManager<IdentityUser> UserManager { get; } protected UserManager<IdentityUser> UserManager { get; }
protected IStringLocalizer<IdentityResource> IdentityLocalizer { get; } protected IStringLocalizer<IdentityResource> IdentityLocalizer { get; }
protected IStringLocalizer<AbpIdentityServerResource> IdentityServerLocalizer { get; } protected IStringLocalizer<AbpIdentityServerResource> IdentityServerLocalizer { get; }
@ -50,12 +52,14 @@ namespace LINGYUN.Abp.IdentityServer.WeChat
IWeChatOpenIdFinder weChatOpenIdFinder, IWeChatOpenIdFinder weChatOpenIdFinder,
UserManager<IdentityUser> userManager, UserManager<IdentityUser> userManager,
IIdentityUserRepository userRepository, IIdentityUserRepository userRepository,
IdentitySecurityLogManager identitySecurityLogManager,
IStringLocalizer<IdentityResource> identityLocalizer, IStringLocalizer<IdentityResource> identityLocalizer,
IStringLocalizer<AbpIdentityServerResource> identityServerLocalizer) IStringLocalizer<AbpIdentityServerResource> identityServerLocalizer)
{ {
EventService = eventService; EventService = eventService;
UserManager = userManager; UserManager = userManager;
UserRepository = userRepository; UserRepository = userRepository;
IdentitySecurityLogManager = identitySecurityLogManager;
WeChatOpenIdFinder = weChatOpenIdFinder; WeChatOpenIdFinder = weChatOpenIdFinder;
IdentityLocalizer = identityLocalizer; IdentityLocalizer = identityLocalizer;
IdentityServerLocalizer = identityServerLocalizer; IdentityServerLocalizer = identityServerLocalizer;
@ -81,7 +85,7 @@ namespace LINGYUN.Abp.IdentityServer.WeChat
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, IdentityServerLocalizer["InvalidGrant:GrantTypeInvalid"]); context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, IdentityServerLocalizer["InvalidGrant:GrantTypeInvalid"]);
return; return;
} }
// TODO: 统一命名规范, 微信认证传递的 code 改为 WeChatOpenIdConsts.WeCahtCodeKey
var wechatCode = raw.Get(AbpWeChatGlobalConsts.TokenName); var wechatCode = raw.Get(AbpWeChatGlobalConsts.TokenName);
if (wechatCode.IsNullOrWhiteSpace() || wechatCode.IsNullOrWhiteSpace()) if (wechatCode.IsNullOrWhiteSpace() || wechatCode.IsNullOrWhiteSpace())
{ {
@ -94,6 +98,7 @@ namespace LINGYUN.Abp.IdentityServer.WeChat
var currentUser = await UserManager.FindByLoginAsync(LoginProvider, wechatOpenId.OpenId); var currentUser = await UserManager.FindByLoginAsync(LoginProvider, wechatOpenId.OpenId);
if (currentUser == null) if (currentUser == null)
{ {
// 检查是否允许自注册
var settingProvider = ServiceProvider.LazyGetRequiredService<ISettingProvider>(); var settingProvider = ServiceProvider.LazyGetRequiredService<ISettingProvider>();
// TODO 检查启用用户注册是否有必要引用账户模块 // TODO 检查启用用户注册是否有必要引用账户模块
if (!await settingProvider.IsTrueAsync("Abp.Account.IsSelfRegistrationEnabled") || if (!await settingProvider.IsTrueAsync("Abp.Account.IsSelfRegistrationEnabled") ||
@ -117,37 +122,108 @@ namespace LINGYUN.Abp.IdentityServer.WeChat
AbpWeChatGlobalConsts.DisplayName))).CheckErrors(); AbpWeChatGlobalConsts.DisplayName))).CheckErrors();
} }
// 检查是否已锁定
if (await UserManager.IsLockedOutAsync(currentUser)) if (await UserManager.IsLockedOutAsync(currentUser))
{ {
Logger.LogInformation("Authentication failed for username: {username}, reason: locked out", currentUser.UserName); Logger.LogInformation("Authentication failed for username: {username}, reason: locked out", currentUser.UserName);
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, IdentityLocalizer["Volo.Abp.Identity:UserLockedOut"]); context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, IdentityLocalizer["Volo.Abp.Identity:UserLockedOut"]);
await SaveSecurityLogAsync(context, currentUser, wechatOpenId, IdentityServerSecurityLogActionConsts.LoginLockedout);
return; return;
} }
var sub = await UserManager.GetUserIdAsync(currentUser); await EventService.RaiseAsync(new UserLoginSuccessEvent(currentUser.UserName, wechatOpenId.OpenId, null));
// 登录之后需要更新安全令牌
(await UserManager.UpdateSecurityStampAsync(currentUser)).CheckErrors();
var additionalClaims = new List<Claim>(); await SetSuccessResultAsync(context, currentUser, wechatOpenId);
if (currentUser.TenantId.HasValue) }
protected virtual Task<bool> CheckFeatureAsync(ExtensionGrantValidationContext context)
{ {
additionalClaims.Add(new Claim(AbpClaimTypes.TenantId, currentUser.TenantId?.ToString())); return Task.FromResult(true);
} }
additionalClaims.Add(new Claim(AbpWeChatClaimTypes.OpenId, wechatOpenId.OpenId));
if (!wechatOpenId.UnionId.IsNullOrWhiteSpace()) protected virtual async Task SetSuccessResultAsync(ExtensionGrantValidationContext context, IdentityUser user, WeChatOpenId wechatOpenId)
{ {
additionalClaims.Add(new Claim(AbpWeChatClaimTypes.UnionId, wechatOpenId.UnionId)); var sub = await UserManager.GetUserIdAsync(user);
Logger.LogInformation("Credentials validated for username: {username}", user.UserName);
var additionalClaims = new List<Claim>();
await AddCustomClaimsAsync(additionalClaims, user, wechatOpenId, context);
context.Result = new GrantValidationResult(
sub,
AuthenticationMethod,
additionalClaims.ToArray()
);
await SaveSecurityLogAsync(
context,
user,
wechatOpenId,
IdentityServerSecurityLogActionConsts.LoginSucceeded);
} }
await EventService.RaiseAsync(new UserLoginSuccessEvent(currentUser.UserName, wechatOpenId.OpenId, null)); protected virtual async Task SaveSecurityLogAsync(
ExtensionGrantValidationContext context,
IdentityUser user,
WeChatOpenId wechatOpenId,
string action)
{
var logContext = new IdentitySecurityLogContext
{
Identity = IdentityServerSecurityLogIdentityConsts.IdentityServer,
Action = action,
UserName = user.UserName,
ClientId = await FindClientIdAsync(context)
};
logContext.WithProperty("GrantType", GrantType);
logContext.WithProperty("Provider", LoginProvider);
logContext.WithProperty("Method", AuthenticationMethod);
await IdentitySecurityLogManager.SaveAsync(logContext);
}
context.Result = new GrantValidationResult(sub, AuthenticationMethod, additionalClaims.ToArray()); protected virtual Task<string> FindClientIdAsync(ExtensionGrantValidationContext context)
{
return Task.FromResult(context.Request?.Client?.ClientId);
}
// 登录之后需要更新安全令牌 protected virtual Task AddCustomClaimsAsync(
(await UserManager.UpdateSecurityStampAsync(currentUser)).CheckErrors(); List<Claim> customClaims,
IdentityUser user,
WeChatOpenId wechatOpenId,
ExtensionGrantValidationContext context)
{
if (user.TenantId.HasValue)
{
customClaims.Add(
new Claim(
AbpClaimTypes.TenantId,
user.TenantId?.ToString()
)
);
} }
protected virtual Task<bool> CheckFeatureAsync(ExtensionGrantValidationContext context) customClaims.Add(
new Claim(
AbpWeChatClaimTypes.OpenId,
wechatOpenId.OpenId));
if (!wechatOpenId.UnionId.IsNullOrWhiteSpace())
{ {
return Task.FromResult(true); customClaims.Add(
new Claim(
AbpWeChatClaimTypes.UnionId,
wechatOpenId.UnionId));
}
return Task.CompletedTask;
} }
} }
} }

Loading…
Cancel
Save