Browse Source

Enhanced WeChat authentication

pull/140/head
cKey 5 years ago
parent
commit
65f78be5f9
  1. 13
      aspnet-core/LINGYUN.MicroService.IdentityServer.sln
  2. 1
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.SmsValidator/LINGYUN/Abp/IdentityServer/SmsValidator/SmsTokenGrantValidator.cs
  3. 101
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/MiniProgram/WeChatMiniProgramGrantValidator.cs
  4. 100
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/Official/WeChatOfficialGrantValidator.cs
  5. 116
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/WeChatGrantValidator.cs
  6. 10
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/Microsoft/AspNetCore/Authentication/WeChat/Official/WeChatOfficialOAuthHandler.cs
  7. 2
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/Microsoft/AspNetCore/Authentication/WeChat/Official/WeChatOfficialOAuthOptions.cs
  8. 2
      aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.MiniProgram/LINGYUN/Abp/WeChat/MiniProgram/AbpWeChatMiniProgramConsts.cs
  9. 2
      aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialConsts.cs
  10. 2
      aspnet-core/modules/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/AbpWeChatGlobalConsts.cs
  11. 11
      aspnet-core/modules/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/AbpWeChatModule.cs
  12. 2
      aspnet-core/modules/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/OpenId/WeChatOpenIdFinder.cs
  13. 2
      aspnet-core/modules/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/Token/WeChatTokenProvider.cs

13
aspnet-core/LINGYUN.MicroService.IdentityServer.sln

