Browse Source

Merge pull request #126 from colinin/3.3

Added WeChat external login provider
pull/177/head
cKey 5 years ago
committed by GitHub
parent
commit
472ada8dc9
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      aspnet-core/LINGYUN.MicroService.All.sln
  2. 4
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN.Abp.IdentityServer.WeChatValidator.csproj
  3. 11
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/AbpIdentityServerWeChatValidatorModule.cs
  4. 5
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/AspNetIdentity/AbpWeChatProfileService.cs
  5. 9
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/IWeChatResourceDataSeeder.cs
  6. 85
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/WeChatResourceDataSeeder.cs
  7. 6
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/WeChatSignatureMiddleware.cs
  8. 15
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/WeChatValidator/WeChatTokenGrantValidator.cs
  9. 22
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/WeChat/Authorization/UserWeChatCodeFinder.cs
  10. 316
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChatValidator/Microsoft/AspNetCore/Authentication/WeChat/WeChatAuthenticationHandler.cs
  11. 54
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChatValidator/Microsoft/AspNetCore/Authentication/WeChat/WeChatAuthenticationOptions.cs
  12. 18
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChatValidator/Microsoft/AspNetCore/Authentication/WeChat/WeChatAuthenticationStateCacheItem.cs
  13. 64
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChatValidator/Microsoft/AspNetCore/Authentication/WeChatAuthenticationExtensions.cs
  14. 16
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChatValidator/System/BytesExtensions.cs
  15. 17
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChatValidator/System/StringExtensions.cs
  16. 63
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChatValidator/System/Text/Json/JsonElementExtensions.cs
  17. 16
      aspnet-core/modules/wechat/LINGYUN.Abp.Notifications.WeChat/LINGYUN/Abp/Notifications/WeChat/WeApp/WeChatWeAppNotificationPublishProvider.cs
  18. 72
      aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/AbpWeChatAuthorizationConsts.cs
  19. 2
      aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/AbpWeChatAuthorizationModule.cs
  20. 2
      aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/AbpWeChatAuthorizationOptions.cs
  21. 48
      aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/AbpWeChatClaimTypes.cs
  22. 2
      aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/OpenId/IUserWeChatOpenIdFinder.cs
  23. 4
      aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/OpenId/IWeChatOpenIdFinder.cs
  24. 6
      aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/OpenId/NullUserWeChatOpenIdFinder.cs
  25. 25
      aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/OpenId/WeChatOpenIdFinder.cs
  26. 4
      aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/Token/WeChatTokenProvider.cs
  27. 22
      aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/WeChatAuthorizationConsts.cs
  28. 8
      aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Authorization/Volo/Abp/Security/Claims/WeChatClaimTypes.cs
  29. 6
      aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Authorization/Volo/Abp/Users/CurrentUserExtensions.cs
  30. 9
      aspnet-core/services/account/AuthServer.Host/AuthIdentityServerModule.cs
  31. 15
      aspnet-core/services/account/AuthServer.Host/DataSeeder/IdentityServerDataSeedContributor.cs
  32. 10
      aspnet-core/services/account/AuthServer.Host/Microsoft/Extensions/DependencyInjection/SameSiteCookiesServiceCollectionExtensions.cs

2
aspnet-core/LINGYUN.MicroService.All.sln

@ -259,7 +259,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.AspNetCore.Sign
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "wechat", "wechat", "{DD9BE9E7-F6BF-4869-BCD2-82F5072BDA21}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.WeChat", "modules\wechat\LINGYUN.Abp.WeChat\LINGYUN.Abp.WeChat.csproj", "{BAE74ABC-1096-495F-A624-BEBFBC1896F2}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.WeChat", "modules\wechat\LINGYUN.Abp.WeChat\LINGYUN.Abp.WeChat.csproj", "{BAE74ABC-1096-495F-A624-BEBFBC1896F2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution

4
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN.Abp.IdentityServer.WeChatValidator.csproj

@ -25,4 +25,8 @@
<ProjectReference Include="..\..\wechat\LINGYUN.Abp.WeChat.Authorization\LINGYUN.Abp.WeChat.Authorization.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Microsoft\DependencyInjection\" />
</ItemGroup>
</Project>

11
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/AbpIdentityServerWeChatValidatorModule.cs

@ -1,6 +1,8 @@
using LINGYUN.Abp.IdentityServer.AspNetIdentity;
using LINGYUN.Abp.IdentityServer.WeChatValidator;
using LINGYUN.Abp.WeChat.Authorization;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.IdentityServer;
using Volo.Abp.IdentityServer.Localization;
@ -17,6 +19,8 @@ namespace LINGYUN.Abp.IdentityServer
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
PreConfigure<IIdentityServerBuilder>(builder =>
{
builder.AddProfileService<AbpWeChatProfileServicee>();
@ -30,6 +34,13 @@ namespace LINGYUN.Abp.IdentityServer
Configure<WeChatSignatureOptions>(configuration.GetSection("WeChat:Signature"));
context.Services
.AddAuthentication()
.AddWeChat(options => // 加入微信认证登录
{
configuration.GetSection("WeChat:Auth")?.Bind(options);
});
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<AbpIdentityServerWeChatValidatorModule>();

5
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/AspNetIdentity/AbpWeChatProfileService.cs

@ -1,5 +1,6 @@
using IdentityServer4.AspNetIdentity;
using IdentityServer4.Models;
using LINGYUN.Abp.WeChat.Authorization;
using Microsoft.AspNetCore.Identity;
using System.Linq;
using System.Security.Principal;
@ -32,8 +33,8 @@ namespace LINGYUN.Abp.IdentityServer.AspNetIdentity
await base.GetProfileDataAsync(context);
// TODO: 可以从令牌获取openid, 安全性呢?
TryAddWeChatClaim(context, WeChatClaimTypes.OpenId);
TryAddWeChatClaim(context, WeChatClaimTypes.UnionId);
TryAddWeChatClaim(context, AbpWeChatClaimTypes.OpenId);
TryAddWeChatClaim(context, AbpWeChatClaimTypes.UnionId);
}
}

9
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/IWeChatResourceDataSeeder.cs

@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace LINGYUN.Abp.IdentityServer
{
public interface IWeChatResourceDataSeeder
{
Task CreateStandardResourcesAsync();
}
}

85
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/WeChatResourceDataSeeder.cs

