From f04afcd98200932d1e57d3fe46d6c6c87625c290 Mon Sep 17 00:00:00 2001 From: cKey <35512826+colinin@users.noreply.github.com> Date: Sun, 21 Jun 2020 11:55:10 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=BE=AE=E4=BF=A1=E6=B3=A8?= =?UTF-8?q?=E5=86=8C=E6=8E=A5=E5=8F=A3,=E9=80=9A=E8=BF=87=E5=BE=AE?= =?UTF-8?q?=E4=BF=A1=E6=B3=A8=E5=86=8C=E7=9A=84=E7=94=A8=E6=88=B7=E5=86=99?= =?UTF-8?q?=E5=85=A5=E5=88=B0UserLogin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...VerifyDto.cs => PhoneNumberRegisterDto.cs} | 2 +- .../Abp/Account/Dto/WeChatRegisterDto.cs | 28 ++++++ .../LINGYUN/Abp/Account/IAccountAppService.cs | 4 +- .../LINGYUN.Abp.Account.Application.csproj | 1 + .../Account/AbpAccountApplicationModule.cs | 6 +- .../LINGYUN/Abp/Account/AccountAppService.cs | 35 ++++++- .../Account/Localization/Resources/en.json | 3 +- .../Localization/Resources/zh-Hans.json | 3 +- .../LINGYUN/Abp/Account/AccountController.cs | 17 +++- .../LINGYUN.Abp.Aliyun.Authorization.csproj | 7 ++ .../Aliyun/AbpBlobStoringAliyunModule.cs | 2 +- .../AbpIdentityServerWeChatValidatorModule.cs | 5 - .../WeChatTokenGrantValidator.cs | 30 ++---- .../Http/HttpClientTokenRequestExtensions.cs | 32 ------ .../System/Net/Http/WeChatOpenIdResponse.cs | 48 --------- .../AbpWeChatAuthorizationModule.cs | 4 +- .../OpenId/IWeChatOpenIdFinder.cs | 9 ++ .../Authorization/OpenId/WeChatOpenId.cs | 30 ++++++ .../OpenId/WeChatOpenIdCacheItem.cs | 28 ++++++ .../OpenId/WeChatOpenIdFinder.cs | 97 +++++++++++++++++++ .../OpenId}/WeChatOpenIdRequest.cs | 2 +- .../OpenId/WeChatOpenIdResponse.cs | 63 ++++++++++++ .../{ => Token}/IWeChatTokenProvider.cs | 0 .../Authorization/{ => Token}/WeChatToken.cs | 0 .../{ => Token}/WeChatTokenCacheItem.cs | 0 .../{ => Token}/WeChatTokenProvider.cs | 2 +- .../{ => Token}/WeChatTokenRequest.cs | 0 .../{ => Token}/WeChatTokenResponse.cs | 0 .../HttpClientWeChatTokenRequestExtensions.cs | 19 ++++ 29 files changed, 350 insertions(+), 127 deletions(-) rename aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/Dto/{RegisterVerifyDto.cs => PhoneNumberRegisterDto.cs} (95%) create mode 100644 aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/Dto/WeChatRegisterDto.cs delete mode 100644 aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/System/Net/Http/HttpClientTokenRequestExtensions.cs delete mode 100644 aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/System/Net/Http/WeChatOpenIdResponse.cs create mode 100644 aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/OpenId/IWeChatOpenIdFinder.cs create mode 100644 aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/OpenId/WeChatOpenId.cs create mode 100644 aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/OpenId/WeChatOpenIdCacheItem.cs create mode 100644 aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/OpenId/WeChatOpenIdFinder.cs rename aspnet-core/modules/common/{LINGYUN.Abp.IdentityServer.WeChatValidator/System/Net/Http => LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/OpenId}/WeChatOpenIdRequest.cs (82%) create mode 100644 aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/OpenId/WeChatOpenIdResponse.cs rename aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/{ => Token}/IWeChatTokenProvider.cs (100%) rename aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/{ => Token}/WeChatToken.cs (100%) rename aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/{ => Token}/WeChatTokenCacheItem.cs (100%) rename aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/{ => Token}/WeChatTokenProvider.cs (97%) rename aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/{ => Token}/WeChatTokenRequest.cs (100%) rename aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/{ => Token}/WeChatTokenResponse.cs (100%) diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/Dto/RegisterVerifyDto.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/Dto/PhoneNumberRegisterDto.cs similarity index 95% rename from aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/Dto/RegisterVerifyDto.cs rename to aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/Dto/PhoneNumberRegisterDto.cs index 5de7157a2..cc12568bf 100644 --- a/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/Dto/RegisterVerifyDto.cs +++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/Dto/PhoneNumberRegisterDto.cs @@ -4,7 +4,7 @@ using Volo.Abp.Identity; namespace LINGYUN.Abp.Account { - public class RegisterVerifyDto + public class PhoneNumberRegisterDto { [Required] [Phone] diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/Dto/WeChatRegisterDto.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/Dto/WeChatRegisterDto.cs new file mode 100644 index 000000000..a0863e0d4 --- /dev/null +++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/Dto/WeChatRegisterDto.cs @@ -0,0 +1,28 @@ +using System.ComponentModel.DataAnnotations; +using Volo.Abp.Auditing; +using Volo.Abp.Identity; + +namespace LINGYUN.Abp.Account +{ + public class WeChatRegisterDto + { + [Required] + public string Code { get; set; } + + [DisableAuditing] + [DataType(DataType.Password)] + [Required] + [StringLength(IdentityUserConsts.MaxPasswordLength)] + public string Password { get; set; } + + [StringLength(IdentityUserConsts.MaxNameLength)] + public string Name { get; set; } + + [StringLength(IdentityUserConsts.MaxUserNameLength)] + public string UserName { get; set; } + + [EmailAddress] + [StringLength(IdentityUserConsts.MaxEmailLength)] + public string EmailAddress { get; set; } + } +} diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/IAccountAppService.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/IAccountAppService.cs index eee5ab984..d0aa0cd5d 100644 --- a/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/IAccountAppService.cs +++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/IAccountAppService.cs @@ -6,7 +6,9 @@ namespace LINGYUN.Abp.Account { public interface IAccountAppService : IApplicationService { - Task RegisterAsync(RegisterVerifyDto input); + Task RegisterAsync(PhoneNumberRegisterDto input); + + Task RegisterAsync(WeChatRegisterDto input); Task ResetPasswordAsync(PasswordResetDto passwordReset); diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN.Abp.Account.Application.csproj b/aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN.Abp.Account.Application.csproj index a10c5a38b..a9070efaf 100644 --- a/aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN.Abp.Account.Application.csproj +++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN.Abp.Account.Application.csproj @@ -11,6 +11,7 @@ + diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN/Abp/Account/AbpAccountApplicationModule.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN/Abp/Account/AbpAccountApplicationModule.cs index a0c4d6f9f..c765eb16e 100644 --- a/aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN/Abp/Account/AbpAccountApplicationModule.cs +++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN/Abp/Account/AbpAccountApplicationModule.cs @@ -1,11 +1,13 @@ -using Volo.Abp.Modularity; +using LINGYUN.Abp.WeChat.Authorization; +using Volo.Abp.Modularity; namespace LINGYUN.Abp.Account { [DependsOn( typeof(AbpAccountDomainModule), typeof(Volo.Abp.Account.AbpAccountApplicationModule), - typeof(AbpAccountApplicationContractsModule))] + typeof(AbpAccountApplicationContractsModule), + typeof(AbpWeChatAuthorizationModule))] public class AbpAccountApplicationModule : AbpModule { diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN/Abp/Account/AccountAppService.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN/Abp/Account/AccountAppService.cs index 0f493118a..e1c531a04 100644 --- a/aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN/Abp/Account/AccountAppService.cs +++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN/Abp/Account/AccountAppService.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Identity; +using LINGYUN.Abp.WeChat.Authorization; +using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Caching.Distributed; using System; using System.Threading.Tasks; @@ -8,8 +9,6 @@ using Volo.Abp.Caching; using Volo.Abp.Identity; using Volo.Abp.Settings; using Volo.Abp.Sms; -using Volo.Abp.Uow; -using Microsoft.AspNetCore.Cryptography; namespace LINGYUN.Abp.Account { @@ -18,6 +17,8 @@ namespace LINGYUN.Abp.Account /// public class AccountAppService : ApplicationService, IAccountAppService { + private IWeChatOpenIdFinder _weChatOpenIdFinder; + protected IWeChatOpenIdFinder WeChatOpenIdFinder => LazyGetRequiredService(ref _weChatOpenIdFinder); protected ISmsSender SmsSender { get; } protected IdentityUserManager UserManager { get; } protected IdentityUserStore UserStore { get; } @@ -40,6 +41,32 @@ namespace LINGYUN.Abp.Account PhoneNumberTokenProvider = phoneNumberTokenProvider; LocalizationResource = typeof(Localization.AccountResource); } + + public virtual async Task RegisterAsync(WeChatRegisterDto input) + { + await CheckSelfRegistrationAsync(); + + var wehchatOpenId = await WeChatOpenIdFinder.FindAsync(input.Code); + + var user = await UserManager.FindByLoginAsync("WeChat", wehchatOpenId.OpenId); + if (user == null) + { + var userName = input.UserName ?? wehchatOpenId.OpenId; + var userEmail = input.EmailAddress ?? $"{userName}@{new Random().Next(1000, 99999)}.com";//如果邮件地址不验证,随意写入一个 + + user = new IdentityUser(GuidGenerator.Create(), userName, userEmail, CurrentTenant.Id) + { + Name = input.Name ?? userName + }; + (await UserManager.CreateAsync(user, input.Password)).CheckErrors(); + + (await UserManager.AddDefaultRolesAsync(user)).CheckErrors(); + + var userLogin = new UserLoginInfo("WeChat", wehchatOpenId.OpenId, "微信认证登录"); + (await UserManager.AddLoginAsync(user, userLogin)).CheckErrors(); + } + return ObjectMapper.Map(user); + } /// /// 用户注册 /// @@ -50,7 +77,7 @@ namespace LINGYUN.Abp.Account /// 如果没有此手机号的缓存记录或验证码不匹配,抛出验证码无效的异常 /// 用户注册成功,清除缓存的验证码记录 /// - public virtual async Task RegisterAsync(RegisterVerifyDto input) + public virtual async Task RegisterAsync(PhoneNumberRegisterDto input) { var phoneVerifyCacheKey = NormalizeCacheKey(input.PhoneNumber); diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Domain/LINGYUN/Abp/Account/Localization/Resources/en.json b/aspnet-core/modules/account/LINGYUN.Abp.Account.Domain/LINGYUN/Abp/Account/Localization/Resources/en.json index d94881919..758961ad5 100644 --- a/aspnet-core/modules/account/LINGYUN.Abp.Account.Domain/LINGYUN/Abp/Account/Localization/Resources/en.json +++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Domain/LINGYUN/Abp/Account/Localization/Resources/en.json @@ -14,6 +14,7 @@ "Description:PhoneVerifyCodeExpiration": "The valid time for the user to send SMS verification code, unit m, default 3m", "RequiredEmailAddress": "Email address required", "InvalidPhoneNumber": "Invalid phone number", - "DuplicatePhoneNumber": "The phone number {0} has been registered!" + "DuplicatePhoneNumber": "The phone number {0} has been registered!", + "DuplicateWeChat": "The wechat has been registered!" } } \ No newline at end of file diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Domain/LINGYUN/Abp/Account/Localization/Resources/zh-Hans.json b/aspnet-core/modules/account/LINGYUN.Abp.Account.Domain/LINGYUN/Abp/Account/Localization/Resources/zh-Hans.json index 114e36d31..da45e2944 100644 --- a/aspnet-core/modules/account/LINGYUN.Abp.Account.Domain/LINGYUN/Abp/Account/Localization/Resources/zh-Hans.json +++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Domain/LINGYUN/Abp/Account/Localization/Resources/zh-Hans.json @@ -14,6 +14,7 @@ "Description:PhoneVerifyCodeExpiration": "用户发送短信验证码的有效时长,单位m,默认3m", "RequiredEmailAddress": "邮件地址必须输入", "InvalidPhoneNumber": "手机号无效", - "DuplicatePhoneNumber": "手机号已经注册过!" + "DuplicatePhoneNumber": "手机号已经注册过!", + "DuplicateWeChat": "微信号已经注册过!" } } \ No newline at end of file diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.HttpApi/LINGYUN/Abp/Account/AccountController.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.HttpApi/LINGYUN/Abp/Account/AccountController.cs index af5b83b4a..55d4db76d 100644 --- a/aspnet-core/modules/account/LINGYUN.Abp.Account.HttpApi/LINGYUN/Abp/Account/AccountController.cs +++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.HttpApi/LINGYUN/Abp/Account/AccountController.cs @@ -9,7 +9,7 @@ namespace LINGYUN.Abp.Account { [RemoteService(Name = AccountRemoteServiceConsts.RemoteServiceName)] [Area("account")] - [Route("api/account/phone")] + [Route("api/account")] public class AccountController : AbpController, IAccountAppService { protected IAccountAppService AccountAppService { get; } @@ -20,21 +20,28 @@ namespace LINGYUN.Abp.Account } [HttpPost] - [Route("register")] - public virtual async Task RegisterAsync(RegisterVerifyDto input) + [Route("wechat/register")] + public virtual async Task RegisterAsync(WeChatRegisterDto input) { return await AccountAppService.RegisterAsync(input); } [HttpPost] - [Route("verify")] + [Route("phone/register")] + public virtual async Task RegisterAsync(PhoneNumberRegisterDto input) + { + return await AccountAppService.RegisterAsync(input); + } + + [HttpPost] + [Route("phone/verify")] public virtual async Task VerifyPhoneNumberAsync(VerifyDto input) { await AccountAppService.VerifyPhoneNumberAsync(input); } [HttpPut] - [Route("reset-password")] + [Route("phone/reset-password")] public virtual async Task ResetPasswordAsync(PasswordResetDto passwordReset) { await AccountAppService.ResetPasswordAsync(passwordReset); diff --git a/aspnet-core/modules/common/LINGYUN.Abp.Aliyun.Authorization/LINGYUN.Abp.Aliyun.Authorization.csproj b/aspnet-core/modules/common/LINGYUN.Abp.Aliyun.Authorization/LINGYUN.Abp.Aliyun.Authorization.csproj index 8be61f7bf..eb9ec9cbb 100644 --- a/aspnet-core/modules/common/LINGYUN.Abp.Aliyun.Authorization/LINGYUN.Abp.Aliyun.Authorization.csproj +++ b/aspnet-core/modules/common/LINGYUN.Abp.Aliyun.Authorization/LINGYUN.Abp.Aliyun.Authorization.csproj @@ -3,6 +3,13 @@ netstandard2.0 + true + 2.9.0 + LINGYUN + + + + D:\LocalNuget diff --git a/aspnet-core/modules/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunModule.cs b/aspnet-core/modules/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunModule.cs index 4e77d7030..8930d080d 100644 --- a/aspnet-core/modules/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunModule.cs +++ b/aspnet-core/modules/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunModule.cs @@ -23,7 +23,7 @@ namespace LINGYUN.Abp.BlobStoring.Aliyun { containerConfiguration.UseAliyun(aliyun => { - aliyun.BucketName = configuration[AliyunBlobProviderConfigurationNames.BucketName]; + aliyun.BucketName = configuration[AliyunBlobProviderConfigurationNames.BucketName] ?? ""; aliyun.CreateBucketIfNotExists = configuration.GetSection(AliyunBlobProviderConfigurationNames.CreateBucketIfNotExists).Get(); aliyun.CreateBucketReferer = configuration.GetSection(AliyunBlobProviderConfigurationNames.CreateBucketReferer).Get>(); aliyun.Endpoint = configuration[AliyunBlobProviderConfigurationNames.Endpoint]; diff --git a/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/AbpIdentityServerWeChatValidatorModule.cs b/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/AbpIdentityServerWeChatValidatorModule.cs index a1b4e7c4c..e821e5f24 100644 --- a/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/AbpIdentityServerWeChatValidatorModule.cs +++ b/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/AbpIdentityServerWeChatValidatorModule.cs @@ -28,11 +28,6 @@ namespace LINGYUN.Abp.IdentityServer Configure(configuration.GetSection("WeChat:Signature")); - context.Services.AddHttpClient(WeChatValidatorConsts.WeChatValidatorClientName, options => - { - options.BaseAddress = new System.Uri("https://api.weixin.qq.com/sns/jscode2session"); - }); - Configure(options => { options.FileSets.AddEmbedded(); diff --git a/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/WeChatValidator/WeChatTokenGrantValidator.cs b/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/WeChatValidator/WeChatTokenGrantValidator.cs index 30955d77f..3f3485552 100644 --- a/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/WeChatValidator/WeChatTokenGrantValidator.cs +++ b/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/WeChatValidator/WeChatTokenGrantValidator.cs @@ -26,6 +26,7 @@ namespace LINGYUN.Abp.IdentityServer.WeChatValidator protected AbpWeChatOptions Options { get; } protected IHttpClientFactory HttpClientFactory{ get; } protected IEventService EventService { get; } + protected IWeChatOpenIdFinder WeChatOpenIdFinder { get; } protected IIdentityUserRepository UserRepository { get; } protected UserManager UserManager { get; } protected SignInManager SignInManager { get; } @@ -35,6 +36,7 @@ namespace LINGYUN.Abp.IdentityServer.WeChatValidator public WeChatTokenGrantValidator( IEventService eventService, + IWeChatOpenIdFinder weChatOpenIdFinder, IHttpClientFactory httpClientFactory, UserManager userManager, IIdentityUserRepository userRepository, @@ -52,6 +54,7 @@ namespace LINGYUN.Abp.IdentityServer.WeChatValidator SignInManager = signInManager; Localizer = stringLocalizer; UserRepository = userRepository; + WeChatOpenIdFinder = weChatOpenIdFinder; HttpClientFactory = httpClientFactory; PhoneNumberTokenProvider = phoneNumberTokenProvider; } @@ -77,28 +80,11 @@ namespace LINGYUN.Abp.IdentityServer.WeChatValidator Localizer["InvalidGrant:WeChatCodeNotFound"]); return; } - var httpClient = HttpClientFactory.CreateClient(WeChatValidatorConsts.WeChatValidatorClientName); - var httpRequest = new WeChatOpenIdRequest - { - Code = wechatCode, - AppId = Options.AppId, - Secret = Options.AppSecret, - BaseUrl = httpClient.BaseAddress.AbsoluteUri - }; - - var wechatOpenIdResponse = await httpClient.RequestWeChatCodeTokenAsync(httpRequest); - if (wechatOpenIdResponse.IsError) - { - Logger.LogWarning("Authentication failed for token: {0}, reason: invalid token", wechatCode); - Logger.LogWarning("WeChat auth failed, error: {0}", wechatOpenIdResponse.ErrorMessage); - context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, - Localizer["InvalidGrant:WeChatTokenInvalid"]); - return; - } - var currentUser = await UserManager.FindByNameAsync(wechatOpenIdResponse.OpenId); + var whchatOpenId = await WeChatOpenIdFinder.FindAsync(wechatCode); + var currentUser = await UserManager.FindByLoginAsync("WeChat", whchatOpenId.OpenId); if(currentUser == null) { - Logger.LogWarning("Invalid grant type: wechat openid: {0} not register", wechatOpenIdResponse.OpenId); + Logger.LogWarning("Invalid grant type: wechat openid: {0} not register", whchatOpenId.OpenId); context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, Localizer["InvalidGrant:WeChatNotRegister"]); return; @@ -110,9 +96,9 @@ namespace LINGYUN.Abp.IdentityServer.WeChatValidator { additionalClaims.Add(new Claim(AbpClaimTypes.TenantId, currentUser.TenantId?.ToString())); } - additionalClaims.Add(new Claim(WeChatValidatorConsts.ClaimTypes.OpenId, wechatOpenIdResponse.OpenId)); + additionalClaims.Add(new Claim(WeChatValidatorConsts.ClaimTypes.OpenId, whchatOpenId.OpenId)); - await EventService.RaiseAsync(new UserLoginSuccessEvent(currentUser.UserName, wechatOpenIdResponse.OpenId, null)); + await EventService.RaiseAsync(new UserLoginSuccessEvent(currentUser.UserName, whchatOpenId.OpenId, null)); context.Result = new GrantValidationResult(sub, WeChatValidatorConsts.AuthenticationMethods.BasedWeChatAuthentication, additionalClaims.ToArray()); } diff --git a/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/System/Net/Http/HttpClientTokenRequestExtensions.cs b/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/System/Net/Http/HttpClientTokenRequestExtensions.cs deleted file mode 100644 index bea6631b8..000000000 --- a/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/System/Net/Http/HttpClientTokenRequestExtensions.cs +++ /dev/null @@ -1,32 +0,0 @@ -using IdentityModel.Client; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace System.Net.Http -{ - public static class HttpClientTokenRequestExtensions - { - public static async Task RequestWeChatCodeTokenAsync(this HttpMessageInvoker client, WeChatOpenIdRequest request, CancellationToken cancellationToken = default) - { - var getResuestUrlBuiilder = new StringBuilder(); - getResuestUrlBuiilder.Append(request.BaseUrl); - getResuestUrlBuiilder.AppendFormat("?appid={0}", request.AppId); - getResuestUrlBuiilder.AppendFormat("&secret={0}", request.Secret); - getResuestUrlBuiilder.AppendFormat("&js_code={0}", request.Code); - getResuestUrlBuiilder.Append("&grant_type=authorization_code"); - - var getRequest = new HttpRequestMessage(HttpMethod.Get, getResuestUrlBuiilder.ToString()); - HttpResponseMessage httpResponse; - try - { - httpResponse = await client.SendAsync(getRequest, cancellationToken).ConfigureAwait(false); - } - catch (Exception ex) - { - return ProtocolResponse.FromException(ex); - } - return await ProtocolResponse.FromHttpResponseAsync(httpResponse).ConfigureAwait(false); - } - } -} diff --git a/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/System/Net/Http/WeChatOpenIdResponse.cs b/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/System/Net/Http/WeChatOpenIdResponse.cs deleted file mode 100644 index 8e13e7d83..000000000 --- a/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/System/Net/Http/WeChatOpenIdResponse.cs +++ /dev/null @@ -1,48 +0,0 @@ -using IdentityModel.Client; - -namespace System.Net.Http -{ - public class WeChatOpenIdResponse : ProtocolResponse - { - /// - /// 用户唯一标识 - /// - public string OpenId => TryGet("openid"); - /// - /// 会话密钥 - /// - /// - /// 仅仅只是要一个openid,这个没多大用吧 - /// - public string SessionKey => TryGet("session_key"); - /// - /// 用户在开放平台的唯一标识符,在满足 UnionID 下发条件的情况下会返回 - /// - public string UnionId => TryGet("unionid"); - /// - /// 微信认证成功没有errorcode或者errorcode为0 - /// - public new bool IsError => !ErrorCode.IsNullOrWhiteSpace() && !"0".Equals(ErrorCode); - /// - /// 错误码 - /// - public string ErrorCode => TryGet("errcode"); - /// - /// 错误信息 - /// - public new string ErrorMessage - { - get - { - return ErrorCode switch - { - "-1" => "系统繁忙,此时请开发者稍候再试", - "0" => string.Empty, - "40029" => "code 无效", - "45011" => "频率限制,每个用户每分钟100次", - _ => "未知的异常", - }; - } - } - } -} diff --git a/aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/AbpWeChatAuthorizationModule.cs b/aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/AbpWeChatAuthorizationModule.cs index e6bf4c160..cb8ba8810 100644 --- a/aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/AbpWeChatAuthorizationModule.cs +++ b/aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/AbpWeChatAuthorizationModule.cs @@ -15,9 +15,9 @@ namespace LINGYUN.Abp.WeChat.Authorization var configuration = context.Services.GetConfiguration(); Configure(configuration.GetSection("WeChat:Auth")); - context.Services.AddHttpClient("WeChatTokenProviderClient", options => + context.Services.AddHttpClient("WeChatRequestClient", options => { - options.BaseAddress = new Uri("https://api.weixin.qq.com/cgi-bin/token"); + options.BaseAddress = new Uri("https://api.weixin.qq.com"); }).AddTransientHttpErrorPolicy(builder => builder.WaitAndRetryAsync(3, i => TimeSpan.FromSeconds(Math.Pow(2, i)))); } diff --git a/aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/OpenId/IWeChatOpenIdFinder.cs b/aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/OpenId/IWeChatOpenIdFinder.cs new file mode 100644 index 000000000..3fa47002a --- /dev/null +++ b/aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/OpenId/IWeChatOpenIdFinder.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace LINGYUN.Abp.WeChat.Authorization +{ + public interface IWeChatOpenIdFinder + { + Task FindAsync(string code); + } +} diff --git a/aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/OpenId/WeChatOpenId.cs b/aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/OpenId/WeChatOpenId.cs new file mode 100644 index 000000000..a1e4ae9a9 --- /dev/null +++ b/aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/OpenId/WeChatOpenId.cs @@ -0,0 +1,30 @@ +namespace LINGYUN.Abp.WeChat.Authorization +{ + public class WeChatOpenId + { + /// + /// 用户在开放平台的唯一标识符,在满足 UnionID 下发条件的情况下会返回 + /// + public string UnionId { get; set; } + /// + /// 用户唯一标识 + /// + public string OpenId { get; set; } + /// + /// 会话密钥 + /// + public string SessionKey { get; set; } + + public WeChatOpenId() + { + + } + + public WeChatOpenId(string openId, string sessionKey, string unionId = null) + { + OpenId = openId; + SessionKey = sessionKey; + UnionId = unionId; + } + } +} diff --git a/aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/OpenId/WeChatOpenIdCacheItem.cs b/aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/OpenId/WeChatOpenIdCacheItem.cs new file mode 100644 index 000000000..52950c532 --- /dev/null +++ b/aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/OpenId/WeChatOpenIdCacheItem.cs @@ -0,0 +1,28 @@ +using System; + +namespace LINGYUN.Abp.WeChat.Authorization +{ + public class WeChatOpenIdCacheItem + { + public string Code { get; set; } + + public WeChatOpenId WeChatOpenId { get; set; } + public WeChatOpenIdCacheItem() + { + + } + + public WeChatOpenIdCacheItem(string code, WeChatOpenId weChatOpenId) + { + Code = code; + WeChatOpenId = weChatOpenId; + } + + public static string CalculateCacheKey(string code, Guid? tenantId = null) + { + string tenant = tenantId != null ? tenantId.Value.ToString("D") : "host"; + + return "t:" + tenant + ",c:" + code; + } + } +} diff --git a/aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/OpenId/WeChatOpenIdFinder.cs b/aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/OpenId/WeChatOpenIdFinder.cs new file mode 100644 index 000000000..1adad5390 --- /dev/null +++ b/aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/OpenId/WeChatOpenIdFinder.cs @@ -0,0 +1,97 @@ +using Microsoft.Extensions.Caching.Distributed; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; +using System; +using System.Net.Http; +using System.Threading.Tasks; +using Volo.Abp.Caching; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Json; +using Volo.Abp.MultiTenancy; + +namespace LINGYUN.Abp.WeChat.Authorization +{ + [Dependency(ServiceLifetime.Singleton, ReplaceServices = true)] + [ExposeServices(typeof(IWeChatOpenIdFinder))] + public class WeChatOpenIdFinder : IWeChatOpenIdFinder + { + public ILogger Logger { get; set; } + protected AbpWeChatOptions Options { get; } + protected ICurrentTenant CurrentTenant { get; } + protected IHttpClientFactory HttpClientFactory { get; } + protected IJsonSerializer JsonSerializer { get; } + protected IDistributedCache Cache { get; } + public WeChatOpenIdFinder( + ICurrentTenant currentTenant, + IJsonSerializer jsonSerializer, + IHttpClientFactory httpClientFactory, + IOptions options, + IDistributedCache cache) + { + CurrentTenant = currentTenant; + JsonSerializer = jsonSerializer; + HttpClientFactory = httpClientFactory; + + Cache = cache; + Options = options.Value; + + Logger = NullLogger.Instance; + } + public virtual async Task FindAsync(string code) + { + return (await GetCacheItemAsync(code, CurrentTenant.Id)).WeChatOpenId; + } + + protected virtual async Task GetCacheItemAsync(string code, Guid? tenantId = null) + { + var cacheKey = WeChatOpenIdCacheItem.CalculateCacheKey(code, tenantId); + + Logger.LogDebug($"WeChatOpenIdFinder.GetCacheItemAsync: {cacheKey}"); + + var cacheItem = await Cache.GetAsync(cacheKey); + + if (cacheItem != null) + { + Logger.LogDebug($"Found in the cache: {cacheKey}"); + return cacheItem; + } + + Logger.LogDebug($"Not found in the cache, getting from the httpClient: {cacheKey}"); + + var client = HttpClientFactory.CreateClient("WeChatRequestClient"); + + var request = new WeChatOpenIdRequest + { + BaseUrl = client.BaseAddress.AbsoluteUri, + AppId = Options.AppId, + Secret = Options.AppSecret, + Code = code + }; + + var response = await client.RequestWeChatOpenIdAsync(request); + var responseContent = await response.Content.ReadAsStringAsync(); + var weChatOpenIdResponse = JsonSerializer.Deserialize(responseContent); + var weChatOpenId = weChatOpenIdResponse.ToWeChatOpenId(); + cacheItem = new WeChatOpenIdCacheItem(code, weChatOpenId); + + Logger.LogDebug($"Setting the cache item: {cacheKey}"); + + var cacheOptions = new DistributedCacheEntryOptions + { + // 微信官方文档表示 session_key的有效期是3天 + // https://developers.weixin.qq.com/community/develop/doc/000c2424654c40bd9c960e71e5b009 + AbsoluteExpiration = DateTimeOffset.Now.AddDays(3) + // SlidingExpiration = TimeSpan.FromDays(3), + }; + + + await Cache.SetAsync(cacheKey, cacheItem, cacheOptions); + + Logger.LogDebug($"Finished setting the cache item: {cacheKey}"); + + return cacheItem; + } + } +} diff --git a/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/System/Net/Http/WeChatOpenIdRequest.cs b/aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/OpenId/WeChatOpenIdRequest.cs similarity index 82% rename from aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/System/Net/Http/WeChatOpenIdRequest.cs rename to aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/OpenId/WeChatOpenIdRequest.cs index 5dddab29e..719d57089 100644 --- a/aspnet-core/modules/common/LINGYUN.Abp.IdentityServer.WeChatValidator/System/Net/Http/WeChatOpenIdRequest.cs +++ b/aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/OpenId/WeChatOpenIdRequest.cs @@ -1,4 +1,4 @@ -namespace System.Net.Http +namespace LINGYUN.Abp.WeChat.Authorization { public class WeChatOpenIdRequest { diff --git a/aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/OpenId/WeChatOpenIdResponse.cs b/aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/OpenId/WeChatOpenIdResponse.cs new file mode 100644 index 000000000..f85b48e42 --- /dev/null +++ b/aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/OpenId/WeChatOpenIdResponse.cs @@ -0,0 +1,63 @@ +using Newtonsoft.Json; +using System; +using Volo.Abp; + +namespace LINGYUN.Abp.WeChat.Authorization +{ + /// + /// 微信OpenId返回对象 + /// + public class WeChatOpenIdResponse + { + /// + /// 错误码 + /// + [JsonProperty("errcode")] + public string ErrorCode { get; set; } + /// + /// 会话密钥 + /// + [JsonProperty("session_key")] + public string SessionKey { get; set; } + /// + /// 用户唯一标识 + /// + [JsonProperty("openid")] + public string OpenId { get; set; } + /// + /// 用户在开放平台的唯一标识符,在满足 UnionID 下发条件的情况下会返回 + /// + [JsonProperty("unionid")] + public string UnionId { get; set; } + /// + /// 错误消息 + /// + public string ErrorMessage + { + get + { + switch (ErrorCode) + { + case "-1": return "系统繁忙,此时请开发者稍候再试"; + case "0": return string.Empty; + case "40029": return "code 无效"; + case "45011": return "频率限制,每个用户每分钟100次"; + default: return "未知的异常,请重试"; + }; + } + } + /// + /// 微信认证成功没有errorcode或者errorcode为0 + /// + public bool IsError => !ErrorCode.IsNullOrWhiteSpace() && !"0".Equals(ErrorCode); + + public WeChatOpenId ToWeChatOpenId() + { + if(IsError) + { + throw new AbpException(ErrorMessage); + } + return new WeChatOpenId(OpenId, SessionKey, UnionId); + } + } +} diff --git a/aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/IWeChatTokenProvider.cs b/aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/Token/IWeChatTokenProvider.cs similarity index 100% rename from aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/IWeChatTokenProvider.cs rename to aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/Token/IWeChatTokenProvider.cs diff --git a/aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/WeChatToken.cs b/aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/Token/WeChatToken.cs similarity index 100% rename from aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/WeChatToken.cs rename to aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/Token/WeChatToken.cs diff --git a/aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/WeChatTokenCacheItem.cs b/aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/Token/WeChatTokenCacheItem.cs similarity index 100% rename from aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/WeChatTokenCacheItem.cs rename to aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/Token/WeChatTokenCacheItem.cs diff --git a/aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/WeChatTokenProvider.cs b/aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/Token/WeChatTokenProvider.cs similarity index 97% rename from aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/WeChatTokenProvider.cs rename to aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/Token/WeChatTokenProvider.cs index 3471461e3..3c6b2b7e6 100644 --- a/aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/WeChatTokenProvider.cs +++ b/aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/Token/WeChatTokenProvider.cs @@ -54,7 +54,7 @@ namespace LINGYUN.Abp.WeChat.Authorization Logger.LogDebug($"Not found in the cache, getting from the httpClient: {cacheKey}"); - var client = HttpClientFactory.CreateClient("WeChatTokenProviderClient"); + var client = HttpClientFactory.CreateClient("WeChatRequestClient"); var request = new WeChatTokenRequest { diff --git a/aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/WeChatTokenRequest.cs b/aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/Token/WeChatTokenRequest.cs similarity index 100% rename from aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/WeChatTokenRequest.cs rename to aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/Token/WeChatTokenRequest.cs diff --git a/aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/WeChatTokenResponse.cs b/aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/Token/WeChatTokenResponse.cs similarity index 100% rename from aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/WeChatTokenResponse.cs rename to aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/Token/WeChatTokenResponse.cs diff --git a/aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/System/Net/Http/HttpClientWeChatTokenRequestExtensions.cs b/aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/System/Net/Http/HttpClientWeChatTokenRequestExtensions.cs index 08cb5cda3..bdb5c9888 100644 --- a/aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/System/Net/Http/HttpClientWeChatTokenRequestExtensions.cs +++ b/aspnet-core/modules/common/LINGYUN.Abp.WeChat.Authorization/System/Net/Http/HttpClientWeChatTokenRequestExtensions.cs @@ -11,6 +11,7 @@ namespace System.Net.Http { var getResuestUrlBuilder = new StringBuilder(); getResuestUrlBuilder.Append(request.BaseUrl); + getResuestUrlBuilder.Append("cgi-bin/token"); getResuestUrlBuilder.Append("?grant_type=client_credential"); getResuestUrlBuilder.AppendFormat("&appid={0}", request.AppId); getResuestUrlBuilder.AppendFormat("&secret={0}", request.AppSecret); @@ -22,5 +23,23 @@ namespace System.Net.Http return httpResponse; } + + public static async Task RequestWeChatOpenIdAsync(this HttpMessageInvoker client, WeChatOpenIdRequest request, CancellationToken cancellationToken = default) + { + var getResuestUrlBuiilder = new StringBuilder(); + getResuestUrlBuiilder.Append(request.BaseUrl); + getResuestUrlBuiilder.Append("sns/jscode2session"); + getResuestUrlBuiilder.AppendFormat("?appid={0}", request.AppId); + getResuestUrlBuiilder.AppendFormat("&secret={0}", request.Secret); + getResuestUrlBuiilder.AppendFormat("&js_code={0}", request.Code); + getResuestUrlBuiilder.Append("&grant_type=authorization_code"); + + var getRequest = new HttpRequestMessage(HttpMethod.Get, getResuestUrlBuiilder.ToString()); + HttpResponseMessage httpResponse; + + httpResponse = await client.SendAsync(getRequest, cancellationToken).ConfigureAwait(false); + + return httpResponse; + } } }