@ -47,11 +47,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Identity.Entity
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.IdentityServer.EntityFrameworkCore", "modules\identityServer\LINGYUN.Abp.IdentityServer.EntityFrameworkCore\LINGYUN.Abp.IdentityServer.EntityFrameworkCore.csproj", "{644FAB8F-ED83-4539-AF43-8F8FE61EF778}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.IdentityServer.EntityFrameworkCore", "modules\identityServer\LINGYUN.Abp.IdentityServer.EntityFrameworkCore\LINGYUN.Abp.IdentityServer.EntityFrameworkCore.csproj", "{644FAB8F-ED83-4539-AF43-8F8FE61EF778}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.WeChat.MiniProgram", "modules\wechat\LINGYUN.Abp.WeChat.MiniProgram\LINGYUN.Abp.WeChat.MiniProgram.csproj", "{648D7B5D-8B78-4F90-BDE0-4AC50C902CC3}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.WeChat.MiniProgram", "modules\wechat\LINGYUN.Abp.WeChat.MiniProgram\LINGYUN.Abp.WeChat.MiniProgram.csproj", "{648D7B5D-8B78-4F90-BDE0-4AC50C902CC3}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.WeChat.Official", "modules\wechat\LINGYUN.Abp.WeChat.Official\LINGYUN.Abp.WeChat.Official.csproj", "{F762D1BB-66FB-421A-A917-D80B7DCC0DC0}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.WeChat.Official", "modules\wechat\LINGYUN.Abp.WeChat.Official\LINGYUN.Abp.WeChat.Official.csproj", "{F762D1BB-66FB-421A-A917-D80B7DCC0DC0}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.IdentityServer.WeChat", "modules\identityServer\LINGYUN.Abp.IdentityServer.WeChat\LINGYUN.Abp.IdentityServer.WeChat.csproj", "{D63C0CA8-554D-43F2-8DB4-8B464E4C1CDA}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.IdentityServer.WeChat", "modules\identityServer\LINGYUN.Abp.IdentityServer.WeChat\LINGYUN.Abp.IdentityServer.WeChat.csproj", "{D63C0CA8-554D-43F2-8DB4-8B464E4C1CDA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.IdentityServer.Domain", "modules\identityServer\LINGYUN.Abp.IdentityServer.Domain\LINGYUN.Abp.IdentityServer.Domain.csproj", "{79CAF2CA-E9E1-48A5-A21F-3786383C12E0}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -127,6 +129,10 @@ Global
{D63C0CA8-554D-43F2-8DB4-8B464E4C1CDA}.Debug|Any CPU.Build.0 = Debug|Any CPU {D63C0CA8-554D-43F2-8DB4-8B464E4C1CDA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D63C0CA8-554D-43F2-8DB4-8B464E4C1CDA}.Release|Any CPU.ActiveCfg = Release|Any CPU {D63C0CA8-554D-43F2-8DB4-8B464E4C1CDA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D63C0CA8-554D-43F2-8DB4-8B464E4C1CDA}.Release|Any CPU.Build.0 = Release|Any CPU {D63C0CA8-554D-43F2-8DB4-8B464E4C1CDA}.Release|Any CPU.Build.0 = Release|Any CPU
{79CAF2CA-E9E1-48A5-A21F-3786383C12E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{79CAF2CA-E9E1-48A5-A21F-3786383C12E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{79CAF2CA-E9E1-48A5-A21F-3786383C12E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{79CAF2CA-E9E1-48A5-A21F-3786383C12E0}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -155,6 +161,7 @@ Global
{648D7B5D-8B78-4F90-BDE0-4AC50C902CC3} = {AF8AECC8-0F42-4FC5-B3C0-00987BA8279F} {648D7B5D-8B78-4F90-BDE0-4AC50C902CC3} = {AF8AECC8-0F42-4FC5-B3C0-00987BA8279F}
{F762D1BB-66FB-421A-A917-D80B7DCC0DC0} = {AF8AECC8-0F42-4FC5-B3C0-00987BA8279F} {F762D1BB-66FB-421A-A917-D80B7DCC0DC0} = {AF8AECC8-0F42-4FC5-B3C0-00987BA8279F}
{D63C0CA8-554D-43F2-8DB4-8B464E4C1CDA} = {98887A8F-7040-4FA1-842F-A4C77A61ED09} {D63C0CA8-554D-43F2-8DB4-8B464E4C1CDA} = {98887A8F-7040-4FA1-842F-A4C77A61ED09}
{79CAF2CA-E9E1-48A5-A21F-3786383C12E0} = {98887A8F-7040-4FA1-842F-A4C77A61ED09}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {FCB77471-9ECB-4666-A316-1D6A6285A468} SolutionGuid = {FCB77471-9ECB-4666-A316-1D6A6285A468}

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

@ -15,6 +15,7 @@ using System.Threading.Tasks;
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 IdentityUser = Volo.Abp.Identity.IdentityUser;
using IdentityResource = Volo.Abp.Identity.Localization.IdentityResource;
namespace LINGYUN.Abp.IdentityServer.SmsValidator namespace LINGYUN.Abp.IdentityServer.SmsValidator
{ {

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

@ -1,24 +1,12 @@
using IdentityModel; using IdentityServer4.Services;
using IdentityServer4.Events;
using IdentityServer4.Models;
using IdentityServer4.Services;
using IdentityServer4.Validation;
using LINGYUN.Abp.WeChat;
using LINGYUN.Abp.WeChat.MiniProgram; using LINGYUN.Abp.WeChat.MiniProgram;
using LINGYUN.Abp.WeChat.OpenId; using LINGYUN.Abp.WeChat.OpenId;
using LINGYUN.Abp.WeChat.Security.Claims;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Security.Claims;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.Identity; using Volo.Abp.Identity;
using Volo.Abp.IdentityServer.Localization; using Volo.Abp.IdentityServer.Localization;
using Volo.Abp.Security.Claims;
using IdentityUser = Volo.Abp.Identity.IdentityUser; using IdentityUser = Volo.Abp.Identity.IdentityUser;
namespace LINGYUN.Abp.IdentityServer.WeChat.MiniProgram namespace LINGYUN.Abp.IdentityServer.WeChat.MiniProgram
@ -26,93 +14,32 @@ namespace LINGYUN.Abp.IdentityServer.WeChat.MiniProgram
/// <summary> /// <summary>
/// 对于小程序绑定用户的扩展授权验证器 /// 对于小程序绑定用户的扩展授权验证器
/// </summary> /// </summary>
public class WeChatMiniProgramGrantValidator : IExtensionGrantValidator public class WeChatMiniProgramGrantValidator : WeChatGrantValidator
{ {
protected ILogger<WeChatMiniProgramGrantValidator> Logger { get; } public override string GrantType => AbpWeChatMiniProgramConsts.GrantType;
protected AbpWeChatMiniProgramOptions Options { get; }
protected IHttpClientFactory HttpClientFactory{ get; } public override string LoginProviderKey => AbpWeChatMiniProgramConsts.ProviderKey;
protected IEventService EventService { get; }
protected IWeChatOpenIdFinder WeChatOpenIdFinder { get; }
protected IIdentityUserRepository UserRepository { get; }
protected UserManager<IdentityUser> UserManager { get; }
protected SignInManager<IdentityUser> SignInManager { get; }
protected IStringLocalizer<AbpIdentityServerResource> Localizer { get; }
protected PhoneNumberTokenProvider<IdentityUser> PhoneNumberTokenProvider { get; }
public override string AuthenticationMethod => AbpWeChatMiniProgramConsts.AuthenticationMethod;
protected AbpWeChatMiniProgramOptions Options { get; }
public WeChatMiniProgramGrantValidator( public WeChatMiniProgramGrantValidator(
IEventService eventService, IEventService eventService,
IWeChatOpenIdFinder weChatOpenIdFinder, IWeChatOpenIdFinder weChatOpenIdFinder,
IHttpClientFactory httpClientFactory,
UserManager<IdentityUser> userManager, UserManager<IdentityUser> userManager,
IIdentityUserRepository userRepository, IIdentityUserRepository userRepository,
SignInManager<IdentityUser> signInManager, IStringLocalizer<Volo.Abp.Identity.Localization.IdentityResource> identityLocalizer,
IStringLocalizer<AbpIdentityServerResource> stringLocalizer, IStringLocalizer<AbpIdentityServerResource> identityServerLocalizer,
PhoneNumberTokenProvider<IdentityUser> phoneNumberTokenProvider, IOptions<AbpWeChatMiniProgramOptions> options)
IOptions<AbpWeChatMiniProgramOptions> options, : base(eventService, weChatOpenIdFinder, userManager, userRepository, identityLocalizer, identityServerLocalizer)
ILogger<WeChatMiniProgramGrantValidator> logger)
{ {
Logger = logger;
Options = options.Value; Options = options.Value;
EventService = eventService;
UserManager = userManager;
SignInManager = signInManager;
Localizer = stringLocalizer;
UserRepository = userRepository;
WeChatOpenIdFinder = weChatOpenIdFinder;
HttpClientFactory = httpClientFactory;
PhoneNumberTokenProvider = phoneNumberTokenProvider;
} }
public string GrantType => AbpWeChatMiniProgramConsts.GrantType; protected override async Task<WeChatOpenId> FindOpenIdAsync(string code)
public async Task ValidateAsync(ExtensionGrantValidationContext context)
{ {
var raw = context.Request.Raw; return await WeChatOpenIdFinder.FindAsync(code, Options.AppId, Options.AppSecret);
var credential = raw.Get(OidcConstants.TokenRequest.GrantType);
if (credential == null || !credential.Equals(GrantType))
{
Logger.LogWarning("Invalid grant type: not allowed");
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant,
Localizer["InvalidGrant:GrantTypeInvalid"]);
return;
}
// TODO: 统一命名规范, 微信认证传递的 code 改为 WeChatOpenIdConsts.WeCahtCodeKey
var wechatCode = raw.Get(AbpWeChatGlobalConsts.TokenName);
if (wechatCode.IsNullOrWhiteSpace() || wechatCode.IsNullOrWhiteSpace())
{
Logger.LogWarning("Invalid grant type: wechat code not found");
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant,
Localizer["InvalidGrant:WeChatCodeNotFound"]);
return;
}
var wechatOpenId = await WeChatOpenIdFinder.FindAsync(wechatCode, Options.AppId, Options.AppSecret);
// 如果存在 UnionId优先使用
var currentUser = await UserManager.FindByLoginAsync(AbpWeChatMiniProgramConsts.ProviderKey, wechatOpenId.UnionId ?? wechatOpenId.OpenId);
if(currentUser == null)
{
Logger.LogWarning("Invalid grant type: wechat openid: {0} not register", wechatOpenId.OpenId);
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant,
Localizer["InvalidGrant:WeChatNotRegister"]);
return;
}
var sub = await UserManager.GetUserIdAsync(currentUser);
var additionalClaims = new List<Claim>();
if (currentUser.TenantId.HasValue)
{
additionalClaims.Add(new Claim(AbpClaimTypes.TenantId, currentUser.TenantId?.ToString()));
}
additionalClaims.Add(new Claim(AbpWeChatClaimTypes.OpenId, wechatOpenId.OpenId));
if (!wechatOpenId.UnionId.IsNullOrWhiteSpace())
{
additionalClaims.Add(new Claim(AbpWeChatClaimTypes.UnionId, wechatOpenId.UnionId));
}
await EventService.RaiseAsync(new UserLoginSuccessEvent(currentUser.UserName, wechatOpenId.OpenId, null));
context.Result = new GrantValidationResult(sub,
AbpWeChatMiniProgramConsts.AuthenticationMethod, additionalClaims.ToArray());
} }
} }
} }

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

@ -1,24 +1,12 @@
using IdentityModel; using IdentityServer4.Services;
using IdentityServer4.Events;
using IdentityServer4.Models;
using IdentityServer4.Services;
using IdentityServer4.Validation;
using LINGYUN.Abp.WeChat;
using LINGYUN.Abp.WeChat.Official; using LINGYUN.Abp.WeChat.Official;
using LINGYUN.Abp.WeChat.OpenId; using LINGYUN.Abp.WeChat.OpenId;
using LINGYUN.Abp.WeChat.Security.Claims;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Security.Claims;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.Identity; using Volo.Abp.Identity;
using Volo.Abp.IdentityServer.Localization; using Volo.Abp.IdentityServer.Localization;
using Volo.Abp.Security.Claims;
using IdentityUser = Volo.Abp.Identity.IdentityUser; using IdentityUser = Volo.Abp.Identity.IdentityUser;
namespace LINGYUN.Abp.IdentityServer.WeChat.Official namespace LINGYUN.Abp.IdentityServer.WeChat.Official
@ -26,92 +14,32 @@ namespace LINGYUN.Abp.IdentityServer.WeChat.Official
/// <summary> /// <summary>
/// 对于公众平台绑定用户的扩展授权验证器 /// 对于公众平台绑定用户的扩展授权验证器
/// </summary> /// </summary>
public class WeChatOfficialGrantValidator : IExtensionGrantValidator public class WeChatOfficialGrantValidator : WeChatGrantValidator
{ {
protected ILogger<WeChatOfficialGrantValidator> Logger { get; } public override string GrantType => AbpWeChatOfficialConsts.GrantType;
protected AbpWeChatOfficialOptions Options { get; }
protected IHttpClientFactory HttpClientFactory { get; } public override string LoginProviderKey => AbpWeChatOfficialConsts.ProviderKey;
protected IEventService EventService { get; }
protected IWeChatOpenIdFinder WeChatOpenIdFinder { get; }
protected IIdentityUserRepository UserRepository { get; }
protected UserManager<IdentityUser> UserManager { get; }
protected SignInManager<IdentityUser> SignInManager { get; }
protected IStringLocalizer<AbpIdentityServerResource> Localizer { get; }
protected PhoneNumberTokenProvider<IdentityUser> PhoneNumberTokenProvider { get; }
public override string AuthenticationMethod => AbpWeChatOfficialConsts.AuthenticationMethod;
protected AbpWeChatOfficialOptions Options { get; }
public WeChatOfficialGrantValidator( public WeChatOfficialGrantValidator(
IEventService eventService, IEventService eventService,
IWeChatOpenIdFinder weChatOpenIdFinder, IWeChatOpenIdFinder weChatOpenIdFinder,
IHttpClientFactory httpClientFactory,
UserManager<IdentityUser> userManager, UserManager<IdentityUser> userManager,
IIdentityUserRepository userRepository, IIdentityUserRepository userRepository,
SignInManager<IdentityUser> signInManager, IStringLocalizer<Volo.Abp.Identity.Localization.IdentityResource> identityLocalizer,
IStringLocalizer<AbpIdentityServerResource> stringLocalizer, IStringLocalizer<AbpIdentityServerResource> identityServerLocalizer,
PhoneNumberTokenProvider<IdentityUser> phoneNumberTokenProvider, IOptions<AbpWeChatOfficialOptions> options)
IOptions<AbpWeChatOfficialOptions> options, : base(eventService, weChatOpenIdFinder, userManager, userRepository, identityLocalizer, identityServerLocalizer)
ILogger<WeChatOfficialGrantValidator> logger)
{ {
Logger = logger;
Options = options.Value; Options = options.Value;
EventService = eventService;
UserManager = userManager;
SignInManager = signInManager;
Localizer = stringLocalizer;
UserRepository = userRepository;
WeChatOpenIdFinder = weChatOpenIdFinder;
HttpClientFactory = httpClientFactory;
PhoneNumberTokenProvider = phoneNumberTokenProvider;
} }
public string GrantType => AbpWeChatOfficialConsts.GrantType; protected override async Task<WeChatOpenId> FindOpenIdAsync(string code)
public async Task ValidateAsync(ExtensionGrantValidationContext context)
{ {
var raw = context.Request.Raw; return await WeChatOpenIdFinder.FindAsync(code, Options.AppId, Options.AppSecret);
var credential = raw.Get(OidcConstants.TokenRequest.GrantType);
if (credential == null || !credential.Equals(GrantType))
{
Logger.LogWarning("Invalid grant type: not allowed");
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant,
Localizer["InvalidGrant:GrantTypeInvalid"]);
return;
}
// TODO: 统一命名规范, 微信认证传递的 code 改为 WeChatOpenIdConsts.WeCahtCodeKey
var wechatCode = raw.Get(AbpWeChatGlobalConsts.TokenName);
if (wechatCode.IsNullOrWhiteSpace() || wechatCode.IsNullOrWhiteSpace())
{
Logger.LogWarning("Invalid grant type: wechat code not found");
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant,
Localizer["InvalidGrant:WeChatCodeNotFound"]);
return;
}
var wechatOpenId = await WeChatOpenIdFinder.FindAsync(wechatCode, Options.AppId, Options.AppSecret);
var currentUser = await UserManager.FindByLoginAsync(AbpWeChatOfficialConsts.ProviderKey, wechatOpenId.OpenId);
if (currentUser == null)
{
Logger.LogWarning("Invalid grant type: wechat openid: {0} not register", wechatOpenId.OpenId);
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant,
Localizer["InvalidGrant:WeChatNotRegister"]);
return;
}
var sub = await UserManager.GetUserIdAsync(currentUser);
var additionalClaims = new List<Claim>();
if (currentUser.TenantId.HasValue)
{
additionalClaims.Add(new Claim(AbpClaimTypes.TenantId, currentUser.TenantId?.ToString()));
}
additionalClaims.Add(new Claim(AbpWeChatClaimTypes.OpenId, wechatOpenId.OpenId));
if (!wechatOpenId.UnionId.IsNullOrWhiteSpace())
{
additionalClaims.Add(new Claim(AbpWeChatClaimTypes.UnionId, wechatOpenId.UnionId));
}
await EventService.RaiseAsync(new UserLoginSuccessEvent(currentUser.UserName, wechatOpenId.OpenId, null));
context.Result = new GrantValidationResult(sub,
AbpWeChatOfficialConsts.AuthenticationMethod, additionalClaims.ToArray());
} }
} }
} }

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