@ -0,0 +1,85 @@
using LINGYUN.Abp.WeChat.Authorization;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Guids;
using Volo.Abp.Identity;
using Volo.Abp.IdentityServer.IdentityResources;
namespace LINGYUN.Abp.IdentityServer
{
public class WeChatResourceDataSeeder : IWeChatResourceDataSeeder, ITransientDependency
{
protected IIdentityClaimTypeRepository ClaimTypeRepository { get; }
protected IIdentityResourceRepository IdentityResourceRepository { get; }
protected IGuidGenerator GuidGenerator { get; }
public WeChatResourceDataSeeder(
IIdentityResourceRepository identityResourceRepository,
IGuidGenerator guidGenerator,
IIdentityClaimTypeRepository claimTypeRepository)
{
IdentityResourceRepository = identityResourceRepository;
GuidGenerator = guidGenerator;
ClaimTypeRepository = claimTypeRepository;
}
public virtual async Task CreateStandardResourcesAsync()
{
var wechatClaimTypes = new string[]
{
AbpWeChatClaimTypes.AvatarUrl,
AbpWeChatClaimTypes.City,
AbpWeChatClaimTypes.Country,
AbpWeChatClaimTypes.NickName,
AbpWeChatClaimTypes.OpenId,
AbpWeChatClaimTypes.Privilege,
AbpWeChatClaimTypes.Province,
AbpWeChatClaimTypes.Sex,
AbpWeChatClaimTypes.UnionId
};
var wechatResource = new IdentityServer4.Models.IdentityResource(
AbpWeChatAuthorizationConsts.ProfileKey,
AbpWeChatAuthorizationConsts.DisplayName,
wechatClaimTypes);
foreach (var claimType in wechatClaimTypes)
{
await AddClaimTypeIfNotExistsAsync(claimType);
}
await AddIdentityResourceIfNotExistsAsync(wechatResource);
}
protected virtual async Task AddIdentityResourceIfNotExistsAsync(IdentityServer4.Models.IdentityResource resource)
{
if (await IdentityResourceRepository.CheckNameExistAsync(resource.Name))
{
return;
}
await IdentityResourceRepository.InsertAsync(
new IdentityResource(
GuidGenerator.Create(),
resource
)
);
}
protected virtual async Task AddClaimTypeIfNotExistsAsync(string claimType)
{
if (await ClaimTypeRepository.AnyAsync(claimType))
{
return;
}
await ClaimTypeRepository.InsertAsync(
new IdentityClaimType(
GuidGenerator.Create(),
claimType,
isStatic: true
)
);
}
}
}

6
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/WeChatSignatureMiddleware.cs

@ -2,8 +2,6 @@
using Microsoft.Extensions.Options;
using System;
using System.Collections;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.DependencyInjection;
@ -63,9 +61,7 @@ namespace LINGYUN.Abp.IdentityServer
signatureStr += al[i];
}
// step3 SHA1加密
using var sha1 = new SHA1CryptoServiceProvider();
byte[] bytes_in = Encoding.ASCII.GetBytes(signatureStr);
byte[] bytes_out = sha1.ComputeHash(bytes_in);
byte[] bytes_out = signatureStr.Sha1();
string result = BitConverter.ToString(bytes_out).Replace("-", "");
// step4 比对
if (result.Equals(signature, StringComparison.CurrentCultureIgnoreCase))

15
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/IdentityServer/WeChatValidator/WeChatTokenGrantValidator.cs

@ -23,7 +23,7 @@ namespace LINGYUN.Abp.IdentityServer.WeChatValidator
public class WeChatTokenGrantValidator : IExtensionGrantValidator
{
protected ILogger<WeChatTokenGrantValidator> Logger { get; }
protected AbpWeChatOptions Options { get; }
protected AbpWeChatAuthorizationOptions Options { get; }
protected IHttpClientFactory HttpClientFactory{ get; }
protected IEventService EventService { get; }
protected IWeChatOpenIdFinder WeChatOpenIdFinder { get; }
@ -43,7 +43,7 @@ namespace LINGYUN.Abp.IdentityServer.WeChatValidator
SignInManager<IdentityUser> signInManager,
IStringLocalizer<AbpIdentityServerResource> stringLocalizer,
PhoneNumberTokenProvider<IdentityUser> phoneNumberTokenProvider,
IOptions<AbpWeChatOptions> options,
IOptions<AbpWeChatAuthorizationOptions> options,
ILogger<WeChatTokenGrantValidator> logger)
{
Logger = logger;
@ -82,7 +82,7 @@ namespace LINGYUN.Abp.IdentityServer.WeChatValidator
return;
}
var wechatOpenId = await WeChatOpenIdFinder.FindAsync(wechatCode);
var currentUser = await UserManager.FindByLoginAsync(WeChatAuthorizationConsts.ProviderKey, wechatOpenId.OpenId);
var currentUser = await UserManager.FindByLoginAsync(AbpWeChatAuthorizationConsts.ProviderKey, wechatOpenId.OpenId);
if(currentUser == null)
{
Logger.LogWarning("Invalid grant type: wechat openid: {0} not register", wechatOpenId.OpenId);
@ -92,20 +92,15 @@ namespace LINGYUN.Abp.IdentityServer.WeChatValidator
}
var sub = await UserManager.GetUserIdAsync(currentUser);
// 微信登录的用户写入token
currentUser.SetToken(WeChatAuthorizationConsts.ProviderKey, WeChatAuthorizationConsts.WeCahtCodeKey, wechatCode);
currentUser.SetToken(WeChatAuthorizationConsts.ProviderKey, WeChatAuthorizationConsts.WeCahtOpenIdKey, wechatOpenId.OpenId);
currentUser.SetToken(WeChatAuthorizationConsts.ProviderKey, WeChatAuthorizationConsts.WeCahtSessionKey, wechatOpenId.SessionKey);
var additionalClaims = new List<Claim>();
if (currentUser.TenantId.HasValue)
{
additionalClaims.Add(new Claim(AbpClaimTypes.TenantId, currentUser.TenantId?.ToString()));
}
additionalClaims.Add(new Claim(WeChatClaimTypes.OpenId, wechatOpenId.OpenId));
additionalClaims.Add(new Claim(AbpWeChatClaimTypes.OpenId, wechatOpenId.OpenId));
if (!wechatOpenId.UnionId.IsNullOrWhiteSpace())
{
additionalClaims.Add(new Claim(WeChatClaimTypes.UnionId, wechatOpenId.UnionId));
additionalClaims.Add(new Claim(AbpWeChatClaimTypes.UnionId, wechatOpenId.UnionId));
}
await EventService.RaiseAsync(new UserLoginSuccessEvent(currentUser.UserName, wechatOpenId.OpenId, null));