@ -0,0 +1,116 @@

using IdentityModel;
using IdentityServer4.Events;
using IdentityServer4.Models;
using IdentityServer4.Services;
using IdentityServer4.Validation;
using LINGYUN.Abp.WeChat;
using LINGYUN.Abp.WeChat.OpenId;
using LINGYUN.Abp.WeChat.Security.Claims;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using System;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;
using Volo.Abp.Identity;
using Volo.Abp.IdentityServer.Localization;
using Volo.Abp.Security.Claims;
using IdentityResource = Volo.Abp.Identity.Localization.IdentityResource;
using IdentityUser = Volo.Abp.Identity.IdentityUser;
namespace LINGYUN.Abp.IdentityServer.WeChat
{
public abstract class WeChatGrantValidator : IExtensionGrantValidator
{
public abstract string GrantType { get; }
public abstract string LoginProviderKey { get; }
public abstract string AuthenticationMethod { get; }
public ILogger Logger { protected get; set; }
protected IEventService EventService { get; }
protected IWeChatOpenIdFinder WeChatOpenIdFinder { get; }
protected IIdentityUserRepository UserRepository { get; }
protected UserManager<IdentityUser> UserManager { get; }
protected IStringLocalizer<IdentityResource> IdentityLocalizer { get; }
protected IStringLocalizer<AbpIdentityServerResource> IdentityServerLocalizer { get; }
public WeChatGrantValidator(
IEventService eventService,
IWeChatOpenIdFinder weChatOpenIdFinder,
UserManager<IdentityUser> userManager,
IIdentityUserRepository userRepository,
IStringLocalizer<IdentityResource> identityLocalizer,
IStringLocalizer<AbpIdentityServerResource> identityServerLocalizer)
{
EventService = eventService;
UserManager = userManager;
UserRepository = userRepository;
WeChatOpenIdFinder = weChatOpenIdFinder;
IdentityLocalizer = identityLocalizer;
IdentityServerLocalizer = identityServerLocalizer;
Logger = NullLogger<WeChatGrantValidator>.Instance;
}
protected abstract Task<WeChatOpenId> FindOpenIdAsync(string code);
public async Task ValidateAsync(ExtensionGrantValidationContext context)
{
var raw = context.Request.Raw;
var credential = raw.Get(OidcConstants.TokenRequest.GrantType);
if (credential == null || !credential.Equals(GrantType))
{
Logger.LogWarning("Invalid grant type: not allowed");
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, IdentityServerLocalizer["InvalidGrant:GrantTypeInvalid"]);
return;
}
// TODO: 统一命名规范, 微信认证传递的 code 改为 WeChatOpenIdConsts.WeCahtCodeKey
var wechatCode = raw.Get(AbpWeChatGlobalConsts.TokenName);
if (wechatCode.IsNullOrWhiteSpace() || wechatCode.IsNullOrWhiteSpace())
{
Logger.LogWarning("Invalid grant type: wechat code not found");
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, IdentityServerLocalizer["InvalidGrant:WeChatCodeNotFound"]);
return;
}
var wechatOpenId = await FindOpenIdAsync(wechatCode);
var currentUser = await UserManager.FindByLoginAsync(LoginProviderKey, wechatOpenId.OpenId);
if (currentUser == null)
{
Logger.LogWarning("Invalid grant type: wechat openid not register", wechatOpenId.OpenId);
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, IdentityServerLocalizer["InvalidGrant:WeChatNotRegister"]);
return;
}
if (await UserManager.IsLockedOutAsync(currentUser))
{
Logger.LogInformation("Authentication failed for username: {username}, reason: locked out", currentUser.UserName);
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, IdentityLocalizer["Volo.Abp.Identity:UserLockedOut"]);
return;
}
var sub = await UserManager.GetUserIdAsync(currentUser);
var additionalClaims = new List<Claim>();
if (currentUser.TenantId.HasValue)
{
additionalClaims.Add(new Claim(AbpClaimTypes.TenantId, currentUser.TenantId?.ToString()));
}
additionalClaims.Add(new Claim(AbpWeChatClaimTypes.OpenId, wechatOpenId.OpenId));
if (!wechatOpenId.UnionId.IsNullOrWhiteSpace())
{
additionalClaims.Add(new Claim(AbpWeChatClaimTypes.UnionId, wechatOpenId.UnionId));
}
await EventService.RaiseAsync(new UserLoginSuccessEvent(currentUser.UserName, wechatOpenId.OpenId, null));
context.Result = new GrantValidationResult(sub, AuthenticationMethod, additionalClaims.ToArray());
// 登录之后需要更新安全令牌
(await UserManager.UpdateSecurityStampAsync(currentUser)).CheckErrors();
}
}
}

10
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/Microsoft/AspNetCore/Authentication/WeChat/Official/WeChatOfficialOAuthHandler.cs

@ -88,7 +88,7 @@ namespace Microsoft.AspNetCore.Authentication.WeChat.Official
// TODO: 此处通过唯一的 CorrelationId, 将 properties生成的State缓存删除 // TODO: 此处通过唯一的 CorrelationId, 将 properties生成的State缓存删除
var state = Request.Query["state"]; var state = Request.Query["state"];
var stateCacheKey = WeChatOfficialStateCacheItem.CalculateCacheKey(state, null); var stateCacheKey = WeChatOfficialStateCacheItem.CalculateCacheKey(state.ToString().ToMd5(), null);
await Cache.RemoveAsync(stateCacheKey, token: Context.RequestAborted); await Cache.RemoveAsync(stateCacheKey, token: Context.RequestAborted);
return new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name); return new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name);
@ -138,10 +138,12 @@ namespace Microsoft.AspNetCore.Authentication.WeChat.Official
await base.HandleChallengeAsync(properties); await base.HandleChallengeAsync(properties);
// TODO: 此处已经生成唯一的 CorrelationId, 可以借此将 properties生成State之后再进行缓存 // TODO: 此处已经生成唯一的 CorrelationId, 可以借此将 properties生成State之后再进行缓存
// 注: 默认的State对于微信来说太长(微信只支持128位长度的State),因此巧妙的利用CorrelationId的MD5值来替代State
// MD5转换防止直接通过CorrelationId干些别的事情...
var state = properties.Items[".xsrf"]; var state = properties.Items[".xsrf"];
var stateToken = Options.StateDataFormat.Protect(properties); var stateToken = Options.StateDataFormat.Protect(properties);
var stateCacheKey = WeChatOfficialStateCacheItem.CalculateCacheKey(state, null); var stateCacheKey = WeChatOfficialStateCacheItem.CalculateCacheKey(state.ToMd5(), null);
await Cache await Cache
.SetAsync( .SetAsync(
@ -177,7 +179,7 @@ namespace Microsoft.AspNetCore.Authentication.WeChat.Official
["response_type"] = "code" ["response_type"] = "code"
}); });
challengeUrl += $"&scope={scope}&state={state}"; challengeUrl += $"&scope={scope}&state={state.ToMd5()}";
return challengeUrl; return challengeUrl;
} }
@ -189,7 +191,7 @@ namespace Microsoft.AspNetCore.Authentication.WeChat.Official
// TODO: 此处借用唯一的 CorrelationId, 将 properties生成的State缓存取出,进行解密 // TODO: 此处借用唯一的 CorrelationId, 将 properties生成的State缓存取出,进行解密
var state = query["state"]; var state = query["state"];
var stateCacheKey = WeChatOfficialStateCacheItem.CalculateCacheKey(state, null); var stateCacheKey = WeChatOfficialStateCacheItem.CalculateCacheKey(state.ToString().ToMd5(), null);
var stateCacheItem = await Cache.GetAsync(stateCacheKey, token: Context.RequestAborted); var stateCacheItem = await Cache.GetAsync(stateCacheKey, token: Context.RequestAborted);
var properties = Options.StateDataFormat.Unprotect(stateCacheItem.State); var properties = Options.StateDataFormat.Unprotect(stateCacheItem.State);