22
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChatValidator/LINGYUN/Abp/WeChat/Authorization/UserWeChatCodeFinder.cs

@ -1,15 +1,15 @@
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Identity;
namespace LINGYUN.Abp.WeChat.Authorization
{
// TODO: 真正的项目需要扩展Abp框架实体来关联微信
[Dependency(ServiceLifetime.Transient, ReplaceServices = true)]
[ExposeServices(typeof(IUserWeChatCodeFinder))]
public class UserWeChatCodeFinder : IUserWeChatCodeFinder
[ExposeServices(typeof(IUserWeChatOpenIdFinder))]
public class UserWeChatCodeFinder : IUserWeChatOpenIdFinder
{
protected IdentityUserManager UserManager { get; }
@ -23,18 +23,24 @@ namespace LINGYUN.Abp.WeChat.Authorization
{
var user = await UserManager.FindByIdAsync(userId.ToString());
var weChatCodeToken = user?.FindToken(WeChatAuthorizationConsts.ProviderKey, WeChatAuthorizationConsts.WeCahtCodeKey);
return weChatCodeToken?.Value ?? userId.ToString();
return GetUserOpenIdOrNull(user);
}
public virtual async Task<string> FindByUserNameAsync(string userName)
{
var user = await UserManager.FindByNameAsync(userName);
var weChatCodeToken = user?.FindToken(WeChatAuthorizationConsts.ProviderKey, WeChatAuthorizationConsts.WeCahtCodeKey);
return GetUserOpenIdOrNull(user);
}
protected string GetUserOpenIdOrNull(IdentityUser user)
{
// 微信扩展登录后openid存储在Login中
var userLogin = user?.Logins
.Where(login => login.LoginProvider == AbpWeChatAuthorizationConsts.ProviderKey)
.FirstOrDefault();
return weChatCodeToken?.Value ?? userName;
return userLogin?.ProviderKey;
}
}
}

316
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChatValidator/Microsoft/AspNetCore/Authentication/WeChat/WeChatAuthenticationHandler.cs