2
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/Microsoft/AspNetCore/Authentication/WeChat/Official/WeChatOfficialOAuthOptions.cs

@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.Authentication.WeChat.Official
{ {
public WeChatOfficialOAuthOptions() public WeChatOfficialOAuthOptions()
{ {
// 用于防止初始化错误 // 用于防止初始化错误,会在OAuthHandler.InitializeHandlerAsync中进行重写
ClientId = "WeChatOfficial"; ClientId = "WeChatOfficial";
ClientSecret = "WeChatOfficial"; ClientSecret = "WeChatOfficial";

2
aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.MiniProgram/LINGYUN/Abp/WeChat/MiniProgram/AbpWeChatMiniProgramConsts.cs

@ -3,7 +3,7 @@
public class AbpWeChatMiniProgramConsts public class AbpWeChatMiniProgramConsts
{ {
/// <summary> /// <summary>
/// 全局对应的Provider名称 /// 微信小程序对应的Provider名称
/// </summary> /// </summary>
public static string ProviderKey { get; set; } = "WeChat.MiniProgram"; public static string ProviderKey { get; set; } = "WeChat.MiniProgram";

2
aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialConsts.cs

@ -3,7 +3,7 @@
public class AbpWeChatOfficialConsts public class AbpWeChatOfficialConsts
{ {
/// <summary> /// <summary>
/// 微信公众号全局对应的Provider名称 /// 微信公众号对应的Provider名称
/// </summary> /// </summary>
public static string ProviderKey { get; set; } = "WeChat.Official"; public static string ProviderKey { get; set; } = "WeChat.Official";

2
aspnet-core/modules/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/AbpWeChatGlobalConsts.cs

@ -14,5 +14,7 @@
/// 微信授权显示名称 /// 微信授权显示名称
/// </summary> /// </summary>
public static string DisplayName { get; set; } = "WeChat"; public static string DisplayName { get; set; } = "WeChat";
public static string HttpClient { get; set; } = "Abp.WeChat";
} }
} }

11
aspnet-core/modules/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/AbpWeChatModule.cs

@ -31,11 +31,12 @@ namespace LINGYUN.Abp.WeChat
.AddVirtualJson("/LINGYUN/Abp/WeChat/Localization/Resources"); .AddVirtualJson("/LINGYUN/Abp/WeChat/Localization/Resources");
}); });
context.Services.AddHttpClient("WeChatRequestClient", options => context.Services.AddHttpClient(AbpWeChatGlobalConsts.HttpClient,
{ options =>
options.BaseAddress = new Uri("https://api.weixin.qq.com"); {
}).AddTransientHttpErrorPolicy(builder => options.BaseAddress = new Uri("https://api.weixin.qq.com");
builder.WaitAndRetryAsync(3, i => TimeSpan.FromSeconds(Math.Pow(2, i)))); }).AddTransientHttpErrorPolicy(builder =>
builder.WaitAndRetryAsync(3, i => TimeSpan.FromSeconds(Math.Pow(2, i))));
} }
} }
} }

2
aspnet-core/modules/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/OpenId/WeChatOpenIdFinder.cs

@ -58,7 +58,7 @@ namespace LINGYUN.Abp.WeChat.OpenId
Logger.LogDebug($"Not found in the cache, getting from the httpClient: {cacheKey}"); Logger.LogDebug($"Not found in the cache, getting from the httpClient: {cacheKey}");
var client = HttpClientFactory.CreateClient("WeChatRequestClient"); var client = HttpClientFactory.CreateClient(AbpWeChatGlobalConsts.HttpClient);
var request = new WeChatOpenIdRequest var request = new WeChatOpenIdRequest
{ {

2
aspnet-core/modules/wechat/LINGYUN.Abp.WeChat/LINGYUN/Abp/WeChat/Token/WeChatTokenProvider.cs

@ -58,7 +58,7 @@ namespace LINGYUN.Abp.WeChat.Token
Logger.LogDebug($"Not found in the cache, getting from the httpClient: {cacheKey}"); Logger.LogDebug($"Not found in the cache, getting from the httpClient: {cacheKey}");
var client = HttpClientFactory.CreateClient("WeChatRequestClient"); var client = HttpClientFactory.CreateClient(AbpWeChatGlobalConsts.HttpClient);
var request = new WeChatTokenRequest var request = new WeChatTokenRequest
{ {

Loading…
Cancel
Save