@ -0,0 +1,316 @@
using LINGYUN.Abp.WeChat.Authorization;
using Microsoft.AspNetCore.Authentication.OAuth;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Net.Http;
using System.Security.Claims;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Threading.Tasks;
using Volo.Abp.Caching;
namespace Microsoft.AspNetCore.Authentication.WeChat
{
public class WeChatAuthenticationHandler : OAuthHandler<WeChatAuthenticationOptions>
{
protected IDistributedCache<WeChatAuthenticationStateCacheItem> Cache { get; }
public WeChatAuthenticationHandler(
IDistributedCache<WeChatAuthenticationStateCacheItem> cache,
IOptionsMonitor<WeChatAuthenticationOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock)
: base(options, logger, encoder, clock)
{
Cache = cache;
}
protected override async Task<AuthenticationTicket> CreateTicketAsync(ClaimsIdentity identity, AuthenticationProperties properties, OAuthTokenResponse tokens)
{
var address = QueryHelpers.AddQueryString(Options.UserInformationEndpoint, new Dictionary<string, string>
{
["access_token"] = tokens.AccessToken,
["openid"] = tokens.Response.GetRootString("openid")
});
var response = await Backchannel.GetAsync(address);
if (!response.IsSuccessStatusCode)
{
Logger.LogError("An error occurred while retrieving the user profile: the remote server " +
"returned a {Status} response with the following payload: {Headers} {Body}.",
/* Status: */ response.StatusCode,
/* Headers: */ response.Headers.ToString(),
/* Body: */ await response.Content.ReadAsStringAsync());
throw new HttpRequestException("An error occurred while retrieving user information.");
}
var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
if (!string.IsNullOrEmpty(payload.GetRootString("errcode")))
{
Logger.LogError("An error occurred while retrieving the user profile: the remote server " +
"returned a {Status} response with the following payload: {Headers} {Body}.",
/* Status: */ response.StatusCode,
/* Headers: */ response.Headers.ToString(),
/* Body: */ await response.Content.ReadAsStringAsync());
throw new HttpRequestException("An error occurred while retrieving user information.");
}
var context = new OAuthCreatingTicketContext(new ClaimsPrincipal(identity), properties, Context, Scheme, Options, Backchannel, tokens, payload.RootElement);
context.RunClaimActions();
await Events.CreatingTicket(context);
// TODO: 此处通过唯一的 CorrelationId, 将 properties生成的State缓存删除
var state = Request.Query["state"];
var stateCacheKey = WeChatAuthenticationStateCacheItem.CalculateCacheKey(state, null);
await Cache.RemoveAsync(stateCacheKey, token: Context.RequestAborted);
return new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name);
}
/// <summary>
/// code换取access_token
/// </summary>
protected override async Task<OAuthTokenResponse> ExchangeCodeAsync(OAuthCodeExchangeContext context)
{
var address = QueryHelpers.AddQueryString(Options.TokenEndpoint, new Dictionary<string, string>()
{
["appid"] = Options.ClientId,
["secret"] = Options.ClientSecret,
["code"] = context.Code,
["grant_type"] = "authorization_code"
});
var response = await Backchannel.GetAsync(address);
if (!response.IsSuccessStatusCode)
{
Logger.LogError("An error occurred while retrieving an access token: the remote server " +
"returned a {Status} response with the following payload: {Headers} {Body}.",
/* Status: */ response.StatusCode,
/* Headers: */ response.Headers.ToString(),
/* Body: */ await response.Content.ReadAsStringAsync());
return OAuthTokenResponse.Failed(new Exception("An error occurred while retrieving an access token."));
}
var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
if (!string.IsNullOrEmpty(payload.GetRootString("errcode")))
{
Logger.LogError("An error occurred while retrieving an access token: the remote server " +
"returned a {Status} response with the following payload: {Headers} {Body}.",
/* Status: */ response.StatusCode,
/* Headers: */ response.Headers.ToString(),
/* Body: */ await response.Content.ReadAsStringAsync());
return OAuthTokenResponse.Failed(new Exception("An error occurred while retrieving an access token."));
}
return OAuthTokenResponse.Success(payload);
}
protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
{
await base.HandleChallengeAsync(properties);
// TODO: 此处已经生成唯一的 CorrelationId, 可以借此将 properties生成State之后再进行缓存
var state = properties.Items[".xsrf"];
var stateToken = Options.StateDataFormat.Protect(properties);
var stateCacheKey = WeChatAuthenticationStateCacheItem.CalculateCacheKey(state, null);
await Cache
.SetAsync(
stateCacheKey,
new WeChatAuthenticationStateCacheItem(stateToken),
new DistributedCacheEntryOptions
{
AbsoluteExpiration = Clock.UtcNow.AddMinutes(2) // TODO: 设定2分钟过期?
},
token: Context.RequestAborted);
}
/// <summary>
/// 构建用户授权地址
/// </summary>
protected override string BuildChallengeUrl(AuthenticationProperties properties, string redirectUri)
{
var state = properties.Items[".xsrf"];
var isWeChatBrewserRequest = IsWeChatBrowser();
var scope = isWeChatBrewserRequest
? AbpWeChatAuthorizationConsts.UserInfoScope
: FormatScope();
var endPoint = isWeChatBrewserRequest
? Options.AuthorizationEndpoint
: AbpWeChatAuthorizationConsts.QrConnectEndpoint;
var challengeUrl = QueryHelpers.AddQueryString(endPoint, new Dictionary<string, string>
{
["appid"] = Options.ClientId,
["redirect_uri"] = redirectUri,
["response_type"] = "code"
});
challengeUrl += $"&scope={scope}&state={state}";
return challengeUrl;
}
protected override async Task<HandleRequestResult> HandleRemoteAuthenticateAsync()
{
var query = Request.Query;
// TODO: 此处借用唯一的 CorrelationId, 将 properties生成的State缓存取出,进行解密
var state = query["state"];
var stateCacheKey = WeChatAuthenticationStateCacheItem.CalculateCacheKey(state, null);
var stateCacheItem = await Cache.GetAsync(stateCacheKey, token: Context.RequestAborted);
var properties = Options.StateDataFormat.Unprotect(stateCacheItem.State);
if (properties == null)
{
return HandleRequestResult.Fail("The oauth state was missing or invalid.");
}
// OAuth2 10.12 CSRF
if (!ValidateCorrelationId(properties))
{
return HandleRequestResult.Fail("Correlation failed.", properties);
}
var error = query["error"];
if (!StringValues.IsNullOrEmpty(error))
{
// Note: access_denied errors are special protocol errors indicating the user didn't
// approve the authorization demand requested by the remote authorization server.
// Since it's a frequent scenario (that is not caused by incorrect configuration),
// denied errors are handled differently using HandleAccessDeniedErrorAsync().
// Visit https://tools.ietf.org/html/rfc6749#section-4.1.2.1 for more information.
var errorDescription = query["error_description"];
var errorUri = query["error_uri"];
if (StringValues.Equals(error, "access_denied"))
{
var result = await HandleAccessDeniedErrorAsync(properties);
if (!result.None)
{
return result;
}
var deniedEx = new Exception("Access was denied by the resource owner or by the remote server.");
deniedEx.Data["error"] = error.ToString();
deniedEx.Data["error_description"] = errorDescription.ToString();
deniedEx.Data["error_uri"] = errorUri.ToString();
return HandleRequestResult.Fail(deniedEx, properties);
}
var failureMessage = new StringBuilder();
failureMessage.Append(error);
if (!StringValues.IsNullOrEmpty(errorDescription))
{
failureMessage.Append(";Description=").Append(errorDescription);
}
if (!StringValues.IsNullOrEmpty(errorUri))
{
failureMessage.Append(";Uri=").Append(errorUri);
}
var ex = new Exception(failureMessage.ToString());
ex.Data["error"] = error.ToString();
ex.Data["error_description"] = errorDescription.ToString();
ex.Data["error_uri"] = errorUri.ToString();
return HandleRequestResult.Fail(ex, properties);
}
var code = query["code"];
if (StringValues.IsNullOrEmpty(code))
{
return HandleRequestResult.Fail("Code was not found.", properties);
}
var codeExchangeContext = new OAuthCodeExchangeContext(properties, code, BuildRedirectUri(Options.CallbackPath));
using var tokens = await ExchangeCodeAsync(codeExchangeContext);
if (tokens.Error != null)
{
return HandleRequestResult.Fail(tokens.Error, properties);
}
if (string.IsNullOrEmpty(tokens.AccessToken))
{
return HandleRequestResult.Fail("Failed to retrieve access token.", properties);
}
var identity = new ClaimsIdentity(ClaimsIssuer);
if (Options.SaveTokens)
{
var authTokens = new List<AuthenticationToken>();
authTokens.Add(new AuthenticationToken { Name = "access_token", Value = tokens.AccessToken });
if (!string.IsNullOrEmpty(tokens.RefreshToken))
{
authTokens.Add(new AuthenticationToken { Name = "refresh_token", Value = tokens.RefreshToken });
}
if (!string.IsNullOrEmpty(tokens.TokenType))
{
authTokens.Add(new AuthenticationToken { Name = "token_type", Value = tokens.TokenType });
}
if (!string.IsNullOrEmpty(tokens.ExpiresIn))
{
int value;
if (int.TryParse(tokens.ExpiresIn, NumberStyles.Integer, CultureInfo.InvariantCulture, out value))
{
// https://www.w3.org/TR/xmlschema-2/#dateTime
// https://msdn.microsoft.com/en-us/library/az4se3k1(v=vs.110).aspx
var expiresAt = Clock.UtcNow + TimeSpan.FromSeconds(value);
authTokens.Add(new AuthenticationToken
{
Name = "expires_at",
Value = expiresAt.ToString("o", CultureInfo.InvariantCulture)
});
}
}
properties.StoreTokens(authTokens);
}
var ticket = await CreateTicketAsync(identity, properties, tokens);
if (ticket != null)
{
return HandleRequestResult.Success(ticket);
}
else
{
return HandleRequestResult.Fail("Failed to retrieve user information from remote server.", properties);
}
}
protected override string FormatScope()
{
return string.Join(",", Options.Scope);
}
protected virtual bool IsWeChatBrowser()
{
var userAgent = Request.Headers[HeaderNames.UserAgent].ToString();
return userAgent.Contains("micromessenger", StringComparison.InvariantCultureIgnoreCase);
}
}
}

54
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChatValidator/Microsoft/AspNetCore/Authentication/WeChat/WeChatAuthenticationOptions.cs

@ -0,0 +1,54 @@
using LINGYUN.Abp.WeChat.Authorization;
using Microsoft.AspNetCore.Authentication.OAuth;
using Microsoft.AspNetCore.Http;
using System.Security.Claims;
using System.Text.Json;
namespace Microsoft.AspNetCore.Authentication.WeChat
{
public class WeChatAuthenticationOptions : OAuthOptions
{
public string AppId
{
get => ClientId;
set => ClientId = value;
}
public string AppSecret
{
get => ClientSecret;
set => ClientSecret = value;
}
public WeChatAuthenticationOptions()
{
ClaimsIssuer = AbpWeChatAuthorizationConsts.ProviderKey;
CallbackPath = new PathString(AbpWeChatAuthorizationConsts.CallbackPath);
AuthorizationEndpoint = AbpWeChatAuthorizationConsts.AuthorizationEndpoint;
TokenEndpoint = AbpWeChatAuthorizationConsts.TokenEndpoint;
UserInformationEndpoint = AbpWeChatAuthorizationConsts.UserInformationEndpoint;
Scope.Add(AbpWeChatAuthorizationConsts.LoginScope);
Scope.Add(AbpWeChatAuthorizationConsts.UserInfoScope);
// 这个原始的属性一定要写进去,框架与UserLogin.ProviderKey进行关联判断是否绑定微信
ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "openid");
ClaimActions.MapJsonKey(ClaimTypes.Name, "nickname");
// 把自定义的身份标识写进令牌
ClaimActions.MapJsonKey(AbpWeChatClaimTypes.OpenId, "openid");
ClaimActions.MapJsonKey(AbpWeChatClaimTypes.UnionId, "unionid"); // TODO: 可用作tenant对比?
ClaimActions.MapJsonKey(AbpWeChatClaimTypes.NickName, "nickname");
ClaimActions.MapJsonKey(AbpWeChatClaimTypes.Sex, "sex", ClaimValueTypes.Integer);
ClaimActions.MapJsonKey(AbpWeChatClaimTypes.Country, "country");
ClaimActions.MapJsonKey(AbpWeChatClaimTypes.Province, "province");
ClaimActions.MapJsonKey(AbpWeChatClaimTypes.City, "city");
ClaimActions.MapJsonKey(AbpWeChatClaimTypes.AvatarUrl, "headimgurl");
ClaimActions.MapCustomJson(AbpWeChatClaimTypes.Privilege, user =>
{
return string.Join(",", user.GetStrings("privilege"));
});
}
}
}

18
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChatValidator/Microsoft/AspNetCore/Authentication/WeChat/WeChatAuthenticationStateCacheItem.cs

@ -0,0 +1,18 @@
namespace Microsoft.AspNetCore.Authentication.WeChat
{
public class WeChatAuthenticationStateCacheItem
{
public string State { get; set; }
public WeChatAuthenticationStateCacheItem() { }
public WeChatAuthenticationStateCacheItem(string state)
{
State = state;
}
public static string CalculateCacheKey(string correlationId, string purpose)
{
return $"ci:{correlationId};p:{purpose ?? "null"}";
}
}
}

64
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChatValidator/Microsoft/AspNetCore/Authentication/WeChatAuthenticationExtensions.cs

@ -0,0 +1,64 @@
using LINGYUN.Abp.WeChat.Authorization;
using Microsoft.AspNetCore.Authentication.WeChat;
using Microsoft.Extensions.DependencyInjection;
using System;
namespace Microsoft.AspNetCore.Authentication
{
public static class WeChatAuthenticationExtensions
{
/// <summary>
/// </summary>
public static AuthenticationBuilder AddWeChat(
this AuthenticationBuilder builder)
{
return builder
.AddWeChat(
AbpWeChatAuthorizationConsts.AuthenticationScheme,
AbpWeChatAuthorizationConsts.DisplayName,
options => { });
}
/// <summary>
/// </summary>
public static AuthenticationBuilder AddWeChat(
this AuthenticationBuilder builder,
Action<WeChatAuthenticationOptions> configureOptions)
{
return builder
.AddWeChat(
AbpWeChatAuthorizationConsts.AuthenticationScheme,
AbpWeChatAuthorizationConsts.DisplayName,
configureOptions);
}
/// <summary>
/// </summary>
public static AuthenticationBuilder AddWeChat(
this AuthenticationBuilder builder,
string authenticationScheme,
Action<WeChatAuthenticationOptions> configureOptions)
{
return builder
.AddWeChat(
authenticationScheme,
AbpWeChatAuthorizationConsts.DisplayName,
configureOptions);
}
/// <summary>
/// </summary>
public static AuthenticationBuilder AddWeChat(
this AuthenticationBuilder builder,
string authenticationScheme,
string displayName,
Action<WeChatAuthenticationOptions> configureOptions)
{
return builder
.AddOAuth<WeChatAuthenticationOptions, WeChatAuthenticationHandler>(
authenticationScheme,
displayName,
configureOptions);
}
}
}

16
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChatValidator/System/BytesExtensions.cs

@ -0,0 +1,16 @@
using System.Security.Cryptography;
namespace System
{
internal static class BytesExtensions
{
public static byte[] Sha1(this byte[] data)
{
using (var sha = SHA1.Create())
{
var hashBytes = sha.ComputeHash(data);
return hashBytes;
}
}
}
}

17
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChatValidator/System/StringExtensions.cs

@ -0,0 +1,17 @@
using System.Security.Cryptography;
using System.Text;
namespace System
{
internal static class StringExtensions
{
public static byte[] Sha1(this string str)
{
using (var sha = SHA1.Create())
{
var hashBytes = sha.ComputeHash(Encoding.ASCII.GetBytes(str));
return hashBytes;
}
}
}
}

63
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChatValidator/System/Text/Json/JsonElementExtensions.cs

@ -0,0 +1,63 @@
using System.Collections.Generic;
namespace System.Text.Json
{
internal static class JsonElementExtensions
{
public static IEnumerable<string> GetRootStrings(this JsonDocument json, string key)
{
return json.RootElement.GetStrings(key);
}
public static IEnumerable<string> GetStrings(this JsonElement json, string key)
{
var result = new List<string>();
if (json.TryGetProperty(key, out JsonElement property) && property.ValueKind == JsonValueKind.Array)
{
foreach (var jsonProp in property.EnumerateArray())
{
result.Add(jsonProp.GetString());
}
}
return result;
}
public static string GetRootString(this JsonDocument json, string key, string defaultValue = "")
{
if (json.RootElement.TryGetProperty(key, out JsonElement property))
{
return property.GetString();
}
return defaultValue;
}
public static string GetString(this JsonElement json, string key, string defaultValue = "")
{
if (json.TryGetProperty(key, out JsonElement property))
{
return property.GetString();
}
return defaultValue;
}
public static int GetRootInt32(this JsonDocument json, string key, int defaultValue = 0)
{
if (json.RootElement.TryGetProperty(key, out JsonElement property) && property.TryGetInt32(out int value))
{
return value;
}
return defaultValue;
}
public static int GetInt32(this JsonElement json, string key, int defaultValue = 0)
{
if (json.TryGetProperty(key, out JsonElement property) && property.TryGetInt32(out int value))
{
return value;
}
return defaultValue;
}
}
}

16
aspnet-core/modules/wechat/LINGYUN.Abp.Notifications.WeChat/LINGYUN/Abp/Notifications/WeChat/WeApp/WeChatWeAppNotificationPublishProvider.cs

@ -20,8 +20,8 @@ namespace LINGYUN.Abp.Notifications.WeChat.WeApp
private IFeatureChecker _featureChecker;
protected IFeatureChecker FeatureChecker => LazyGetRequiredService(ref _featureChecker);
private IWeChatOpenIdFinder _weChatOpenIdFinder;
protected IWeChatOpenIdFinder WeChatOpenIdFinder => LazyGetRequiredService(ref _weChatOpenIdFinder);
private IUserWeChatOpenIdFinder _userWeChatOpenIdFinder;
protected IUserWeChatOpenIdFinder UserWeChatOpenIdFinder => LazyGetRequiredService(ref _userWeChatOpenIdFinder);
protected IWeChatWeAppNotificationSender NotificationSender { get; }
protected AbpWeChatWeAppNotificationOptions Options { get; }
@ -79,15 +79,13 @@ namespace LINGYUN.Abp.Notifications.WeChat.WeApp
var weAppLang = GetOrDefault(notification.Data, "WeAppLanguage", Options.DefaultWeAppLanguage);
Logger.LogDebug($"Get wechat weapp language: {weAppLang ?? null}");
// TODO: 如果微信端发布通知,请组装好 wx-code 字段在通知数据内容里面
string weChatCode = GetOrDefault(notification.Data, "wx-code", "");
// TODO: 如果微信端发布通知,请组装好 openid 字段在通知数据内容里面
string weChatCode = GetOrDefault(notification.Data, AbpWeChatClaimTypes.OpenId, "");
WeChatOpenId openId = weChatCode.IsNullOrWhiteSpace()
? await WeChatOpenIdFinder.FindByUserNameAsync(identifier.UserName) // 按照实际情况,需要自行实现 IUserWeChatCodeFinder 接口以获取微信Code,然后通过Code来获取OpenId
: await WeChatOpenIdFinder.FindAsync(weChatCode);
var openId = !weChatCode.IsNullOrWhiteSpace() ? weChatCode
: await UserWeChatOpenIdFinder.FindByUserIdAsync(identifier.UserId);
var weChatWeAppNotificationData = new WeChatWeAppSendNotificationData(openId.OpenId,
var weChatWeAppNotificationData = new WeChatWeAppSendNotificationData(openId,
templateId, redirect, weAppState, weAppLang);
// 写入模板数据

72
aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/AbpWeChatAuthorizationConsts.cs

@ -0,0 +1,72 @@
namespace LINGYUN.Abp.WeChat.Authorization
{
/// <summary>
/// 与微信认证相关的静态(可变)常量
/// </summary>
public static class AbpWeChatAuthorizationConsts
{
/// <summary>
/// 微信授权名称
/// </summary>
public const string AuthenticationScheme = "WeChat";
/// <summary>
/// 微信授权显示名称
/// </summary>
public static string DisplayName = "WeChat";
/// <summary>
/// 微信个人信息标识
/// </summary>
public static string ProfileKey { get; set; } = "wechat.profile";
/// <summary>
/// 微信提供者标识
/// </summary>
public static string ProviderKey { get; set; } = AuthenticationScheme;
/// <summary>
/// 回调地址
/// </summary>
public static string CallbackPath { get; set; } = "/signin-wechat";
/// <summary>
/// 微信客户端外的网页登录
/// </summary>
public const string QrConnectEndpoint = "https://open.weixin.qq.com/connect/qrconnect";
/// <summary>
/// 微信客户端内的网页登录
/// </summary>
public const string AuthorizationEndpoint = "https://open.weixin.qq.com/connect/oauth2/authorize";
/// <summary>
/// 用户允许授权后通过返回的code换取access_token地址
/// </summary>
public const string TokenEndpoint = "https://api.weixin.qq.com/sns/oauth2/access_token";
/// <summary>
/// 使用access_token获取用户个人信息地址
/// </summary>
public const string UserInformationEndpoint = "https://api.weixin.qq.com/sns/userinfo";
/// <summary>
/// 弹出授权页面,可通过openid拿到昵称、性别、所在地。
/// 并且, 即使在未关注的情况下,只要用户授权,也能获取其信息
/// <br />
/// <br />
/// 详询: <see cref="https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html"/>
/// </summary>
/// <remarks>
/// 以snsapi_userinfo为scope发起的网页授权,是用来获取用户的基本信息的。
/// 但这种授权需要用户手动同意,并且由于用户同意过,所以无须关注,就可在授权后获取该用户的基本信息
/// </remarks>
public const string UserInfoScope = "snsapi_userinfo";
/// <summary>
/// 不弹出授权页面,直接跳转,只能获取用户openid
/// <br />
/// <br />
/// 详询: <see cref="https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html"/>
/// </summary>
/// <remarks>
/// 以snsapi_base为scope发起的网页授权,是用来获取进入页面的用户的openid的,并且是静默授权并自动跳转到回调页的。
/// 用户感知的就是直接进入了回调页(往往是业务页面)
/// </remarks>
public const string LoginScope = "snsapi_login";
}
}

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

@ -16,7 +16,7 @@ namespace LINGYUN.Abp.WeChat.Authorization
public override void ConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
Configure<AbpWeChatOptions>(configuration.GetSection("WeChat:Auth"));
Configure<AbpWeChatAuthorizationOptions>(configuration.GetSection("WeChat:Auth"));
context.Services.AddHttpClient("WeChatRequestClient", options =>
{

2
aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/AbpWeChatOptions.cs → aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/AbpWeChatAuthorizationOptions.cs

@ -1,6 +1,6 @@
namespace LINGYUN.Abp.WeChat.Authorization
{
public class AbpWeChatOptions
public class AbpWeChatAuthorizationOptions
{
public string AppId { get; set; }
public string AppSecret { get; set; }

48
aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/AbpWeChatClaimTypes.cs

@ -0,0 +1,48 @@
namespace LINGYUN.Abp.WeChat.Authorization
{
/// <summary>
/// 微信认证身份类型,可以像 <see cref="Volo.Abp.Security.Claims.AbpClaimTypes"/> 自行配置
/// <br />
/// See: <see cref="https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html"/>
/// </summary>
public class AbpWeChatClaimTypes
{
/// <summary>
/// 用户的唯一标识
/// </summary>
public static string OpenId { get; set; } = "wx-openid"; // 可变更
/// <summary>
/// 只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段。
/// </summary>
public static string UnionId { get; set; } = "wx-unionid"; //可变更
/// <summary>
/// 用户昵称
/// </summary>
public static string NickName { get; set; } = "nickname";
/// <summary>
/// 用户的性别,值为1时是男性,值为2时是女性,值为0时是未知
/// </summary>
public static string Sex { get; set; } = "sex";
/// <summary>
/// 国家,如中国为CN
/// </summary>
public static string Country { get; set; } = "country";
/// <summary>
/// 用户个人资料填写的省份
/// </summary>
public static string Province { get; set; } = "province";
/// <summary>
/// 普通用户个人资料填写的城市
/// </summary>
public static string City { get; set; } = "city";
/// <summary>
/// 用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空。
/// 若用户更换头像,原有头像URL将失效。
/// </summary>
public static string AvatarUrl { get; set; } = "avatar";
/// <summary>
/// 用户特权信息,json 数组,如微信沃卡用户为(chinaunicom)
/// </summary>
public static string Privilege { get; set; } = "privilege";
}
}

2
aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/OpenId/IUserWeChatCodeFinder.cs → aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/OpenId/IUserWeChatOpenIdFinder.cs

@ -3,7 +3,7 @@ using System.Threading.Tasks;
namespace LINGYUN.Abp.WeChat.Authorization
{
public interface IUserWeChatCodeFinder
public interface IUserWeChatOpenIdFinder
{
Task<string> FindByUserIdAsync(Guid userId);

4
aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/OpenId/IWeChatOpenIdFinder.cs

@ -6,9 +6,5 @@ namespace LINGYUN.Abp.WeChat.Authorization
public interface IWeChatOpenIdFinder
{
Task<WeChatOpenId> FindAsync(string code);
Task<WeChatOpenId> FindByUserIdAsync(Guid userId);
Task<WeChatOpenId> FindByUserNameAsync(string userName);
}
}

6
aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/OpenId/NullUserWeChatCodeFinder.cs → aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/OpenId/NullUserWeChatOpenIdFinder.cs

@ -4,16 +4,16 @@ using Volo.Abp.DependencyInjection;
namespace LINGYUN.Abp.WeChat.Authorization
{
public class NullUserWeChatCodeFinder : IUserWeChatCodeFinder, ISingletonDependency
public class NullUserWeChatOpenIdFinder : IUserWeChatOpenIdFinder, ISingletonDependency
{
public Task<string> FindByUserIdAsync(Guid userId)
{
return Task.FromResult(userId.ToString());
return Task.FromResult("");
}
public Task<string> FindByUserNameAsync(string userName)
{
return Task.FromResult(userName);
return Task.FromResult("");
}
}
}

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

@ -18,23 +18,20 @@ namespace LINGYUN.Abp.WeChat.Authorization
public class WeChatOpenIdFinder : IWeChatOpenIdFinder
{
public ILogger<WeChatOpenIdFinder> Logger { get; set; }
protected AbpWeChatOptions Options { get; }
protected AbpWeChatAuthorizationOptions Options { get; }
protected ICurrentTenant CurrentTenant { get; }
protected IHttpClientFactory HttpClientFactory { get; }
protected IJsonSerializer JsonSerializer { get; }
protected IUserWeChatCodeFinder UserWeChatCodeFinder { get; }
protected IDistributedCache<WeChatOpenIdCacheItem> Cache { get; }
public WeChatOpenIdFinder(
ICurrentTenant currentTenant,
IJsonSerializer jsonSerializer,
IUserWeChatCodeFinder userWeChatCodeFinder,
IHttpClientFactory httpClientFactory,
IOptions<AbpWeChatOptions> options,
IOptions<AbpWeChatAuthorizationOptions> options,
IDistributedCache<WeChatOpenIdCacheItem> cache)
{
CurrentTenant = currentTenant;
JsonSerializer = jsonSerializer;
UserWeChatCodeFinder = userWeChatCodeFinder;
HttpClientFactory = httpClientFactory;
Cache = cache;
@ -49,24 +46,6 @@ namespace LINGYUN.Abp.WeChat.Authorization
return (await GetCacheItemAsync(code)).WeChatOpenId;
}
public virtual async Task<WeChatOpenId> FindByUserIdAsync(Guid userId)
{
var code = await UserWeChatCodeFinder.FindByUserIdAsync(userId);
// TODO: 如果需要获取SessionKey的话呢,需要再以openid作为标识来缓存一下吗
// 或者前端保存code,通过传递code来获取
return (await GetCacheItemAsync(code)).WeChatOpenId;
}
public virtual async Task<WeChatOpenId> FindByUserNameAsync(string userName)
{
var code = await UserWeChatCodeFinder.FindByUserNameAsync(userName);
// TODO: 如果需要获取SessionKey的话呢,需要再以openid作为标识来缓存一下吗
// 或者前端保存code,通过传递code来获取
return (await GetCacheItemAsync(code)).WeChatOpenId;
}
protected virtual async Task<WeChatOpenIdCacheItem> GetCacheItemAsync(string code)
{
var cacheKey = WeChatOpenIdCacheItem.CalculateCacheKey(code);

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

@ -18,11 +18,11 @@ namespace LINGYUN.Abp.WeChat.Authorization
protected IHttpClientFactory HttpClientFactory { get; }
protected IJsonSerializer JsonSerializer { get; }
protected IDistributedCache<WeChatTokenCacheItem> Cache { get; }
protected AbpWeChatOptions Options { get; }
protected AbpWeChatAuthorizationOptions Options { get; }
public WeChatTokenProvider(
IJsonSerializer jsonSerializer,
IHttpClientFactory httpClientFactory,
IOptions<AbpWeChatOptions> options,
IOptions<AbpWeChatAuthorizationOptions> options,
IDistributedCache<WeChatTokenCacheItem> cache)
{
JsonSerializer = jsonSerializer;

22
aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Authorization/LINGYUN/Abp/WeChat/Authorization/WeChatAuthorizationConsts.cs

@ -1,22 +0,0 @@
namespace LINGYUN.Abp.WeChat.Authorization
{
public class WeChatAuthorizationConsts
{
/// <summary>
/// 微信提供者标识
/// </summary>
public static string ProviderKey { get; set; } = "WeChat";
/// <summary>
/// 微信Code参数名称
/// </summary>
public static string WeCahtCodeKey { get; set; } = "wx-code";
/// <summary>
/// 微信OpenId参数名称
/// </summary>
public static string WeCahtOpenIdKey { get; set; } = "wx-open-id";
/// <summary>
/// 微信SessionKey参数名称
/// </summary>
public static string WeCahtSessionKey { get; set; } = "wx-session-key";
}
}

8
aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Authorization/Volo/Abp/Security/Claims/WeChatClaimTypes.cs

@ -1,8 +0,0 @@
namespace Volo.Abp.Security.Claims
{
public class WeChatClaimTypes
{
public static string OpenId { get; set; } = "wx-openid";
public static string UnionId { get; set; } = "wx-unionid";
}
}

6
aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Authorization/Volo/Abp/Users/CurrentUserExtensions.cs

@ -1,4 +1,4 @@
using Volo.Abp.Security.Claims;
using LINGYUN.Abp.WeChat.Authorization;
namespace Volo.Abp.Users
{
@ -11,7 +11,7 @@ namespace Volo.Abp.Users
/// <returns></returns>
public static string FindWeChatOpenId(this ICurrentUser currentUser)
{
var weChatClaim = currentUser.FindClaim(WeChatClaimTypes.OpenId);
var weChatClaim = currentUser.FindClaim(AbpWeChatClaimTypes.OpenId);
if (weChatClaim == null)
{
return null;
@ -27,7 +27,7 @@ namespace Volo.Abp.Users
/// <returns></returns>
public static string FindWeChatUnionId(this ICurrentUser currentUser)
{
var weChatClaim = currentUser.FindClaim(WeChatClaimTypes.UnionId);
var weChatClaim = currentUser.FindClaim(AbpWeChatClaimTypes.UnionId);
if (weChatClaim == null)
{
return null;

9
aspnet-core/services/account/AuthServer.Host/AuthIdentityServerModule.cs

@ -3,6 +3,7 @@ using LINGYUN.Abp.EventBus.CAP;
using LINGYUN.Abp.IdentityServer;
using LINGYUN.Abp.MultiTenancy.DbFinder;
using LINGYUN.Abp.PermissionManagement.Identity;
using Microsoft.AspNetCore.Authentication.WeChat;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.DataProtection;
@ -114,6 +115,11 @@ namespace AuthServer.Host
options.InitVectorBytes = encryptionConfiguration.GetSection("InitVector").Exists()
? Encoding.ASCII.GetBytes(encryptionConfiguration["InitVector"])
: options.InitVectorBytes;
var keySizeConfig = encryptionConfiguration.GetSection("Keysize");
options.Keysize = keySizeConfig.Exists()
? keySizeConfig.Get<int>()
: options.Keysize;
}
});
@ -216,10 +222,11 @@ namespace AuthServer.Host
app.UseVirtualFiles();
app.UseRouting();
app.UseCors(DefaultCorsPolicyName);
app.UseWeChatSignature();
app.UseMultiTenancy();
app.UseAuthentication();
app.UseJwtTokenMiddleware();
app.UseAbpClaimsMap();
app.UseMultiTenancy();
app.UseAbpRequestLocalization();
app.UseIdentityServer();
app.UseAuthorization();

15
aspnet-core/services/account/AuthServer.Host/DataSeeder/IdentityServerDataSeedContributor.cs

@ -1,4 +1,5 @@
using Microsoft.Extensions.Configuration;
using LINGYUN.Abp.IdentityServer;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.IO;
@ -13,7 +14,6 @@ using Volo.Abp.IdentityServer.ApiResources;
using Volo.Abp.IdentityServer.Clients;
using Volo.Abp.IdentityServer.IdentityResources;
using Volo.Abp.PermissionManagement;
using Volo.Abp.Security.Claims;
using Volo.Abp.Uow;
namespace AuthServer.DataSeeder
@ -25,6 +25,7 @@ namespace AuthServer.DataSeeder
private readonly IIdentityResourceDataSeeder _identityResourceDataSeeder;
private readonly IIdentityClaimTypeRepository _identityClaimTypeRepository;
private readonly IPermissionDataSeeder _permissionDataSeeder;
private readonly IWeChatResourceDataSeeder _weChatResourceDataSeeder;
private readonly IGuidGenerator _guidGenerator;
private readonly IConfiguration _configuration;
@ -32,6 +33,7 @@ namespace AuthServer.DataSeeder
IClientRepository clientRepository,
IPermissionDataSeeder permissionDataSeeder,
IApiResourceRepository apiResourceRepository,
IWeChatResourceDataSeeder weChatResourceDataSeeder,
IIdentityResourceDataSeeder identityResourceDataSeeder,
IIdentityClaimTypeRepository identityClaimTypeRepository,
IGuidGenerator guidGenerator)
@ -40,6 +42,7 @@ namespace AuthServer.DataSeeder
_permissionDataSeeder = permissionDataSeeder;
_apiResourceRepository = apiResourceRepository;
_identityClaimTypeRepository = identityClaimTypeRepository;
_weChatResourceDataSeeder = weChatResourceDataSeeder;
_identityResourceDataSeeder = identityResourceDataSeeder;
_guidGenerator = guidGenerator;
var env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production";
@ -62,13 +65,7 @@ namespace AuthServer.DataSeeder
private async Task CreateWeChatClaimTypeAsync()
{
if (!await _identityClaimTypeRepository.AnyAsync(WeChatClaimTypes.OpenId))
{
var wechatClaimType = new IdentityClaimType(_guidGenerator.Create(), WeChatClaimTypes.OpenId,
isStatic: true, description: "适用于微信认证的用户标识");
await _identityClaimTypeRepository.InsertAsync(wechatClaimType);
}
await _weChatResourceDataSeeder.CreateStandardResourcesAsync();
}
private async Task CreateApiResourcesAsync()

10
aspnet-core/services/account/AuthServer.Host/Microsoft/Extensions/DependencyInjection/SameSiteCookiesServiceCollectionExtensions.cs

@ -146,7 +146,13 @@ namespace Microsoft.Extensions.DependencyInjection
{
try
{
return Convert.ToInt32(userAgent.Split("Chrome/")[1].Split('.')[0]);
string version = "0";
var chromeAgents = userAgent.Split("Chrome/");
if (chromeAgents.Length > 1 && chromeAgents[1].Split('.').Length > 0)
{
version = chromeAgents[1].Split('.')[0];
}
return Convert.ToInt32(version);
}
catch (Exception)
{
@ -154,4 +160,4 @@ namespace Microsoft.Extensions.DependencyInjection
}
}
}
}
}
Loading…
Cancel
Save