Browse Source

feat: 增加企业微信登录

pull/1133/head
colin 11 months ago
parent
commit
d720281bdd
  1. 4
      aspnet-core/framework/console/LINGYUN.Abp.Encryption.Console/AbpEncryptionConsoleModule.cs
  2. 47
      aspnet-core/framework/console/LINGYUN.Abp.Encryption.Console/Program.cs
  3. 4
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Application.Contracts/LINGYUN/Abp/WeChat/Work/Authorize/IWeChatWorkAuthorizeAppService.cs
  4. 6
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Application.Contracts/LINGYUN/Abp/WeChat/Work/Message/IWeChatWorkMessageAppService.cs
  5. 13
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Application/LINGYUN/Abp/WeChat/Work/Authorize/WeChatWorkAuthorizeAppService.cs
  6. 52
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Application/LINGYUN/Abp/WeChat/Work/Message/WeChatWorkMessageAppService.cs
  7. 3
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.AspNetCore/FodyWeavers.xml
  8. 30
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.AspNetCore/FodyWeavers.xsd
  9. 24
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.AspNetCore/LINGYUN.Abp.WeChat.Work.AspNetCore.csproj
  10. 11
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.AspNetCore/LINGYUN/Abp/WeChat/Work/AspNetCore/AbpWeChatWorkAspNetCoreModule.cs
  11. 40
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.AspNetCore/Microsoft/AspNetCore/Authentication/WeChat/Work/WeChatWorkOAuthConsts.cs
  12. 329
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.AspNetCore/Microsoft/AspNetCore/Authentication/WeChat/Work/WeChatWorkOAuthHandler.cs
  13. 43
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.AspNetCore/Microsoft/AspNetCore/Authentication/WeChat/Work/WeChatWorkOAuthOptions.cs
  14. 63
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.AspNetCore/Microsoft/AspNetCore/Authentication/WeChatWorkAuthenticationExtensions.cs
  15. 16
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.AspNetCore/System/BytesExtensions.cs
  16. 17
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.AspNetCore/System/StringExtensions.cs
  17. 63
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.AspNetCore/System/Text/Json/JsonElementExtensions.cs
  18. 16
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.HttpApi/LINGYUN/Abp/WeChat/Work/Authorize/WeChatWorkAuthorizeController.cs
  19. 10
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.HttpApi/LINGYUN/Abp/WeChat/Work/Message/WeChatWorkMessageController.cs
  20. 4
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/AbpWeChatWorkGlobalConsts.cs
  21. 3
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/AbpWeChatWorkModule.cs
  22. 4
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Authorize/IWeChatWorkAuthorizeGenerator.cs
  23. 2
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Authorize/IWeChatWorkUserFinder.cs
  24. 16
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Authorize/WeChatWorkAuthorizeGenerator.cs
  25. 6
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Authorize/WeChatWorkUserFinder.cs
  26. 6
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Chat/WeChatWorkAppChatManager.cs
  27. 12
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Localization/Resources/en.json
  28. 8
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Localization/Resources/zh-Hans.json
  29. 6
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Media/IWeChatWorkMediaProvider.cs
  30. 9
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Media/WeChatWorkMediaProvider.cs
  31. 3
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/IWeChatWorkMessageManager.cs
  32. 4
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/WeChatWorkMessageManager.cs
  33. 4
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/WeChatWorkMessageSender.cs
  34. 26
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/NumberToStringConverter.cs
  35. 16
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/Claims/AbpWeChatWorkClaimTypes.cs
  36. 35
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/WeChatWorkCryptoConfiguration.cs
  37. 12
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/WeChatWorkCryptoConfigurationDictionary.cs
  38. 41
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Settings/WeChatWorkSettingDefinitionProvider.cs
  39. 20
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Settings/WeChatWorkSettingNames.cs
  40. 5
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Token/IWeChatWorkTokenProvider.cs
  41. 4
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Token/Models/WeChatWorkTokenResponse.cs
  42. 68
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Token/WeChatWorkTokenProvider.cs
  43. 43
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/WeChatWorkApplicationConfiguration.cs
  44. 14
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/WeChatWorkApplicationConfigurationDictionary.cs
  45. 11
      aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/WeChatWorkOptions.cs
  46. 272
      aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore/DataSeeder/ClientDataSeederContributor.cs
  47. 1
      aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore/LY.MicroService.Applications.Single.EntityFrameworkCore.csproj
  48. 2
      aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore/SingleMigrationsDbContext.cs
  49. 2
      aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore/SingleMigrationsEntityFrameworkCoreModule.cs
  50. 5
      aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Areas/Account/Controllers/Models/QrCodeUserInfoResult.cs
  51. 53
      aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Areas/Account/Controllers/QrCodeLoginController.cs
  52. 7
      aspnet-core/modules/gdpr/LINGYUN.Abp.Gdpr.Domain.Identity/LINGYUN/Abp/Gdpr/Identity/AbpGdprIdentityUserAccountProvider.cs
  53. 1
      aspnet-core/modules/identity/LINGYUN.Abp.Identity.QrCode/LINGYUN/Abp/Identity/QrCode/QrCodeCacheItem.cs
  54. 1
      aspnet-core/modules/identity/LINGYUN.Abp.Identity.QrCode/LINGYUN/Abp/Identity/QrCode/QrCodeInfo.cs
  55. 7
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat.Work/LINGYUN/Abp/IdentityServer/WeChat/Work/WeChatWorkGrantValidator.cs
  56. 7
      aspnet-core/modules/openIddict/LINGYUN.Abp.OpenIddict.WeChat.Work/LINGYUN/Abp/OpenIddict/WeChat/Work/WeChatWorkTokenExtensionGrant.cs
  57. 54
      aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.WeChat.Work/LINGYUN/Abp/Notifications/WeChat/Work/WeChatWorkNotificationPublishProvider.cs
  58. 91
      aspnet-core/services/LY.MicroService.Applications.Single/Authentication/AbpCookieAuthenticationHandler.cs
  59. 16
      aspnet-core/services/LY.MicroService.Applications.Single/DataSeeder/DataSeederWorker.cs
  60. 14
      aspnet-core/services/LY.MicroService.Applications.Single/GlobalUsings.cs
  61. 16
      aspnet-core/services/LY.MicroService.Applications.Single/IdentityResources/CustomIdentityResources.cs
  62. 14
      aspnet-core/services/LY.MicroService.Applications.Single/LY.MicroService.Applications.Single.csproj
  63. 131
      aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.Configure.cs
  64. 15
      aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.cs
  65. 8
      aspnet-core/services/LY.MicroService.Applications.Single/Program.cs
  66. 14
      aspnet-core/services/LY.MicroService.AuthServer/AuthServerModule.Configure.cs
  67. 2
      aspnet-core/services/LY.MicroService.AuthServer/AuthServerModule.cs
  68. 89
      aspnet-core/services/LY.MicroService.AuthServer/Authentication/AbpCookieAuthenticationHandler.cs
  69. 1
      aspnet-core/services/LY.MicroService.AuthServer/LY.MicroService.AuthServer.csproj

4
aspnet-core/framework/console/LINGYUN.Abp.Encryption.Console/AbpEncryptionConsoleModule.cs

@ -1,11 +1,13 @@
using LINGYUN.Abp.Encryption.SM4;
using System.Text;
using Volo.Abp.Modularity;
using Volo.Abp.Security;
using Volo.Abp.Security.Encryption;
namespace LINGYUN.Abp.Encryption.Console;
[DependsOn(typeof(AbpEncryptionSM4Module))]
//[DependsOn(typeof(AbpEncryptionSM4Module))]
[DependsOn(typeof(AbpSecurityModule))]
public class AbpEncryptionConsoleModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)

47
aspnet-core/framework/console/LINGYUN.Abp.Encryption.Console/Program.cs

@ -16,27 +16,40 @@ namespace LINGYUN.Abp.Encryption.Console
application.Initialize();
WriteLine("D:解密 E:加密");
var opt = ReadLine();
bool en = false;
if ("E".Equals(opt, StringComparison.InvariantCultureIgnoreCase))
{
en = true;
WriteLine("请输入需要加密的字符串");
}
else
while (true)
{
WriteLine("请输入需要解密的字符串");
WriteLine("D:解密 E:加密 Q: 退出");
var opt = ReadLine();
var en = false;
if (opt == "Q")
{
break;
}
if ("E".Equals(opt, StringComparison.InvariantCultureIgnoreCase))
{
en = true;
WriteLine("请输入需要加密的字符串");
}
else
{
WriteLine("请输入需要解密的字符串");
}
var sourceChr = ReadLine();
var encryptionService = application.ServiceProvider.GetRequiredService<IStringEncryptionService>();
WriteLine(en ? encryptionService.Encrypt(sourceChr) : encryptionService.Decrypt(sourceChr));
WriteLine("按任意键继续");
ReadKey();
Clear();
}
var sourceChr = ReadLine();
var encryptionService = application.ServiceProvider.GetRequiredService<IStringEncryptionService>();
WriteLine(en ? encryptionService.Encrypt(sourceChr) : encryptionService.Decrypt(sourceChr));
application.Shutdown();
ReadKey();
}
}
}

4
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Application.Contracts/LINGYUN/Abp/WeChat/Work/Authorize/IWeChatWorkAuthorizeAppService.cs

@ -5,13 +5,11 @@ namespace LINGYUN.Abp.WeChat.Work.Authorize;
public interface IWeChatWorkAuthorizeAppService : IApplicationService
{
Task<string> GenerateOAuth2AuthorizeAsync(
string agentid,
string redirectUri,
string responseType = "code",
string scope = "snsapi_base");
Task<string> GenerateOAuth2LoginAsync(
string redirectUri,
string loginType = "ServiceApp",
string agentid = "");
string loginType = "ServiceApp");
}

6
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Application.Contracts/LINGYUN/Abp/WeChat/Work/Message/IWeChatWorkMessageAppService.cs

@ -13,18 +13,16 @@ public interface IWeChatWorkMessageAppService : IApplicationService
/// <remarks>
/// 参考文档:<see cref="https://developer.work.weixin.qq.com/document/path/90238"/>
/// </remarks>
/// <param name="agentId"></param>
/// <param name="input"></param>
/// <returns></returns>
Task<string> Handle(string agentId, MessageValidationInput input);
Task<string> Handle(MessageValidationInput input);
/// <summary>
/// 处理企业微信消息
/// </summary>
/// <remarks>
/// 参考文档:<see cref="https://developer.work.weixin.qq.com/document/path/90238"/>
/// </remarks>
/// <param name="agentId"></param>
/// <param name="input"></param>
/// <returns></returns>
Task<string> Handle(string agentId, MessageHandleInput input);
Task<string> Handle(MessageHandleInput input);
}

13
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Application/LINGYUN/Abp/WeChat/Work/Authorize/WeChatWorkAuthorizeAppService.cs

@ -21,21 +21,22 @@ public class WeChatWorkAuthorizeAppService : ApplicationService, IWeChatWorkAuth
_authorizeGenerator = authorizeGenerator;
}
public async virtual Task<string> GenerateOAuth2AuthorizeAsync(string agentid, string redirectUri, string responseType = "code", string scope = "snsapi_base")
public async virtual Task<string> GenerateOAuth2AuthorizeAsync(string redirectUri, string responseType = "code", string scope = "snsapi_base")
{
var state = _encryptionService.Encrypt($"agentid={agentid}&redirectUri={redirectUri}&responseType={responseType}&scope={scope}&random={Guid.NewGuid():D}").ToMd5();
return await _authorizeGenerator.GenerateOAuth2AuthorizeAsync(agentid, redirectUri, state, responseType, scope);
var state = _encryptionService.Encrypt($"redirectUri={redirectUri}&responseType={responseType}&scope={scope}&random={Guid.NewGuid():D}").ToMd5();
return await _authorizeGenerator.GenerateOAuth2AuthorizeAsync(redirectUri, state, responseType, scope);
}
public async virtual Task<string> GenerateOAuth2LoginAsync(string redirectUri, string loginType = "ServiceApp", string agentid = "")
public async virtual Task<string> GenerateOAuth2LoginAsync(string redirectUri, string loginType = "ServiceApp")
{
var state = _encryptionService.Encrypt($"agentid={agentid}&redirectUri={redirectUri}&loginType={loginType}&agentid={agentid}&random={Guid.NewGuid():D}").ToMd5();
var state = _encryptionService.Encrypt($"redirectUri={redirectUri}&loginType={loginType}&random={Guid.NewGuid():D}").ToMd5();
var corpId = await SettingProvider.GetOrNullAsync(WeChatWorkSettingNames.Connection.CorpId);
Check.NotNullOrEmpty(corpId, nameof(corpId));
return await _authorizeGenerator.GenerateOAuth2LoginAsync(corpId, redirectUri, state, loginType, agentid);
return await _authorizeGenerator.GenerateOAuth2LoginAsync(corpId, redirectUri, state, loginType);
}
}

52
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.Application/LINGYUN/Abp/WeChat/Work/Message/WeChatWorkMessageAppService.cs

@ -3,7 +3,7 @@ using LINGYUN.Abp.WeChat.Common.Crypto.Models;
using LINGYUN.Abp.WeChat.Common.Messages;
using LINGYUN.Abp.WeChat.Work.Settings;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Services;
@ -13,33 +13,40 @@ namespace LINGYUN.Abp.WeChat.Work.Message;
public class WeChatWorkMessageAppService : ApplicationService, IWeChatWorkMessageAppService
{
private readonly IWeChatCryptoService _cryptoService;
private readonly WeChatWorkOptions _options;
private readonly IDistributedEventBus _distributedEventBus;
private readonly IMessageResolver _messageResolver;
public WeChatWorkMessageAppService(
IMessageResolver messageResolver,
IWeChatCryptoService cryptoService,
IDistributedEventBus distributedEventBus,
IOptionsMonitor<WeChatWorkOptions> options)
IDistributedEventBus distributedEventBus)
{
_cryptoService = cryptoService;
_messageResolver = messageResolver;
_distributedEventBus = distributedEventBus;
_options = options.CurrentValue;
}
public async virtual Task<string> Handle(string agentId, MessageValidationInput input)
public async virtual Task<string> Handle(MessageValidationInput input)
{
var corpId = await SettingProvider.GetOrNullAsync(WeChatWorkSettingNames.Connection.CorpId);
var allSettings = await SettingProvider.GetAllAsync(
new[] {
WeChatWorkSettingNames.Connection.CorpId,
WeChatWorkSettingNames.Connection.Token,
WeChatWorkSettingNames.Connection.EncodingAESKey,
} );
var corpId = allSettings.FirstOrDefault(x => x.Name == WeChatWorkSettingNames.Connection.CorpId)?.Value;
var token = allSettings.FirstOrDefault(x => x.Name == WeChatWorkSettingNames.Connection.Token)?.Value;
var aesKey = allSettings.FirstOrDefault(x => x.Name == WeChatWorkSettingNames.Connection.EncodingAESKey)?.Value;
Check.NotNullOrEmpty(corpId, nameof(corpId));
Check.NotNullOrEmpty(token, nameof(token));
Check.NotNullOrEmpty(aesKey, nameof(aesKey));
var applicationConfiguration = _options.Applications.GetConfiguration(agentId);
var cryptoConfiguration = applicationConfiguration.GetCryptoConfiguration("Message");
var echoData = new WeChatCryptoEchoData(
input.EchoStr,
corpId,
cryptoConfiguration.Token,
cryptoConfiguration.EncodingAESKey,
token,
aesKey,
input.Msg_Signature,
input.TimeStamp.ToString(),
input.Nonce);
@ -49,18 +56,27 @@ public class WeChatWorkMessageAppService : ApplicationService, IWeChatWorkMessag
return echoStr;
}
public async virtual Task<string> Handle(string agentId, MessageHandleInput input)
public async virtual Task<string> Handle(MessageHandleInput input)
{
var corpId = await SettingProvider.GetOrNullAsync(WeChatWorkSettingNames.Connection.CorpId);
Check.NotNullOrEmpty(corpId, nameof(corpId));
var allSettings = await SettingProvider.GetAllAsync(
new[] {
WeChatWorkSettingNames.Connection.CorpId,
WeChatWorkSettingNames.Connection.Token,
WeChatWorkSettingNames.Connection.EncodingAESKey,
});
var applicationConfiguration = _options.Applications.GetConfiguration(agentId);
var cryptoConfiguration = applicationConfiguration.GetCryptoConfiguration("Message");
var corpId = allSettings.FirstOrDefault(x => x.Name == WeChatWorkSettingNames.Connection.CorpId)?.Value;
var token = allSettings.FirstOrDefault(x => x.Name == WeChatWorkSettingNames.Connection.Token)?.Value;
var aesKey = allSettings.FirstOrDefault(x => x.Name == WeChatWorkSettingNames.Connection.EncodingAESKey)?.Value;
Check.NotNullOrEmpty(corpId, nameof(corpId));
Check.NotNullOrEmpty(token, nameof(token));
Check.NotNullOrEmpty(aesKey, nameof(aesKey));
var messageData = new MessageResolveData(
corpId,
cryptoConfiguration.Token,
cryptoConfiguration.EncodingAESKey,
token,
aesKey,
input.Msg_Signature,
input.TimeStamp,
input.Nonce,

3
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.AspNetCore/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.AspNetCore/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

24
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.AspNetCore/LINGYUN.Abp.WeChat.Work.AspNetCore.csproj

@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\..\configureawait.props" />
<Import Project="..\..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<AssemblyName>LINGYUN.Abp.WeChat.Work.AspNetCore</AssemblyName>
<PackageId>LINGYUN.Abp.WeChat.Work.AspNetCore</PackageId>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.AspNetCore" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.WeChat.Work\LINGYUN.Abp.WeChat.Work.csproj" />
</ItemGroup>
</Project>

11
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.AspNetCore/LINGYUN/Abp/WeChat/Work/AspNetCore/AbpWeChatWorkAspNetCoreModule.cs

@ -0,0 +1,11 @@
using Volo.Abp.AspNetCore;
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.WeChat.Work.AspNetCore;
[DependsOn(
typeof(AbpWeChatWorkModule),
typeof(AbpAspNetCoreModule))]
public class AbpWeChatWorkAspNetCoreModule : AbpModule
{
}

40
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.AspNetCore/Microsoft/AspNetCore/Authentication/WeChat/Work/WeChatWorkOAuthConsts.cs

@ -0,0 +1,40 @@
using LINGYUN.Abp.WeChat.Work;
namespace Microsoft.AspNetCore.Authentication.WeChat.Work;
public static class WeChatWorkOAuthConsts
{
/// <summary>
/// 微信个人信息标识
/// </summary>
public static string ProfileKey => AbpWeChatWorkGlobalConsts.ProfileKey;
/// <summary>
/// 微信提供者标识
/// </summary>
public static string ProviderKey => AbpWeChatWorkGlobalConsts.ProviderName;
/// <summary>
/// 微信提供者显示名称
/// </summary>
public static string DisplayName => AbpWeChatWorkGlobalConsts.DisplayName;
/// <summary>
/// 回调地址
/// </summary>
public static string CallbackPath { get; set; } = "/signin-wxwork";
/// <summary>
/// 微信客户端内的网页登录
/// </summary>
public const string AuthorizationEndpoint = "https://login.work.weixin.qq.com/wwlogin/sso/login";
/// <summary>
/// 用户允许授权后通过返回的code换取access_token地址
/// </summary>
public const string TokenEndpoint = "https://qyapi.weixin.qq.com/cgi-bin/gettoken";
/// <summary>
/// 使用access_token获取用户个人信息地址
/// </summary>
public const string UserInformationEndpoint = "https://qyapi.weixin.qq.com/cgi-bin/auth/getuserinfo";
public const string UserInfoScope = "snsapi_privateinfo";
public const string LoginScope = "snsapi_base";
}

329
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.AspNetCore/Microsoft/AspNetCore/Authentication/WeChat/Work/WeChatWorkOAuthHandler.cs

@ -0,0 +1,329 @@
using LINGYUN.Abp.WeChat.Work.Settings;
using LINGYUN.Abp.WeChat.Work.Token;
using Microsoft.AspNetCore.Authentication.OAuth;
using Microsoft.AspNetCore.WebUtilities;
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.Linq;
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;
using Volo.Abp.Settings;
namespace Microsoft.AspNetCore.Authentication.WeChat.Work;
public class WeChatWorkOAuthHandler : OAuthHandler<WeChatWorkOAuthOptions>
{
protected ISettingProvider SettingProvider { get; }
public WeChatWorkOAuthHandler(
ISettingProvider settingProvider,
IWeChatWorkTokenProvider wechatWorkTokenProvider,
IOptionsMonitor<WeChatWorkOAuthOptions> options,
ILoggerFactory logger,
UrlEncoder encoder)
: base(options, logger, encoder)
{
SettingProvider = settingProvider;
}
/// <summary>
/// 重写应用配置
/// </summary>
/// <returns></returns>
protected async override Task InitializeHandlerAsync()
{
var settings = await SettingProvider.GetAllAsync(
new[] {
WeChatWorkSettingNames.Connection.CorpId,
WeChatWorkSettingNames.Connection.AgentId,
WeChatWorkSettingNames.Connection.Secret,
});
var corpId = settings.FirstOrDefault(x => x.Name == WeChatWorkSettingNames.Connection.CorpId)?.Value;
var agentId = settings.FirstOrDefault(x => x.Name == WeChatWorkSettingNames.Connection.AgentId)?.Value;
var secret = settings.FirstOrDefault(x => x.Name == WeChatWorkSettingNames.Connection.Secret)?.Value;
Check.NotNullOrEmpty(corpId, nameof(corpId));
Check.NotNullOrEmpty(agentId, nameof(agentId));
Check.NotNullOrEmpty(secret, nameof(secret));
// 用配置项重写
Options.CorpId = corpId;
Options.ClientId = agentId;
Options.ClientSecret = secret;
Options.TimeProvider ??= TimeProvider.System;
await base.InitializeHandlerAsync();
}
/// <summary>
/// 第一步:构建用户授权地址
/// </summary>
protected override string BuildChallengeUrl(AuthenticationProperties properties, string redirectUri)
{
var isWeChatBrewserRequest = IsWeChatBrowser();
var scope = isWeChatBrewserRequest
? WeChatWorkOAuthConsts.UserInfoScope
: WeChatWorkOAuthConsts.LoginScope;
var parameters = new Dictionary<string, string>
{
{ "appid", Options.CorpId },
{ "redirect_uri", redirectUri },
{ "response_type", "code" },
{ "scope", scope },
{ "agentid", Options.ClientId },
{ "lang", "zh" },
};
var state = Options.StateDataFormat.Protect(properties);
parameters["state"] = state; ;
return $"{QueryHelpers.AddQueryString(Options.AuthorizationEndpoint, parameters)}";
}
/// <summary>
/// 第二步:code换取access_token
/// </summary>
protected async override Task<OAuthTokenResponse> ExchangeCodeAsync(OAuthCodeExchangeContext context)
{
var parameters = new Dictionary<string, string>()
{
{ "corpid", Options.CorpId },
{ "corpsecret", Options.ClientSecret },
};
var address = QueryHelpers.AddQueryString(Options.TokenEndpoint, parameters);
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 (payload.GetRootInt32("errcode") != 0)
{
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);
}
/// <summary>
/// 第三步:构建用户票据
/// </summary>
/// <param name="identity"></param>
/// <param name="properties"></param>
/// <param name="tokens"></param>
/// <returns></returns>
/// <exception cref="HttpRequestException"></exception>
protected async virtual Task<AuthenticationTicket> CreateTicketAsync(string code, ClaimsIdentity identity, AuthenticationProperties properties, OAuthTokenResponse tokens)
{
var address = QueryHelpers.AddQueryString(Options.UserInformationEndpoint, new Dictionary<string, string>
{
["access_token"] = tokens.AccessToken,
["code"] = code
});
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 (payload.GetRootInt32("errcode") != 0)
{
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);
return new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name);
}
protected async override Task<HandleRequestResult> HandleRemoteAuthenticateAsync()
{
var query = Request.Query;
var state = query["state"];
var properties = Options.StateDataFormat.Unprotect(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 = Options.TimeProvider.GetUtcNow() + TimeSpan.FromSeconds(value);
authTokens.Add(new AuthenticationToken
{
Name = "expires_at",
Value = expiresAt.ToString("o", CultureInfo.InvariantCulture)
});
}
}
properties.StoreTokens(authTokens);
}
var ticket = await CreateTicketAsync(code, identity, properties, tokens);
if (ticket != null)
{
return HandleRequestResult.Success(ticket);
}
else
{
return HandleRequestResult.Fail("Failed to retrieve user information from remote server.", properties);
}
}
protected virtual bool IsWeChatBrowser()
{
var userAgent = Request.Headers[HeaderNames.UserAgent].ToString();
return userAgent.Contains("micromessenger", StringComparison.InvariantCultureIgnoreCase);
}
}

43
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.AspNetCore/Microsoft/AspNetCore/Authentication/WeChat/Work/WeChatWorkOAuthOptions.cs

@ -0,0 +1,43 @@
using LINGYUN.Abp.WeChat.Work.Security.Claims;
using Microsoft.AspNetCore.Authentication.OAuth;
using Microsoft.AspNetCore.Http;
using System.Security.Claims;
using Volo.Abp.Security.Claims;
namespace Microsoft.AspNetCore.Authentication.WeChat.Work;
public class WeChatWorkOAuthOptions : OAuthOptions
{
/// <summary>
/// 企业Id
/// </summary>
public string CorpId { get; set; }
public WeChatWorkOAuthOptions()
{
// 用于防止初始化错误,会在OAuthHandler.InitializeHandlerAsync中进行重写
CorpId = "CorpId";
ClientId = "ClientId";
ClientSecret = "ClientSecret";
CallbackPath = new PathString(WeChatWorkOAuthConsts.CallbackPath);
AuthorizationEndpoint = WeChatWorkOAuthConsts.AuthorizationEndpoint;
TokenEndpoint = WeChatWorkOAuthConsts.TokenEndpoint;
UserInformationEndpoint = WeChatWorkOAuthConsts.UserInformationEndpoint;
Scope.Add(WeChatWorkOAuthConsts.LoginScope);
Scope.Add(WeChatWorkOAuthConsts.UserInfoScope);
ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "userid");
ClaimActions.MapJsonKey(ClaimTypes.Name, "userid");
ClaimActions.MapJsonKey("sub", "userid");
// 把自定义的身份标识写进令牌
ClaimActions.MapJsonKey(AbpWeChatWorkClaimTypes.UserId, "userid");
ClaimActions.MapJsonKey(AbpWeChatWorkClaimTypes.QrCode, "qr_code");
ClaimActions.MapJsonKey(AbpWeChatWorkClaimTypes.BizMail, "biz_mail");
ClaimActions.MapJsonKey(AbpWeChatWorkClaimTypes.Address, "address");
ClaimActions.MapJsonKey(AbpClaimTypes.PhoneNumber, "mobile");
ClaimActions.MapJsonKey(AbpClaimTypes.Email, "email");
}
}

63
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.AspNetCore/Microsoft/AspNetCore/Authentication/WeChatWorkAuthenticationExtensions.cs

@ -0,0 +1,63 @@
using LINGYUN.Abp.WeChat.Work;
using Microsoft.AspNetCore.Authentication.WeChat.Work;
using Microsoft.Extensions.DependencyInjection;
using System;
namespace Microsoft.AspNetCore.Authentication;
public static class WeChatWorkAuthenticationExtensions
{
/// <summary>
/// </summary>
public static AuthenticationBuilder AddWeChatWork(
this AuthenticationBuilder builder)
{
return builder
.AddWeChatWork(
AbpWeChatWorkGlobalConsts.AuthenticationScheme,
AbpWeChatWorkGlobalConsts.DisplayName,
options => { });
}
/// <summary>
/// </summary>
public static AuthenticationBuilder AddWeChatWork(
this AuthenticationBuilder builder,
Action<WeChatWorkOAuthOptions> configureOptions)
{
return builder
.AddWeChatWork(
AbpWeChatWorkGlobalConsts.AuthenticationScheme,
AbpWeChatWorkGlobalConsts.DisplayName,
configureOptions);
}
/// <summary>
/// </summary>
public static AuthenticationBuilder AddWeChatWork(
this AuthenticationBuilder builder,
string authenticationScheme,
Action<WeChatWorkOAuthOptions> configureOptions)
{
return builder
.AddWeChatWork(
authenticationScheme,
AbpWeChatWorkGlobalConsts.DisplayName,
configureOptions);
}
/// <summary>
/// </summary>
public static AuthenticationBuilder AddWeChatWork(
this AuthenticationBuilder builder,
string authenticationScheme,
string displayName,
Action<WeChatWorkOAuthOptions> configureOptions)
{
return builder
.AddOAuth<WeChatWorkOAuthOptions, WeChatWorkOAuthHandler>(
authenticationScheme,
displayName,
configureOptions);
}
}

16
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.AspNetCore/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/framework/wechat/LINGYUN.Abp.WeChat.Work.AspNetCore/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/framework/wechat/LINGYUN.Abp.WeChat.Work.AspNetCore/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/framework/wechat/LINGYUN.Abp.WeChat.Work.HttpApi/LINGYUN/Abp/WeChat/Work/Authorize/WeChatWorkAuthorizeController.cs

@ -42,12 +42,11 @@ public class WeChatWorkAuthorizeController : AbpControllerBase, IWeChatWorkAutho
[HttpGet]
[Route("oauth2")]
public virtual Task<string> GenerateOAuth2AuthorizeAsync(
[FromQuery(Name = "agent_id")] string agentid,
[FromQuery(Name = "redirect_uri")] string redirectUri,
[FromQuery(Name = "response_type")] string responseType = "code",
[FromQuery] string scope = "snsapi_base")
{
return _service.GenerateOAuth2AuthorizeAsync(agentid, redirectUri, responseType, scope);
return _service.GenerateOAuth2AuthorizeAsync(redirectUri, responseType, scope);
}
/// <summary>
@ -64,12 +63,11 @@ public class WeChatWorkAuthorizeController : AbpControllerBase, IWeChatWorkAutho
[HttpGet]
[Route("oauth2/authorize")]
public async virtual Task<IActionResult> OAuth2AuthorizeAsync(
[FromQuery(Name = "agent_id")] string agentid,
[FromQuery(Name = "redirect_uri")] string redirectUri,
[FromQuery(Name = "response_type")] string responseType = "code",
[FromQuery] string scope = "snsapi_base")
{
var url = await _service.GenerateOAuth2AuthorizeAsync(agentid, redirectUri, responseType, scope);
var url = await _service.GenerateOAuth2AuthorizeAsync(redirectUri, responseType, scope);
return Redirect(url);
}
@ -88,10 +86,9 @@ public class WeChatWorkAuthorizeController : AbpControllerBase, IWeChatWorkAutho
[Route("oauth2/login")]
public virtual Task<string> GenerateOAuth2LoginAsync(
[FromQuery(Name = "redirect_uri")] string redirectUri,
[FromQuery(Name = "login_type")] string loginType = "ServiceApp",
[FromQuery(Name = "agent_id")] string agentid = "")
[FromQuery(Name = "login_type")] string loginType = "ServiceApp")
{
return _service.GenerateOAuth2LoginAsync(redirectUri, loginType, agentid);
return _service.GenerateOAuth2LoginAsync(redirectUri, loginType);
}
/// <summary>
@ -108,10 +105,9 @@ public class WeChatWorkAuthorizeController : AbpControllerBase, IWeChatWorkAutho
[Route("oauth2/login/sso")]
public async virtual Task<IActionResult> OAuth2LoginAsync(
[FromQuery(Name = "redirect_uri")] string redirectUri,
[FromQuery(Name = "login_type")] string loginType = "ServiceApp",
[FromQuery(Name = "agent_id")] string agentid = "")
[FromQuery(Name = "login_type")] string loginType = "ServiceApp")
{
var url = await _service.GenerateOAuth2LoginAsync(redirectUri, loginType, agentid);
var url = await _service.GenerateOAuth2LoginAsync(redirectUri, loginType);
return Redirect(url);
}

10
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work.HttpApi/LINGYUN/Abp/WeChat/Work/Message/WeChatWorkMessageController.cs

@ -36,10 +36,9 @@ public class WeChatWorkMessageController : AbpControllerBase, IWeChatWorkMessage
/// <param name="input">企业微信服务器传递的验证消息参数</param>
/// <returns></returns>
[HttpGet]
[Route("{agentId}")]
public virtual Task<string> Handle([FromRoute] string agentId, [FromQuery] MessageValidationInput input)
public virtual Task<string> Handle([FromQuery] MessageValidationInput input)
{
return _service.Handle(agentId, input);
return _service.Handle(input);
}
/// <summary>
@ -50,14 +49,13 @@ public class WeChatWorkMessageController : AbpControllerBase, IWeChatWorkMessage
/// <param name="input">企业微信服务器传递的消息参数</param>
/// <returns></returns>
[HttpPost]
[Route("{agentId}")]
public async virtual Task<string> Handle([FromRoute] string agentId, [FromQuery] MessageHandleInput input)
public async virtual Task<string> Handle([FromQuery] MessageHandleInput input)
{
using var reader = new StreamReader(Request.Body, Encoding.UTF8);
var content = await reader.ReadToEndAsync();
input.Data = content;
return await _service.Handle(agentId, input);
return await _service.Handle(input);
}
}

4
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/AbpWeChatWorkGlobalConsts.cs

@ -5,11 +5,11 @@ public class AbpWeChatWorkGlobalConsts
/// <summary>
/// 企业微信对应的Provider名称
/// </summary>
public static string ProviderName { get; set; } = "WeChat.Work";
public static string ProviderName { get; set; } = "WeChat.WeCom";
/// <summary>
/// 企业微信授权类型
/// </summary>
public static string GrantType { get; set; } = "wx-work";
public static string GrantType { get; set; } = "wecom";
/// <summary>
/// 企业微信授权名称
/// </summary>

3
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/AbpWeChatWorkModule.cs

@ -24,9 +24,6 @@ public class AbpWeChatWorkModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
Configure<WeChatWorkOptions>(configuration.GetSection("WeChat:Work"));
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<AbpWeChatWorkModule>();

4
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Authorize/IWeChatWorkAuthorizeGenerator.cs

@ -9,14 +9,12 @@ public interface IWeChatWorkAuthorizeGenerator
/// <remarks>
/// 参考:https://developer.work.weixin.qq.com/document/path/91022
/// </remarks>
/// <param name="agentid"></param>
/// <param name="redirectUri"></param>
/// <param name="state"></param>
/// <param name="responseType"></param>
/// <param name="scope"></param>
/// <returns></returns>
Task<string> GenerateOAuth2AuthorizeAsync(
string agentid,
string redirectUri,
string state,
string responseType = "code",
@ -24,7 +22,6 @@ public interface IWeChatWorkAuthorizeGenerator
/// <summary>
/// 构建网页登录链接
/// </summary>
/// <param name="appid"></param>
/// <param name="redirectUri"></param>
/// <param name="state"></param>
/// <param name="loginType"></param>
@ -32,7 +29,6 @@ public interface IWeChatWorkAuthorizeGenerator
/// <param name="lang"></param>
/// <returns></returns>
Task<string> GenerateOAuth2LoginAsync(
string appid,
string redirectUri,
string state,
string loginType = "ServiceApp",

2
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Authorize/IWeChatWorkUserFinder.cs

@ -9,12 +9,10 @@ namespace LINGYUN.Abp.WeChat.Work.Authorize;
public interface IWeChatWorkUserFinder
{
Task<WeChatWorkUserInfo> GetUserInfoAsync(
string agentId,
string code,
CancellationToken cancellationToken = default);
Task<WeChatWorkUserDetail> GetUserDetailAsync(
string agentId,
string userTicket,
CancellationToken cancellationToken = default);
}

16
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Authorize/WeChatWorkAuthorizeGenerator.cs

@ -27,15 +27,16 @@ public class WeChatWorkAuthorizeGenerator : IWeChatWorkAuthorizeGenerator, ISing
}
public async virtual Task<string> GenerateOAuth2AuthorizeAsync(
string agentid,
string redirectUri,
string state,
string responseType = "code",
string scope = "snsapi_base")
{
var corpId = await SettingProvider.GetOrNullAsync(WeChatWorkSettingNames.Connection.CorpId);
var agentId = await SettingProvider.GetOrNullAsync(WeChatWorkSettingNames.Connection.AgentId);
Check.NotNullOrEmpty(corpId, nameof(corpId));
Check.NotNullOrEmpty(agentId, nameof(agentId));
var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.OAuthClient);
@ -49,20 +50,23 @@ public class WeChatWorkAuthorizeGenerator : IWeChatWorkAuthorizeGenerator, ISing
.AppendFormat("&response_type={0}", responseType)
.AppendFormat("&scope={0}", scope)
.AppendFormat("&state={0}", state)
.AppendFormat("&agentid={0}", agentid)
.AppendFormat("&agentid={0}", agentId)
.Append("#wechat_redirect");
return generatedUrlBuilder.ToString();
}
public virtual Task<string> GenerateOAuth2LoginAsync(
public async virtual Task<string> GenerateOAuth2LoginAsync(
string appid,
string redirectUri,
string state,
string loginType = "ServiceApp",
string agentid = "",
string lang = "zh")
{
var agentId = await SettingProvider.GetOrNullAsync(WeChatWorkSettingNames.Connection.AgentId);
Check.NotNullOrEmpty(agentId, nameof(agentId));
var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.LoginClient);
var generatedUrlBuilder = new StringBuilder();
@ -72,11 +76,11 @@ public class WeChatWorkAuthorizeGenerator : IWeChatWorkAuthorizeGenerator, ISing
.Append("wwlogin/sso/login")
.AppendFormat("?login_type={0}", loginType)
.AppendFormat("&appid={0}", appid)
.AppendFormat("&agentid={0}", agentid)
.AppendFormat("&agentid={0}", agentId)
.AppendFormat("&redirect_uri={0}", HttpUtility.UrlEncode(redirectUri))
.AppendFormat("&state={0}", state)
.AppendFormat("&lang={0}", lang);
return Task.FromResult(generatedUrlBuilder.ToString());
return generatedUrlBuilder.ToString();
}
}

6
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Authorize/WeChatWorkUserFinder.cs

@ -26,11 +26,10 @@ public class WeChatWorkUserFinder : IWeChatWorkUserFinder, ISingletonDependency
}
public async virtual Task<WeChatWorkUserInfo> GetUserInfoAsync(
string agentId,
string code,
CancellationToken cancellationToken = default)
{
var token = await WeChatWorkTokenProvider.GetTokenAsync(agentId, cancellationToken);
var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken);
var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient);
using var response = await client.GetUserInfoAsync(token.AccessToken, code, cancellationToken);
@ -40,11 +39,10 @@ public class WeChatWorkUserFinder : IWeChatWorkUserFinder, ISingletonDependency
}
public async virtual Task<WeChatWorkUserDetail> GetUserDetailAsync(
string agentId,
string userTicket,
CancellationToken cancellationToken = default)
{
var token = await WeChatWorkTokenProvider.GetTokenAsync(agentId, cancellationToken);
var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken);
var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient);
var request = new WeChatWorkUserDetailRequest(userTicket);

6
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Chat/WeChatWorkAppChatManager.cs

@ -28,7 +28,7 @@ public class WeChatWorkAppChatManager : IWeChatWorkAppChatManager, ISingletonDep
WeChatWorkAppChatCreateRequest request,
CancellationToken cancellationToken = default)
{
var token = await WeChatWorkTokenProvider.GetTokenAsync(request.AgentId, cancellationToken);
var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken);
var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient);
using var response = await client.CreateAppChatAsync(token.AccessToken, request, cancellationToken);
@ -40,7 +40,7 @@ public class WeChatWorkAppChatManager : IWeChatWorkAppChatManager, ISingletonDep
string chatId,
CancellationToken cancellationToken = default)
{
var token = await WeChatWorkTokenProvider.GetTokenAsync(agentId, cancellationToken);
var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken);
var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient);
using var response = await client.GetAppChatAsync(token.AccessToken, agentId, cancellationToken);
@ -51,7 +51,7 @@ public class WeChatWorkAppChatManager : IWeChatWorkAppChatManager, ISingletonDep
WeChatWorkAppChatUpdateRequest request,
CancellationToken cancellationToken = default)
{
var token = await WeChatWorkTokenProvider.GetTokenAsync(request.AgentId, cancellationToken);
var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken);
var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient);
using var response = await client.UpdateAppChatAsync(token.AccessToken, request, cancellationToken);

12
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Localization/Resources/en.json

@ -22,8 +22,16 @@
"Description:WeChatWork": "企业微信应用集成",
"DisplayName:Connection": "连接参数",
"Description:Connection": "企业微信连接参数",
"DisplayName:WeChatWork.Connection.CorpId": "Corp Id",
"Description:WeChatWork.Connection.CorpId": "Each enterprise has a unique corpid, to obtain this information, you can view \"Enterprise ID\" under \"My Enterprise\" - \"Enterprise Information\" in the management background (requires administrator permissions).",
"DisplayName:WeChatWork.Connection.CorpId": "企业Id",
"Description:WeChatWork.Connection.CorpId": "每个企业都拥有唯一的corpid,获取此信息可在管理后台“我的企业”-“企业信息”下查看“企业ID”(需要有管理员权限)",
"DisplayName:WeChatWork.Connection.AgentId": "应用Id",
"Description:WeChatWork.Connection.AgentId": "每个应用都有唯一的agentid。在管理后台->“应用管理”->“应用”,点进某个应用,即可看到agentid",
"DisplayName:WeChatWork.Connection.Secret": "访问密钥",
"Description:WeChatWork.Connection.Secret": "secret是企业应用里面用于保障数据安全的“钥匙”,每一个应用都有一个独立的访问密钥,为了保证数据的安全,secret务必不能泄漏,在管理后台->“应用管理”->“应用”->“自建”,点进某个应用,即可看到",
"DisplayName:WeChatWork.Connection.Token": "Token",
"Description:WeChatWork.Connection.Token": "用于计算签名,详见: https://developer.work.weixin.qq.com/document/path/90930",
"DisplayName:WeChatWork.Connection.EncodingAESKey": "EncodingAESKey",
"Description:WeChatWork.Connection.EncodingAESKey": "用于消息加密,详见: https://developer.work.weixin.qq.com/document/path/90930",
"DisplayName:WeChatWork.EnabledQuickLogin": "Enabled Quick Login",
"Description:WeChatWork.EnabledQuickLogin": "Users can log in directly by scanning the code obtained when they are not registered.",
"WeChatWork:100400": "处理企业微信服务器消息失败,请检查应用签名配置!",

8
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Localization/Resources/zh-Hans.json

@ -24,6 +24,14 @@
"Description:Connection": "企业微信连接参数",
"DisplayName:WeChatWork.Connection.CorpId": "企业Id",
"Description:WeChatWork.Connection.CorpId": "每个企业都拥有唯一的corpid,获取此信息可在管理后台“我的企业”-“企业信息”下查看“企业ID”(需要有管理员权限)",
"DisplayName:WeChatWork.Connection.AgentId": "应用Id",
"Description:WeChatWork.Connection.AgentId": "每个应用都有唯一的agentid。在管理后台->“应用管理”->“应用”,点进某个应用,即可看到agentid",
"DisplayName:WeChatWork.Connection.Secret": "访问密钥",
"Description:WeChatWork.Connection.Secret": "secret是企业应用里面用于保障数据安全的“钥匙”,每一个应用都有一个独立的访问密钥,为了保证数据的安全,secret务必不能泄漏,在管理后台->“应用管理”->“应用”->“自建”,点进某个应用,即可看到",
"DisplayName:WeChatWork.Connection.Token": "Token",
"Description:WeChatWork.Connection.Token": "用于计算签名,详见: https://developer.work.weixin.qq.com/document/path/90930",
"DisplayName:WeChatWork.Connection.EncodingAESKey": "EncodingAESKey",
"Description:WeChatWork.Connection.EncodingAESKey": "用于消息加密,详见: https://developer.work.weixin.qq.com/document/path/90930",
"DisplayName:WeChatWork.EnabledQuickLogin": "启用快捷登录",
"Description:WeChatWork.EnabledQuickLogin": "用户可在未注册时通过扫码得到的code直接登录",
"WeChatWork:100400": "处理企业微信服务器消息失败,请检查应用签名配置!",

6
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Media/IWeChatWorkMediaProvider.cs

@ -18,13 +18,11 @@ public interface IWeChatWorkMediaProvider
/// <remarks>
/// API: <see cref="https://developer.work.weixin.qq.com/document/path/90253"/>
/// </remarks>
/// <param name="agentId">应用标识</param>
/// <param name="type">媒体文件类型</param>
/// <param name="media">待上传文件</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<WeChatWorkMediaResponse> UploadAsync(
string agentId,
string type,
IRemoteStreamContent media,
CancellationToken cancellationToken = default);
@ -34,13 +32,11 @@ public interface IWeChatWorkMediaProvider
/// <remarks>
/// API: <see cref="https://developer.work.weixin.qq.com/document/path/90254"/>
/// </remarks>
/// <param name="agentId">应用标识</param>
/// <param name="mediaId">媒体文件id</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
///
Task<IRemoteStreamContent> GetAsync(
string agentId,
string mediaId,
CancellationToken cancellationToken = default);
/// <summary>
@ -49,12 +45,10 @@ public interface IWeChatWorkMediaProvider
/// <remarks>
/// API: <see cref="https://developer.work.weixin.qq.com/document/path/90256"/>
/// </remarks>
/// <param name="agentId">应用标识</param>
/// <param name="image">待上传图片</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<WeChatWorkImageResponse> UploadImageAsync(
string agentId,
IRemoteStreamContent image,
CancellationToken cancellationToken = default);
}

9
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Media/WeChatWorkMediaProvider.cs

@ -23,11 +23,10 @@ public class WeChatWorkMediaProvider : IWeChatWorkMediaProvider, ISingletonDepen
}
public async virtual Task<IRemoteStreamContent> GetAsync(
string agentId,
string mediaId,
CancellationToken cancellationToken = default)
{
var token = await WeChatWorkTokenProvider.GetTokenAsync(agentId, cancellationToken);
var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken);
var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient);
using var response = await client.GetMediaAsync(
@ -75,12 +74,11 @@ public class WeChatWorkMediaProvider : IWeChatWorkMediaProvider, ISingletonDepen
}
public async virtual Task<WeChatWorkMediaResponse> UploadAsync(
string agentId,
string type,
IRemoteStreamContent media,
CancellationToken cancellationToken = default)
{
var token = await WeChatWorkTokenProvider.GetTokenAsync(agentId, cancellationToken);
var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken);
var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient);
var request = new WeChatWorkMediaRequest(
@ -95,11 +93,10 @@ public class WeChatWorkMediaProvider : IWeChatWorkMediaProvider, ISingletonDepen
}
public async virtual Task<WeChatWorkImageResponse> UploadImageAsync(
string agentId,
IRemoteStreamContent image,
CancellationToken cancellationToken = default)
{
var token = await WeChatWorkTokenProvider.GetTokenAsync(agentId, cancellationToken);
var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken);
var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient);
var request = new WeChatWorkMediaRequest(
token.AccessToken,

3
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/IWeChatWorkMessageManager.cs

@ -13,10 +13,9 @@ public interface IWeChatWorkMessageManager
/// <remarks>
/// 参考:https://developer.work.weixin.qq.com/document/path/94867
/// </remarks>
/// <param name="agentId">应用标识</param>
/// <param name="messageId">消息ID。从应用发送消息接口 <see cref="IWeChatWorkMessageSender"/> 处获得。</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<bool> ReCallMessageAsync(string agentId, string messageId, CancellationToken cancellationToken = default);
Task<bool> ReCallMessageAsync( string messageId, CancellationToken cancellationToken = default);
}

4
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/WeChatWorkMessageManager.cs

@ -26,9 +26,9 @@ public class WeChatWorkMessageManager : IWeChatWorkMessageManager, ISingletonDep
Logger = NullLogger<WeChatWorkMessageManager>.Instance;
}
public async virtual Task<bool> ReCallMessageAsync(string agentId, string messageId, CancellationToken cancellationToken = default)
public async virtual Task<bool> ReCallMessageAsync(string messageId, CancellationToken cancellationToken = default)
{
var token = await WeChatWorkTokenProvider.GetTokenAsync(agentId, cancellationToken);
var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken);
var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient);
var request = new WeChatWorkMessageReCallRequest(

4
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Messages/WeChatWorkMessageSender.cs

@ -38,7 +38,7 @@ public class WeChatWorkMessageSender : IWeChatWorkMessageSender, ISingletonDepen
LimitPolicy.Days)]
public async virtual Task<WeChatWorkMessageResponse> SendAsync(WeChatWorkMessage message, CancellationToken cancellationToken = default)
{
var token = await WeChatWorkTokenProvider.GetTokenAsync(message.AgentId, cancellationToken);
var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken);
var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient);
var request = new WeChatWorkMessageRequest<WeChatWorkMessage>(
@ -64,7 +64,7 @@ public class WeChatWorkMessageSender : IWeChatWorkMessageSender, ISingletonDepen
LimitPolicy.Minute)]
public async virtual Task<WeChatWorkResponse> SendAsync(WeChatWorkAppChatMessage message, CancellationToken cancellationToken = default)
{
var token = await WeChatWorkTokenProvider.GetTokenAsync(message.AgentId, cancellationToken);
var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken);
var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient);
var request = new WeChatWorkMessageRequest<WeChatWorkAppChatMessage>(

26
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/NumberToStringConverter.cs

@ -0,0 +1,26 @@
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace LINGYUN.Abp.WeChat.Work;
internal class NumberToStringConverter : JsonConverter<string>
{
public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.Number)
{
return reader.GetInt32().ToString();
}
if (reader.TokenType == JsonTokenType.String)
{
return reader.GetString();
}
throw new JsonException("Unexpected token type");
}
public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options)
{
writer.WriteStringValue(value);
}
}

16
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/Claims/AbpWeChatWorkClaimTypes.cs

@ -2,7 +2,19 @@
public static class AbpWeChatWorkClaimTypes
{
/// <summary>
/// 用户的唯一标识
/// 唯一标识
/// </summary>
public static string UserId { get; set; } = "wecom-uid"; // 可变更
public static string UserId { get; set; } = "userid";
/// <summary>
/// 二维码名片
/// </summary>
public static string QrCode { get; set; } = "qr_code";
/// <summary>
/// 企业邮箱
/// </summary>
public static string BizMail { get; set; } = "biz_mail";
/// <summary>
/// 地址
/// </summary>
public static string Address { get; set; } = "address";
}

35
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/WeChatWorkCryptoConfiguration.cs

@ -1,35 +0,0 @@
using System.Collections.Generic;
namespace LINGYUN.Abp.WeChat.Work.Security;
/// <summary>
/// 企业微信加解密配置
/// </summary>
public class WeChatWorkCryptoConfiguration : Dictionary<string, string>
{
/// <summary>
/// 用于生成签名的Token
/// </summary>
public string Token {
get => this.GetOrDefault(nameof(Token));
set => this[nameof(Token)] = value;
}
/// <summary>
/// 用于消息加密的密钥
/// </summary>
public string EncodingAESKey {
get => this.GetOrDefault(nameof(EncodingAESKey));
set => this[nameof(EncodingAESKey)] = value;
}
public WeChatWorkCryptoConfiguration()
{
}
public WeChatWorkCryptoConfiguration(string token, string encodingAESKey)
{
this[nameof(Token)] = token;
this[nameof(EncodingAESKey)] = encodingAESKey;
}
}

12
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Security/WeChatWorkCryptoConfigurationDictionary.cs

@ -1,12 +0,0 @@
using JetBrains.Annotations;
using System.Collections.Generic;
namespace LINGYUN.Abp.WeChat.Work.Security;
public class WeChatWorkCryptoConfigurationDictionary : Dictionary<string, WeChatWorkCryptoConfiguration>
{
[CanBeNull]
public WeChatWorkCryptoConfiguration GetCryptoConfigurationOrNull(string feture)
{
return this.GetOrDefault(feture);
}
}

41
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Settings/WeChatWorkSettingDefinitionProvider.cs

@ -40,6 +40,47 @@ public class WeChatWorkSettingDefinitionProvider : SettingDefinitionProvider
ConfigurationSettingValueProvider.ProviderName,
GlobalSettingValueProvider.ProviderName,
TenantSettingValueProvider.ProviderName),
new SettingDefinition(
WeChatWorkSettingNames.Connection.AgentId,
displayName: L("DisplayName:WeChatWork.Connection.AgentId"),
description: L("Description:WeChatWork.Connection.AgentId"),
isEncrypted: true)
.WithProviders(
DefaultValueSettingValueProvider.ProviderName,
ConfigurationSettingValueProvider.ProviderName,
GlobalSettingValueProvider.ProviderName,
TenantSettingValueProvider.ProviderName),
new SettingDefinition(
WeChatWorkSettingNames.Connection.Secret,
displayName: L("DisplayName:WeChatWork.Connection.Secret"),
description: L("Description:WeChatWork.Connection.Secret"),
isEncrypted: true)
.WithProviders(
DefaultValueSettingValueProvider.ProviderName,
ConfigurationSettingValueProvider.ProviderName,
GlobalSettingValueProvider.ProviderName,
TenantSettingValueProvider.ProviderName),
new SettingDefinition(
WeChatWorkSettingNames.Connection.Token,
displayName: L("DisplayName:WeChatWork.Connection.Token"),
description: L("Description:WeChatWork.Connection.Token"),
isEncrypted: true)
.WithProviders(
DefaultValueSettingValueProvider.ProviderName,
ConfigurationSettingValueProvider.ProviderName,
GlobalSettingValueProvider.ProviderName,
TenantSettingValueProvider.ProviderName),
new SettingDefinition(
WeChatWorkSettingNames.Connection.EncodingAESKey,
displayName: L("DisplayName:WeChatWork.Connection.EncodingAESKey"),
description: L("Description:WeChatWork.Connection.EncodingAESKey"),
isEncrypted: true)
.WithProviders(
DefaultValueSettingValueProvider.ProviderName,
ConfigurationSettingValueProvider.ProviderName,
GlobalSettingValueProvider.ProviderName,
TenantSettingValueProvider.ProviderName),
};
}

20
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Settings/WeChatWorkSettingNames.cs

@ -12,7 +12,25 @@ public static class WeChatWorkSettingNames
public static class Connection
{
public const string Prefix = WeChatWorkSettingNames.Prefix + ".Connection";
/// <summary>
/// 企业Id
/// </summary>
public static string CorpId = Prefix + ".CorpId";
/// <summary>
/// 应用Id
/// </summary>
public static string AgentId = Prefix + ".AgentId";
/// <summary>
/// 应用密钥
/// </summary>
public static string Secret = Prefix + ".Secret";
/// <summary>
/// Token
/// </summary>
public static string Token = Prefix + ".Token";
/// <summary>
/// EncodingAESKey
/// </summary>
public static string EncodingAESKey = Prefix + ".EncodingAESKey";
}
}

5
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Token/IWeChatWorkTokenProvider.cs

@ -17,7 +17,7 @@ public interface IWeChatWorkTokenProvider
/// <param name="agentId">应用标识</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<WeChatWorkToken> GetTokenAsync(string agentId, CancellationToken cancellationToken = default);
Task<WeChatWorkToken> GetTokenAsync(CancellationToken cancellationToken = default);
/// <summary>
/// 获取应用Token
/// </summary>
@ -26,7 +26,8 @@ public interface IWeChatWorkTokenProvider
/// </remarks>
/// <param name="corpId">企业标识</param>
/// <param name="agentId">应用标识</param>
/// <param name="secret">应用密钥</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<WeChatWorkToken> GetTokenAsync(string corpId, string agentId, CancellationToken cancellationToken = default);
Task<WeChatWorkToken> GetTokenAsync(string corpId, string agentId, string secret, CancellationToken cancellationToken = default);
}

4
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Token/Models/WeChatWorkTokenResponse.cs

@ -1,4 +1,5 @@
using Newtonsoft.Json;
using System.Text.Json.Serialization;
namespace LINGYUN.Abp.WeChat.Work.Token.Models;
@ -11,11 +12,14 @@ public class WeChatWorkTokenResponse : WeChatWorkResponse
/// 访问令牌
/// </summary>
[JsonProperty("access_token")]
[JsonPropertyName("access_token")]
public string AccessToken { get; set; }
/// <summary>
/// 过期时间,单位(s)
/// </summary>
[JsonProperty("expires_in")]
[JsonPropertyName("expires_in")]
[System.Text.Json.Serialization.JsonConverter(typeof(NumberToStringConverter))]
public int ExpiresIn { get; set; }
public WeChatWorkToken ToWeChatWorkToken()

68
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/Token/WeChatWorkTokenProvider.cs

@ -1,10 +1,8 @@
using LINGYUN.Abp.WeChat.Work.Settings;
using LINGYUN.Abp.WeChat.Work.Token.Models;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using System;
using System.Net.Http;
using System.Threading;
@ -21,38 +19,35 @@ public class WeChatWorkTokenProvider : IWeChatWorkTokenProvider, ISingletonDepen
public ILogger<WeChatWorkTokenProvider> Logger { get; set; }
protected ISettingProvider SettingProvider { get; }
protected IHttpClientFactory HttpClientFactory { get; }
protected IMemoryCache MemoryCache { get; }
protected IDistributedCache<WeChatWorkTokenCacheItem> WeChatWorkTokenCache { get; }
protected WeChatWorkOptions WeChatWorkOptions { get; }
public WeChatWorkTokenProvider(
ISettingProvider settingProvider,
IHttpClientFactory httpClientFactory,
IMemoryCache memoryCache,
IDistributedCache<WeChatWorkTokenCacheItem> cache,
IOptionsMonitor<WeChatWorkOptions> weChatWorkOptions)
IDistributedCache<WeChatWorkTokenCacheItem> cache)
{
HttpClientFactory = httpClientFactory;
SettingProvider = settingProvider;
MemoryCache = memoryCache;
WeChatWorkTokenCache = cache;
WeChatWorkOptions = weChatWorkOptions.CurrentValue;
Logger = NullLogger<WeChatWorkTokenProvider>.Instance;
}
public async virtual Task<WeChatWorkToken> GetTokenAsync(string agentId, CancellationToken cancellationToken = default)
public async virtual Task<WeChatWorkToken> GetTokenAsync(CancellationToken cancellationToken = default)
{
var corpId = await SettingProvider.GetOrNullAsync(WeChatWorkSettingNames.Connection.CorpId);
var agentId = await SettingProvider.GetOrNullAsync(WeChatWorkSettingNames.Connection.AgentId);
var secret = await SettingProvider.GetOrNullAsync(WeChatWorkSettingNames.Connection.Secret);
return await GetTokenAsync(corpId, agentId, cancellationToken);
return await GetTokenAsync(corpId, agentId, secret, cancellationToken);
}
public async virtual Task<WeChatWorkToken> GetTokenAsync(
string corpId,
string agentId,
string secret,
CancellationToken cancellationToken = default)
{
return (await GetCacheItemAsync("WeChatWorkToken", corpId, agentId, cancellationToken)).Token;
return (await GetCacheItemAsync("WeChatWorkToken", corpId, agentId, secret, cancellationToken)).Token;
}
/// <summary>
/// 获取缓存中的Token配置
@ -60,55 +55,23 @@ public class WeChatWorkTokenProvider : IWeChatWorkTokenProvider, ISingletonDepen
/// <param name="provider"></param>
/// <param name="corpId"></param>
/// <param name="agentId"></param>
/// <param name="secret"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
protected async virtual Task<WeChatWorkTokenCacheItem> GetCacheItemAsync(
string provider,
string corpId,
string agentId,
string agentId,
string secret,
CancellationToken cancellationToken = default)
{
Check.NotNullOrEmpty(corpId, nameof(corpId));
Check.NotNullOrEmpty(agentId, nameof(agentId));
Check.NotNullOrEmpty(secret, nameof(secret));
var cacheKey = WeChatWorkTokenCacheItem.CalculateCacheKey(provider, corpId, agentId);
return await GetOrRefreshMemoryCacheAsync(cacheKey, provider, corpId, agentId);
}
/// <summary>
/// 获取或刷新内存缓存中的Token配置
/// </summary>
/// <param name="cacheKey"></param>
/// <param name="provider"></param>
/// <param name="corpId"></param>
/// <param name="agentId"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
protected async virtual Task<WeChatWorkTokenCacheItem> GetOrRefreshMemoryCacheAsync(
string cacheKey,
string provider,
string corpId,
string agentId,
CancellationToken cancellationToken = default)
{
if (MemoryCache.TryGetValue<WeChatWorkTokenCacheItem>(cacheKey, out var cacheItem))
{
return cacheItem;
}
cacheItem = await GetOrRefreshDistributedCacheAsync(cacheKey, provider, corpId, agentId);
var cacheOptions = new MemoryCacheEntryOptions
{
AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(cacheItem.Token.ExpiresIn - 120),
};
cacheOptions.RegisterPostEvictionCallback(async (key, value, reason, state) =>
{
if (reason == EvictionReason.Expired)
{
await GetOrRefreshDistributedCacheAsync(cacheKey, provider, corpId, agentId);
}
});
return MemoryCache.Set(cacheKey, cacheItem, cacheOptions);
return await GetCacheItemAsync(cacheKey, provider, corpId, agentId, secret);
}
/// <summary>
/// 获取或刷新分布式缓存中的Token配置
@ -117,13 +80,15 @@ public class WeChatWorkTokenProvider : IWeChatWorkTokenProvider, ISingletonDepen
/// <param name="provider"></param>
/// <param name="corpId"></param>
/// <param name="agentId"></param>
/// <param name="secret"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
protected async virtual Task<WeChatWorkTokenCacheItem> GetOrRefreshDistributedCacheAsync(
protected async virtual Task<WeChatWorkTokenCacheItem> GetCacheItemAsync(
string cacheKey,
string provider,
string corpId,
string agentId,
string secret,
CancellationToken cancellationToken = default)
{
var cacheItem = await WeChatWorkTokenCache.GetAsync(cacheKey, token: cancellationToken);
@ -137,12 +102,11 @@ public class WeChatWorkTokenProvider : IWeChatWorkTokenProvider, ISingletonDepen
Logger.LogDebug($"Not found WeChatWorkToken in the cache, getting from the httpClient: {cacheKey}");
var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient);
var applicationConfiguration = WeChatWorkOptions.Applications.GetConfiguration(agentId);
var request = new WeChatWorkTokenRequest
{
CorpId = corpId,
CorpSecret = applicationConfiguration.Secret,
CorpSecret = secret,
};
using var response = await client.GetTokenAsync(request, cancellationToken);

43
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/WeChatWorkApplicationConfiguration.cs

@ -1,43 +0,0 @@
using JetBrains.Annotations;
using LINGYUN.Abp.WeChat.Work.Security;
namespace LINGYUN.Abp.WeChat.Work;
/// <summary>
/// 企业微信应用配置
/// </summary>
public class WeChatWorkApplicationConfiguration
{
/// <summary>
/// 应用的标识
/// </summary>
public string AgentId { get; set; }
/// <summary>
/// 应用的凭证密钥
/// </summary>
public string Secret { get; set; }
/// <summary>
/// 应用加密配置
/// </summary>
public WeChatWorkCryptoConfigurationDictionary CryptoKeys { get; set; }
public WeChatWorkApplicationConfiguration()
{
CryptoKeys = new WeChatWorkCryptoConfigurationDictionary();
}
public WeChatWorkApplicationConfiguration(string agentId, string secret)
{
AgentId = agentId;
Secret = secret;
CryptoKeys = new WeChatWorkCryptoConfigurationDictionary();
}
[NotNull]
public WeChatWorkCryptoConfiguration GetCryptoConfiguration(string feture)
{
return CryptoKeys.GetCryptoConfigurationOrNull(feture)
?? throw new AbpWeChatWorkException("WeChatWork:101404", $"WeChat Work crypto was not found configuration with feture '{feture}' .")
.WithData("AgentId", AgentId)
.WithData("Feture", feture);
}
}

14
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/WeChatWorkApplicationConfigurationDictionary.cs

@ -1,14 +0,0 @@
using JetBrains.Annotations;
using System.Collections.Generic;
namespace LINGYUN.Abp.WeChat.Work;
public class WeChatWorkApplicationConfigurationDictionary : Dictionary<string, WeChatWorkApplicationConfiguration>
{
[NotNull]
public WeChatWorkApplicationConfiguration GetConfiguration(string agentId)
{
return this.GetOrDefault(agentId)
?? throw new AbpWeChatWorkException("WeChatWork:100404", $"WeChat Work application was not found configuration with agent '{agentId}' .")
.WithData("AgentId", agentId);
}
}

11
aspnet-core/framework/wechat/LINGYUN.Abp.WeChat.Work/LINGYUN/Abp/WeChat/Work/WeChatWorkOptions.cs

@ -1,11 +0,0 @@
namespace LINGYUN.Abp.WeChat.Work;
public class WeChatWorkOptions
{
public WeChatWorkApplicationConfigurationDictionary Applications { get; set; }
public WeChatWorkOptions()
{
Applications = new WeChatWorkApplicationConfigurationDictionary();
}
}

272
aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore/DataSeeder/ClientDataSeederContributor.cs

@ -1,19 +1,11 @@
using LINGYUN.Abp.IdentityServer.IdentityResources;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration;
using OpenIddict.Abstractions;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Guids;
using Volo.Abp.IdentityServer.ApiResources;
using Volo.Abp.IdentityServer.ApiScopes;
using Volo.Abp.IdentityServer.Clients;
using Volo.Abp.IdentityServer.IdentityResources;
using Volo.Abp.MultiTenancy;
using Volo.Abp.PermissionManagement;
@ -24,13 +16,6 @@ public class ClientDataSeederContributor : IDataSeedContributor, ITransientDepen
private readonly IOpenIddictApplicationManager _applicationManager;
private readonly IOpenIddictScopeManager _scopeManager;
private readonly IClientRepository _clientRepository;
private readonly IApiResourceRepository _apiResourceRepository;
private readonly IApiScopeRepository _apiScopeRepository;
private readonly ICustomIdentityResourceDataSeeder _customIdentityResourceDataSeeder;
private readonly IIdentityResourceDataSeeder _identityResourceDataSeeder;
private readonly IGuidGenerator _guidGenerator;
private readonly IPermissionDataSeeder _permissionDataSeeder;
private readonly IConfiguration _configuration;
private readonly ICurrentTenant _currentTenant;
@ -38,24 +23,12 @@ public class ClientDataSeederContributor : IDataSeedContributor, ITransientDepen
public ClientDataSeederContributor(
IOpenIddictApplicationManager applicationManager,
IOpenIddictScopeManager scopeManager,
IClientRepository clientRepository,
IApiResourceRepository apiResourceRepository,
IApiScopeRepository apiScopeRepository,
ICustomIdentityResourceDataSeeder customIdentityResourceDataSeeder,
IIdentityResourceDataSeeder identityResourceDataSeeder,
IGuidGenerator guidGenerator,
IPermissionDataSeeder permissionDataSeeder,
IConfiguration configuration,
ICurrentTenant currentTenant)
{
_applicationManager = applicationManager;
_scopeManager = scopeManager;
_clientRepository = clientRepository;
_apiResourceRepository = apiResourceRepository;
_apiScopeRepository = apiScopeRepository;
_customIdentityResourceDataSeeder = customIdentityResourceDataSeeder;
_identityResourceDataSeeder = identityResourceDataSeeder;
_guidGenerator = guidGenerator;
_permissionDataSeeder = permissionDataSeeder;
_configuration = configuration;
_currentTenant = currentTenant;
@ -65,13 +38,7 @@ public class ClientDataSeederContributor : IDataSeedContributor, ITransientDepen
{
using (_currentTenant.Change(context.TenantId))
{
if (_configuration.GetValue<bool>("AuthServer:UseOpenIddict"))
{
await SeedOpenIddictAsync();
return;
}
await SeedIdentityServerAsync();
await SeedOpenIddictAsync();
}
}
@ -231,239 +198,4 @@ public class ClientDataSeederContributor : IDataSeedContributor, ITransientDepen
}
#endregion
#region IdentityServer
private async Task SeedIdentityServerAsync()
{
await _identityResourceDataSeeder.CreateStandardResourcesAsync();
await _customIdentityResourceDataSeeder.CreateCustomResourcesAsync();
await CreateApiResourcesAsync();
await CreateApiScopesAsync();
await CreateClientsAsync();
}
private async Task CreateApiScopesAsync()
{
await CreateApiScopeAsync("lingyun-abp-application");
}
private async Task CreateApiResourcesAsync()
{
var commonApiUserClaims = new[]
{
"email",
"email_verified",
"name",
"phone_number",
"phone_number_verified",
"role"
};
await CreateApiResourceAsync("lingyun-abp-application", commonApiUserClaims);
}
private async Task<ApiResource> CreateApiResourceAsync(string name, IEnumerable<string> claims, IEnumerable<string> secrets = null)
{
var apiResource = await _apiResourceRepository.FindByNameAsync(name);
if (apiResource == null)
{
apiResource = await _apiResourceRepository.InsertAsync(
new ApiResource(
_guidGenerator.Create(),
name,
name + " API"
),
autoSave: true
);
}
foreach (var claim in claims)
{
if (apiResource.FindClaim(claim) == null)
{
apiResource.AddUserClaim(claim);
}
}
if (secrets != null)
{
foreach (var secret in secrets)
{
if (apiResource.FindSecret(secret) == null)
{
apiResource.AddSecret(secret);
}
}
}
return await _apiResourceRepository.UpdateAsync(apiResource);
}
private async Task<ApiScope> CreateApiScopeAsync(string name)
{
var apiScope = await _apiScopeRepository.FindByNameAsync(name);
if (apiScope == null)
{
apiScope = await _apiScopeRepository.InsertAsync(
new ApiScope(
_guidGenerator.Create(),
name,
name + " API"
),
autoSave: true
);
}
return apiScope;
}
private async Task CreateClientsAsync()
{
string commonSecret = IdentityServer4.Models.HashExtensions.Sha256("1q2w3e*");
var commonScopes = new[]
{
"email",
"openid",
"profile",
"role",
"phone",
"address",
"offline_access" // 加上刷新,
};
var configurationSection = _configuration.GetSection("IdentityServer:Clients");
var vueClientId = configurationSection["VueAdmin:ClientId"];
if (!vueClientId.IsNullOrWhiteSpace())
{
var vueClientPermissions = new string[1]
{
"AbpIdentity.UserLookup"
};
var vueClientRootUrl = configurationSection["VueAdmin:RootUrl"].EnsureEndsWith('/');
await CreateClientAsync(
vueClientId,
commonScopes.Union(new[] { "lingyun-abp-application" }),
new[] { "password", "client_credentials", "implicit", "phone_verify", "wx-mp" },
commonSecret,
redirectUri: $"{vueClientRootUrl}signin-oidc",
postLogoutRedirectUri: $"{vueClientRootUrl}signout-callback-oidc",
corsOrigins: configurationSection["CorsOrigins"],
permissions: vueClientPermissions
);
}
// InternalService 内部服务间通讯客户端,必要的话需要在前端指定它拥有所有权限,当前项目仅预置用户查询权限
var internalServiceClientId = configurationSection["InternalService:ClientId"];
if (!internalServiceClientId.IsNullOrWhiteSpace())
{
var internalServicePermissions = new string[2]
{
"AbpIdentity.UserLookup","AbpIdentity.Users"
};
await CreateClientAsync(
internalServiceClientId,
commonScopes.Union(new[] { "lingyun-abp-application" }),
new[] { "client_credentials" },
commonSecret,
permissions: internalServicePermissions
);
}
}
private async Task<Client> CreateClientAsync(
string name,
IEnumerable<string> scopes,
IEnumerable<string> grantTypes,
string secret,
string redirectUri = null,
string postLogoutRedirectUri = null,
IEnumerable<string> permissions = null,
string corsOrigins = null)
{
var client = await _clientRepository.FindByClientIdAsync(name);
if (client == null)
{
client = await _clientRepository.InsertAsync(
new Client(
_guidGenerator.Create(),
name
)
{
ClientName = name,
ProtocolType = "oidc",
Description = name,
AlwaysIncludeUserClaimsInIdToken = true,
AllowOfflineAccess = true,
AbsoluteRefreshTokenLifetime = 10800, //3 hours
AccessTokenLifetime = 7200, //2 hours
AuthorizationCodeLifetime = 300,
IdentityTokenLifetime = 300,
RequireConsent = false
},
autoSave: true
);
}
foreach (var scope in scopes)
{
if (client.FindScope(scope) == null)
{
client.AddScope(scope);
}
}
foreach (var grantType in grantTypes)
{
if (client.FindGrantType(grantType) == null)
{
client.AddGrantType(grantType);
}
}
if (client.FindSecret(secret) == null)
{
client.AddSecret(secret);
}
if (redirectUri != null)
{
if (client.FindRedirectUri(redirectUri) == null)
{
client.AddRedirectUri(redirectUri);
}
}
if (postLogoutRedirectUri != null)
{
if (client.FindPostLogoutRedirectUri(postLogoutRedirectUri) == null)
{
client.AddPostLogoutRedirectUri(postLogoutRedirectUri);
}
}
if (corsOrigins != null)
{
var corsOriginsSplit = corsOrigins.Split(";");
foreach (var corsOrigin in corsOriginsSplit)
{
if (client.FindCorsOrigin(corsOrigin) == null)
{
client.AddCorsOrigin(corsOrigin);
}
}
}
if (permissions != null)
{
await _permissionDataSeeder.SeedAsync(ClientPermissionValueProvider.ProviderName, name, permissions);
}
return await _clientRepository.UpdateAsync(client);
}
#endregion
}

1
aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore/LY.MicroService.Applications.Single.EntityFrameworkCore.csproj

@ -35,7 +35,6 @@
<ProjectReference Include="..\..\modules\saas\LINGYUN.Abp.Saas.EntityFrameworkCore\LINGYUN.Abp.Saas.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\..\modules\platform\LINGYUN.Platform.EntityFrameworkCore\LINGYUN.Platform.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\..\modules\localization-management\LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore\LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\..\modules\identityServer\LINGYUN.Abp.IdentityServer.EntityFrameworkCore\LINGYUN.Abp.IdentityServer.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\..\modules\identity\LINGYUN.Abp.Identity.EntityFrameworkCore\LINGYUN.Abp.Identity.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\..\modules\text-templating\LINGYUN.Abp.TextTemplating.EntityFrameworkCore\LINGYUN.Abp.TextTemplating.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\..\modules\task-management\LINGYUN.Abp.TaskManagement.EntityFrameworkCore\LINGYUN.Abp.TaskManagement.EntityFrameworkCore.csproj" />

2
aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore/SingleMigrationsDbContext.cs

@ -15,7 +15,6 @@ using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.FeatureManagement.EntityFrameworkCore;
using Volo.Abp.Identity.EntityFrameworkCore;
using Volo.Abp.IdentityServer.EntityFrameworkCore;
using Volo.Abp.OpenIddict.EntityFrameworkCore;
using Volo.Abp.PermissionManagement.EntityFrameworkCore;
using Volo.Abp.SettingManagement.EntityFrameworkCore;
@ -37,7 +36,6 @@ public class SingleMigrationsDbContext : AbpDbContext<SingleMigrationsDbContext>
modelBuilder.ConfigureAuditLogging();
modelBuilder.ConfigureIdentity();
modelBuilder.ConfigureIdentityServer();
modelBuilder.ConfigureOpenIddict();
modelBuilder.ConfigureSaas();
modelBuilder.ConfigureFeatureManagement();

2
aspnet-core/migrations/LY.MicroService.Applications.Single.EntityFrameworkCore/SingleMigrationsEntityFrameworkCoreModule.cs

@ -2,7 +2,6 @@ using LINGYUN.Abp.AuditLogging.EntityFrameworkCore;
using LINGYUN.Abp.Data.DbMigrator;
using LINGYUN.Abp.Gdpr.EntityFrameworkCore;
using LINGYUN.Abp.Identity.EntityFrameworkCore;
using LINGYUN.Abp.IdentityServer.EntityFrameworkCore;
using LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore;
using LINGYUN.Abp.MessageService.EntityFrameworkCore;
using LINGYUN.Abp.Notifications.EntityFrameworkCore;
@ -32,7 +31,6 @@ namespace LY.MicroService.Applications.Single.EntityFrameworkCore;
typeof(PlatformEntityFrameworkCoreModule),
typeof(AbpLocalizationManagementEntityFrameworkCoreModule),
typeof(AbpIdentityEntityFrameworkCoreModule),
typeof(AbpIdentityServerEntityFrameworkCoreModule),
typeof(AbpOpenIddictEntityFrameworkCoreModule),
typeof(AbpTextTemplatingEntityFrameworkCoreModule),
typeof(WebhooksManagementEntityFrameworkCoreModule),

5
aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Areas/Account/Controllers/Models/QrCodeUserInfoResult.cs

@ -1,7 +1,10 @@
namespace LINGYUN.Abp.Account.Web.Areas.Account.Controllers.Models;
using System;
namespace LINGYUN.Abp.Account.Web.Areas.Account.Controllers.Models;
public class QrCodeUserInfoResult : QrCodeInfoResult
{
public Guid? TenantId { get; set; }
public string UserId { get; set; }
public string UserName { get; set; }
public string Picture { get; set; }

53
aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Areas/Account/Controllers/QrCodeLoginController.cs

@ -58,6 +58,7 @@ public class QrCodeLoginController : AbpControllerBase
Picture = qrCodeInfo.Picture,
UserId = qrCodeInfo.UserId,
UserName = qrCodeInfo.UserName,
TenantId = qrCodeInfo.TenantId
};
}
@ -66,22 +67,26 @@ public class QrCodeLoginController : AbpControllerBase
[Authorize]
public async Task<QrCodeUserInfoResult> ScanCodeAsync(string key)
{
var currentUser = await _userManager.GetByIdAsync(CurrentUser.GetId());
using (CurrentTenant.Change(CurrentUser.TenantId))
{
var currentUser = await _userManager.GetByIdAsync(CurrentUser.GetId());
var userName = CurrentUser.FindClaim(AbpClaimTypes.Name)?.Value ?? currentUser.UserName;
var userId = await _userManager.GetUserIdAsync(currentUser);
var userName = CurrentUser.FindClaim(AbpClaimTypes.Name)?.Value ?? currentUser.UserName;
var userId = await _userManager.GetUserIdAsync(currentUser);
var qrCodeInfo = await _qrCodeLoginProvider.ScanCodeAsync(key,
new QrCodeScanParams(userId, userName, currentUser.TenantId));
var qrCodeInfo = await _qrCodeLoginProvider.ScanCodeAsync(key,
new QrCodeScanParams(userId, userName, currentUser.TenantId));
return new QrCodeUserInfoResult
{
Key = qrCodeInfo.Key,
Status = qrCodeInfo.Status,
Picture = qrCodeInfo.Picture,
UserId = qrCodeInfo.UserId,
UserName = qrCodeInfo.UserName
};
return new QrCodeUserInfoResult
{
Key = qrCodeInfo.Key,
Status = qrCodeInfo.Status,
Picture = qrCodeInfo.Picture,
UserId = qrCodeInfo.UserId,
UserName = qrCodeInfo.UserName,
TenantId = qrCodeInfo.TenantId
};
}
}
[HttpPost]
@ -89,15 +94,19 @@ public class QrCodeLoginController : AbpControllerBase
[Authorize]
public async Task<QrCodeUserInfoResult> ConfirmCodeAsync(string key)
{
var qrCodeInfo = await _qrCodeLoginProvider.ConfirmCodeAsync(key);
return new QrCodeUserInfoResult
using (CurrentTenant.Change(CurrentUser.TenantId))
{
Key = qrCodeInfo.Key,
Status = qrCodeInfo.Status,
Picture = qrCodeInfo.Picture,
UserId = qrCodeInfo.UserId,
UserName = qrCodeInfo.UserName
};
var qrCodeInfo = await _qrCodeLoginProvider.ConfirmCodeAsync(key);
return new QrCodeUserInfoResult
{
Key = qrCodeInfo.Key,
Status = qrCodeInfo.Status,
Picture = qrCodeInfo.Picture,
UserId = qrCodeInfo.UserId,
UserName = qrCodeInfo.UserName,
TenantId = qrCodeInfo.TenantId
};
}
}
}

7
aspnet-core/modules/gdpr/LINGYUN.Abp.Gdpr.Domain.Identity/LINGYUN/Abp/Gdpr/Identity/AbpGdprIdentityUserAccountProvider.cs

@ -20,6 +20,11 @@ public class AbpGdprIdentityUserAccountProvider : GdprUserAccountProviderBase
var identityUser = await identityUserManager.GetByIdAsync(context.UserId);
(await identityUserManager.DeleteAsync(identityUser)).CheckErrors();
// 默认管理员账号保留
// TODO: 保留统一维护的地方?
if (identityUser.UserName == "admin")
{
(await identityUserManager.DeleteAsync(identityUser)).CheckErrors();
}
}
}

1
aspnet-core/modules/identity/LINGYUN.Abp.Identity.QrCode/LINGYUN/Abp/Identity/QrCode/QrCodeCacheItem.cs

@ -30,6 +30,7 @@ public class QrCodeCacheItem
UserId = UserId,
UserName = UserName,
Picture = Picture,
TenantId = TenantId,
};
qrCodeInfo.SetToken(Token);
qrCodeInfo.SetStatus(Status);

1
aspnet-core/modules/identity/LINGYUN.Abp.Identity.QrCode/LINGYUN/Abp/Identity/QrCode/QrCodeInfo.cs

@ -8,6 +8,7 @@ public class QrCodeInfo
public string Token { get; private set; }
public QrCodeStatus Status { get; private set; }
public string UserId { get; set; }
public Guid? TenantId { get; set; }
public string UserName { get; set; }
public string Picture { get; set; }

7
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat.Work/LINGYUN/Abp/IdentityServer/WeChat/Work/WeChatWorkGrantValidator.cs

@ -73,18 +73,17 @@ public class WeChatWorkGrantValidator : IExtensionGrantValidator
return;
}
var agentId = raw.Get(AbpWeChatWorkGlobalConsts.AgentId);
var code = raw.Get(AbpWeChatWorkGlobalConsts.Code);
if (agentId.IsNullOrWhiteSpace() || code.IsNullOrWhiteSpace())
if (code.IsNullOrWhiteSpace())
{
Logger.LogInformation("Invalid grant type: agentId or code not found");
Logger.LogInformation("Invalid grant type: code not found");
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, WeChatWorkLocalizer["InvalidGrant:AgentIdOrCodeNotFound"]);
return;
}
try
{
var userInfo = await WeChatWorkUserFinder.GetUserInfoAsync(agentId, code);
var userInfo = await WeChatWorkUserFinder.GetUserInfoAsync(code);
var currentUser = await UserManager.FindByLoginAsync(AbpWeChatWorkGlobalConsts.ProviderName, userInfo.UserId);
if (currentUser == null)

7
aspnet-core/modules/openIddict/LINGYUN.Abp.OpenIddict.WeChat.Work/LINGYUN/Abp/OpenIddict/WeChat/Work/WeChatWorkTokenExtensionGrant.cs

@ -44,11 +44,10 @@ public class WeChatWorkTokenExtensionGrant : ITokenExtensionGrant
var logger = GetRequiredService<ILogger<WeChatWorkTokenExtensionGrant>>(context);
var localizer = GetRequiredService<IStringLocalizer<AbpOpenIddictResource>>(context);
var agentId = context.Request.GetParameter(AbpWeChatWorkGlobalConsts.AgentId)?.ToString();
var code = context.Request.GetParameter(AbpWeChatWorkGlobalConsts.Code)?.ToString();
if (agentId.IsNullOrWhiteSpace() || code.IsNullOrWhiteSpace())
if (code.IsNullOrWhiteSpace())
{
logger.LogWarning("Invalid grant type: agentId or code not found");
logger.LogWarning("Invalid grant type: code not found");
var properties = new AuthenticationProperties(new Dictionary<string, string>
{
@ -63,7 +62,7 @@ public class WeChatWorkTokenExtensionGrant : ITokenExtensionGrant
try
{
var userInfo = await userFinder.GetUserInfoAsync(agentId, code);
var userInfo = await userFinder.GetUserInfoAsync(code);
var userManager = GetRequiredService<IdentityUserManager>(context);
var currentUser = await userManager.FindByLoginAsync(AbpWeChatWorkGlobalConsts.ProviderName, userInfo.UserId);

54
aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.WeChat.Work/LINGYUN/Abp/Notifications/WeChat/Work/WeChatWorkNotificationPublishProvider.cs

@ -1,12 +1,10 @@
using LINGYUN.Abp.RealTime.Localization;
using LINGYUN.Abp.WeChat.Work;
using LINGYUN.Abp.WeChat.Work.Authorize;
using LINGYUN.Abp.WeChat.Work.Features;
using LINGYUN.Abp.WeChat.Work.Messages;
using LINGYUN.Abp.WeChat.Work.Messages.Models;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Linq;
@ -25,21 +23,18 @@ public class WeChatWorkNotificationPublishProvider : NotificationPublishProvider
protected IWeChatWorkMessageSender WeChatWorkMessageSender { get; }
protected IWeChatWorkInternalUserFinder WeChatWorkInternalUserFinder { get; }
protected INotificationDefinitionManager NotificationDefinitionManager { get; }
protected WeChatWorkOptions WeChatWorkOptions { get; }
public WeChatWorkNotificationPublishProvider(
IFeatureChecker featureChecker,
IStringLocalizerFactory localizerFactory,
IWeChatWorkMessageSender weChatWorkMessageSender,
IWeChatWorkInternalUserFinder weChatWorkInternalUserFinder,
INotificationDefinitionManager notificationDefinitionManager,
IOptionsMonitor<WeChatWorkOptions> weChatWorkOptions)
INotificationDefinitionManager notificationDefinitionManager)
{
FeatureChecker = featureChecker;
LocalizerFactory = localizerFactory;
WeChatWorkMessageSender = weChatWorkMessageSender;
WeChatWorkInternalUserFinder = weChatWorkInternalUserFinder;
NotificationDefinitionManager = notificationDefinitionManager;
WeChatWorkOptions = weChatWorkOptions.CurrentValue;
}
protected async override Task<bool> CanPublishAsync(NotificationInfo notification, CancellationToken cancellationToken = default)
@ -69,18 +64,6 @@ public class WeChatWorkNotificationPublishProvider : NotificationPublishProvider
{
return;
}
// 发送到所有应用
if (agentId.Contains("@all"))
{
foreach (var application in WeChatWorkOptions.Applications)
{
sendToAgentIds.Add(application.Key);
}
}
else
{
sendToAgentIds.AddRange(agentId.Split(';'));
}
var title = "";
var message = "";
@ -112,27 +95,24 @@ public class WeChatWorkNotificationPublishProvider : NotificationPublishProvider
}
}
foreach (var sendToAgentId in sendToAgentIds)
{
var findUserList = await WeChatWorkInternalUserFinder
.FindUserIdentifierListAsync(sendToAgentId, identifiers.Select(id => id.UserId));
if (!findUserList.Any())
{
continue;
}
var findUserList = await WeChatWorkInternalUserFinder
.FindUserIdentifierListAsync(agentId, identifiers.Select(id => id.UserId));
await PublishToAgentAsync(
sendToAgentId,
notification,
findUserList.JoinAsString("|"),
title,
message,
description,
toParty,
toTag,
cancellationToken);
if (!findUserList.Any())
{
return;
}
await PublishToAgentAsync(
agentId,
notification,
findUserList.JoinAsString("|"),
title,
message,
description,
toParty,
toTag,
cancellationToken);
}
protected async virtual Task PublishToAgentAsync(

91
aspnet-core/services/LY.MicroService.Applications.Single/Authentication/AbpCookieAuthenticationHandler.cs

@ -1,91 +0,0 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.Extensions.Options;
using Microsoft.Net.Http.Headers;
using System.Text.Encodings.Web;
using Volo.Abp.Http;
namespace LY.MicroService.Applications.Single.Authentication;
public class AbpCookieAuthenticationHandler : CookieAuthenticationHandler
{
public AbpCookieAuthenticationHandler(
IOptionsMonitor<CookieAuthenticationOptions> options,
ILoggerFactory logger,
UrlEncoder encoder) : base(options, logger, encoder)
{
}
public AbpCookieAuthenticationHandler(
IOptionsMonitor<CookieAuthenticationOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock) : base(options, logger, encoder, clock)
{
}
protected const string XRequestFromHeader = "X-Request-From";
protected const string DontRedirectRequestFromHeader = "vben";
protected override Task InitializeEventsAsync()
{
var events = new CookieAuthenticationEvents
{
OnRedirectToLogin = ctx =>
{
if (string.Equals(ctx.Request.Headers[XRequestFromHeader], DontRedirectRequestFromHeader, StringComparison.Ordinal))
{
// ctx.Response.Headers.Location = ctx.RedirectUri;
ctx.Response.StatusCode = 401;
}
else
{
ctx.Response.Redirect(ctx.RedirectUri);
}
return Task.CompletedTask;
},
OnRedirectToAccessDenied = ctx =>
{
if (string.Equals(ctx.Request.Headers[XRequestFromHeader], DontRedirectRequestFromHeader, StringComparison.Ordinal))
{
// ctx.Response.Headers.Location = ctx.RedirectUri;
ctx.Response.StatusCode = 401;
}
else
{
ctx.Response.Redirect(ctx.RedirectUri);
}
return Task.CompletedTask;
},
OnRedirectToLogout = ctx =>
{
if (string.Equals(ctx.Request.Headers[XRequestFromHeader], DontRedirectRequestFromHeader, StringComparison.Ordinal))
{
// ctx.Response.Headers.Location = ctx.RedirectUri;
ctx.Response.StatusCode = 401;
}
else
{
ctx.Response.Redirect(ctx.RedirectUri);
}
return Task.CompletedTask;
},
OnRedirectToReturnUrl = ctx =>
{
if (string.Equals(ctx.Request.Headers[XRequestFromHeader], DontRedirectRequestFromHeader, StringComparison.Ordinal))
{
// ctx.Response.Headers.Location = ctx.RedirectUri;
ctx.Response.StatusCode = 401;
}
else
{
ctx.Response.Redirect(ctx.RedirectUri);
}
return Task.CompletedTask;
}
};
Events = events;
return Task.CompletedTask;
}
}

16
aspnet-core/services/LY.MicroService.Applications.Single/DataSeeder/DataSeederWorker.cs

@ -1,16 +0,0 @@
namespace LY.MicroService.Applications.Single.DataSeeder;
public class DataSeederWorker : BackgroundService
{
protected IDataSeeder DataSeeder { get; }
public DataSeederWorker(IDataSeeder dataSeeder)
{
DataSeeder = dataSeeder;
}
protected async override Task ExecuteAsync(CancellationToken stoppingToken)
{
await DataSeeder.SeedAsync();
}
}

14
aspnet-core/services/LY.MicroService.Applications.Single/GlobalUsings.cs

@ -2,6 +2,7 @@
global using Elsa;
global using Elsa.Options;
global using LINGYUN.Abp.Account;
global using LINGYUN.Abp.Account.Web.OpenIddict;
global using LINGYUN.Abp.Aliyun.Localization;
global using LINGYUN.Abp.Aliyun.SettingManagement;
global using LINGYUN.Abp.AspNetCore.HttpOverrides;
@ -42,6 +43,10 @@ global using LINGYUN.Abp.ExceptionHandling.Emailing;
global using LINGYUN.Abp.Exporter.MiniExcel;
global using LINGYUN.Abp.Features.LimitValidation;
global using LINGYUN.Abp.Features.LimitValidation.Redis.Client;
global using LINGYUN.Abp.Gdpr;
global using LINGYUN.Abp.Gdpr.EntityFrameworkCore;
global using LINGYUN.Abp.Gdpr.Identity;
global using LINGYUN.Abp.Gdpr.Web;
global using LINGYUN.Abp.Http.Client.Wrapper;
global using LINGYUN.Abp.Idempotent;
global using LINGYUN.Abp.Identity;
@ -52,7 +57,6 @@ global using LINGYUN.Abp.Identity.OrganizaztionUnits;
global using LINGYUN.Abp.Identity.Session;
global using LINGYUN.Abp.Identity.Session.AspNetCore;
global using LINGYUN.Abp.Identity.WeChat;
global using LINGYUN.Abp.IdentityServer.IdentityResources;
global using LINGYUN.Abp.IdGenerator;
global using LINGYUN.Abp.IM.SignalR;
global using LINGYUN.Abp.IP2Region;
@ -74,6 +78,7 @@ global using LINGYUN.Abp.OpenIddict.AspNetCore.Session;
global using LINGYUN.Abp.OpenIddict.LinkUser;
global using LINGYUN.Abp.OpenIddict.Permissions;
global using LINGYUN.Abp.OpenIddict.Portal;
global using LINGYUN.Abp.OpenIddict.QrCode;
global using LINGYUN.Abp.OpenIddict.Sms;
global using LINGYUN.Abp.OpenIddict.WeChat;
global using LINGYUN.Abp.OpenIddict.WeChat.Work;
@ -111,6 +116,7 @@ global using LINGYUN.Abp.WeChat.Official;
global using LINGYUN.Abp.WeChat.Official.Handlers;
global using LINGYUN.Abp.WeChat.SettingManagement;
global using LINGYUN.Abp.WeChat.Work;
global using LINGYUN.Abp.WeChat.Work.AspNetCore;
global using LINGYUN.Abp.WeChat.Work.Handlers;
global using LINGYUN.Abp.Wrapper;
global using LINGYUN.Platform;
@ -119,20 +125,16 @@ global using LINGYUN.Platform.HttpApi;
global using LINGYUN.Platform.Localization;
global using LINGYUN.Platform.Settings.VueVbenAdmin;
global using LINGYUN.Platform.Theme.VueVbenAdmin;
global using LY.MicroService.Applications.Single.Authentication;
global using LY.MicroService.Applications.Single.EntityFrameworkCore.MySql;
global using LY.MicroService.Applications.Single.IdentityResources;
global using LY.MicroService.Applications.Single.Messages;
global using Medallion.Threading;
global using Medallion.Threading.Redis;
global using Microsoft.AspNetCore.Authentication.Cookies;
global using Microsoft.AspNetCore.Authentication.JwtBearer;
global using Microsoft.AspNetCore.Cors;
global using Microsoft.AspNetCore.DataProtection;
global using Microsoft.AspNetCore.Identity;
global using Microsoft.AspNetCore.Server.Kestrel.Core;
global using Microsoft.Extensions.Caching.StackExchangeRedis;
global using Microsoft.Extensions.DependencyInjection.Extensions;
global using Microsoft.IdentityModel.Logging;
global using Microsoft.OpenApi.Models;
global using MiniExcelLibs.Attributes;
@ -166,8 +168,6 @@ global using Volo.Abp.Features;
global using Volo.Abp.GlobalFeatures;
global using Volo.Abp.Http.Client;
global using Volo.Abp.Identity.Localization;
global using Volo.Abp.IdentityServer;
global using Volo.Abp.IdentityServer.Localization;
global using Volo.Abp.Imaging;
global using Volo.Abp.Json;
global using Volo.Abp.Json.SystemTextJson;

16
aspnet-core/services/LY.MicroService.Applications.Single/IdentityResources/CustomIdentityResources.cs

@ -1,16 +0,0 @@
namespace LY.MicroService.Applications.Single.IdentityResources;
public class CustomIdentityResources
{
public class AvatarUrl : IdentityServer4.Models.IdentityResource
{
public AvatarUrl()
{
Name = IdentityConsts.ClaimType.Avatar.Name;
DisplayName = IdentityConsts.ClaimType.Avatar.DisplayName;
Description = IdentityConsts.ClaimType.Avatar.Description;
Emphasize = true;
UserClaims = new string[] { IdentityConsts.ClaimType.Avatar.Name };
}
}
}

14
aspnet-core/services/LY.MicroService.Applications.Single/LY.MicroService.Applications.Single.csproj

@ -37,7 +37,6 @@
<PackageReference Include="Swashbuckle.AspNetCore" />
<PackageReference Include="Quartz.Serialization.Json" />
<PackageReference Include="Volo.Abp.Account.Web.OpenIddict" />
<!--<PackageReference Include="Volo.Abp.Account.Web.IdentityServer" />-->
<PackageReference Include="Volo.Abp.AspNetCore.Serilog" />
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy" />
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic" />
@ -50,13 +49,11 @@
<PackageReference Include="Volo.Abp.FeatureManagement.HttpApi" />
<PackageReference Include="Volo.Abp.PermissionManagement.Application" />
<PackageReference Include="Volo.Abp.PermissionManagement.Domain.Identity" />
<PackageReference Include="Volo.Abp.PermissionManagement.Domain.IdentityServer" />
<PackageReference Include="Volo.Abp.PermissionManagement.Domain.OpenIddict" />
<PackageReference Include="Volo.Abp.PermissionManagement.HttpApi" />
<PackageReference Include="Volo.Abp.Identity.AspNetCore" />
<PackageReference Include="Volo.Abp.Imaging.ImageSharp" />
<PackageReference Include="Volo.Abp.MailKit" />
<!--<PackageReference Include="Volo.Abp.IdentityServer.EntityFrameworkCore" />-->
<PackageReference Include="Volo.Abp.SettingManagement.EntityFrameworkCore" />
<PackageReference Include="Volo.Abp.PermissionManagement.EntityFrameworkCore" />
<PackageReference Include="Volo.Abp.OpenIddict.EntityFrameworkCore" />
@ -115,6 +112,7 @@
<ProjectReference Include="..\..\framework\wechat\LINGYUN.Abp.WeChat.Official.HttpApi\LINGYUN.Abp.WeChat.Official.HttpApi.csproj" />
<ProjectReference Include="..\..\framework\wechat\LINGYUN.Abp.WeChat.SettingManagement\LINGYUN.Abp.WeChat.SettingManagement.csproj" />
<ProjectReference Include="..\..\framework\wechat\LINGYUN.Abp.WeChat.Work.Application\LINGYUN.Abp.WeChat.Work.Application.csproj" />
<ProjectReference Include="..\..\framework\wechat\LINGYUN.Abp.WeChat.Work.AspNetCore\LINGYUN.Abp.WeChat.Work.AspNetCore.csproj" />
<ProjectReference Include="..\..\framework\wechat\LINGYUN.Abp.WeChat.Work.Handlers\LINGYUN.Abp.WeChat.Work.Handlers.csproj" />
<ProjectReference Include="..\..\framework\wechat\LINGYUN.Abp.WeChat.Work.HttpApi\LINGYUN.Abp.WeChat.Work.HttpApi.csproj" />
<ProjectReference Include="..\..\framework\wechat\LINGYUN.Abp.WeChat.Work\LINGYUN.Abp.WeChat.Work.csproj" />
@ -160,7 +158,6 @@
<ProjectReference Include="..\..\modules\gdpr\LINGYUN.Abp.Gdpr.EntityFrameworkCore\LINGYUN.Abp.Gdpr.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\..\modules\gdpr\LINGYUN.Abp.Gdpr.HttpApi\LINGYUN.Abp.Gdpr.HttpApi.csproj" />
<ProjectReference Include="..\..\modules\gdpr\LINGYUN.Abp.Gdpr.Web\LINGYUN.Abp.Gdpr.Web.csproj" />
<ProjectReference Include="..\..\modules\identityServer\LINGYUN.Abp.IdentityServer.Session\LINGYUN.Abp.IdentityServer.Session.csproj" />
<ProjectReference Include="..\..\modules\identity\LINGYUN.Abp.Identity.Application.Contracts\LINGYUN.Abp.Identity.Application.Contracts.csproj" />
<ProjectReference Include="..\..\modules\identity\LINGYUN.Abp.Identity.Application\LINGYUN.Abp.Identity.Application.csproj" />
<ProjectReference Include="..\..\modules\identity\LINGYUN.Abp.Identity.AspNetCore.Session\LINGYUN.Abp.Identity.AspNetCore.Session.csproj" />
@ -170,15 +167,6 @@
<ProjectReference Include="..\..\modules\identity\LINGYUN.Abp.Identity.HttpApi\LINGYUN.Abp.Identity.HttpApi.csproj" />
<ProjectReference Include="..\..\modules\identity\LINGYUN.Abp.Identity.Notifications\LINGYUN.Abp.Identity.Notifications.csproj" />
<ProjectReference Include="..\..\modules\identity\LINGYUN.Abp.Identity.OrganizaztionUnits\LINGYUN.Abp.Identity.OrganizaztionUnits.csproj" />
<ProjectReference Include="..\..\modules\identityServer\LINGYUN.Abp.IdentityServer.Application.Contracts\LINGYUN.Abp.IdentityServer.Application.Contracts.csproj" />
<ProjectReference Include="..\..\modules\identityServer\LINGYUN.Abp.IdentityServer.Application\LINGYUN.Abp.IdentityServer.Application.csproj" />
<ProjectReference Include="..\..\modules\identityServer\LINGYUN.Abp.IdentityServer.Domain\LINGYUN.Abp.IdentityServer.Domain.csproj" />
<ProjectReference Include="..\..\modules\identityServer\LINGYUN.Abp.IdentityServer.EntityFrameworkCore\LINGYUN.Abp.IdentityServer.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\..\modules\identityServer\LINGYUN.Abp.IdentityServer.HttpApi\LINGYUN.Abp.IdentityServer.HttpApi.csproj" />
<ProjectReference Include="..\..\modules\identityServer\LINGYUN.Abp.IdentityServer.LinkUser\LINGYUN.Abp.IdentityServer.LinkUser.csproj" />
<ProjectReference Include="..\..\modules\identityServer\LINGYUN.Abp.IdentityServer.Portal\LINGYUN.Abp.IdentityServer.Portal.csproj" />
<ProjectReference Include="..\..\modules\identityServer\LINGYUN.Abp.IdentityServer.SmsValidator\LINGYUN.Abp.IdentityServer.SmsValidator.csproj" />
<ProjectReference Include="..\..\modules\identityServer\LINGYUN.Abp.IdentityServer.WeChat.Work\LINGYUN.Abp.IdentityServer.WeChat.Work.csproj" />
<ProjectReference Include="..\..\modules\identity\LINGYUN.Abp.Identity.Session.AspNetCore\LINGYUN.Abp.Identity.Session.AspNetCore.csproj" />
<ProjectReference Include="..\..\modules\localization-management\LINGYUN.Abp.LocalizationManagement.Application.Contracts\LINGYUN.Abp.LocalizationManagement.Application.Contracts.csproj" />
<ProjectReference Include="..\..\modules\localization-management\LINGYUN.Abp.LocalizationManagement.Application\LINGYUN.Abp.LocalizationManagement.Application.csproj" />

131
aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.Configure.cs

@ -1,5 +1,7 @@
using LINGYUN.Abp.Identity.QrCode;
using LY.MicroService.Applications.Single.DataSeeder;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Extensions.DependencyInjection;
using OpenIddict.Validation.AspNetCore;
using VoloAbpExceptionHandlingOptions = Volo.Abp.AspNetCore.ExceptionHandling.AbpExceptionHandlingOptions;
namespace LY.MicroService.Applications.Single;
@ -97,38 +99,23 @@ public partial class MicroServiceApplicationsSingleModule
{
var certificate = new X509Certificate2(cerPath, cerConfig["Password"]);
if (configuration.GetValue<bool>("AuthServer:UseOpenIddict"))
PreConfigure<AbpOpenIddictAspNetCoreOptions>(options =>
{
PreConfigure<AbpOpenIddictAspNetCoreOptions>(options =>
{
//https://documentation.openiddict.com/configuration/encryption-and-signing-credentials.html
options.AddDevelopmentEncryptionAndSigningCertificate = false;
});
PreConfigure<OpenIddictServerBuilder>(builder =>
{
builder.AddSigningCertificate(certificate);
builder.AddEncryptionCertificate(certificate);
builder.UseDataProtection();
//https://documentation.openiddict.com/configuration/encryption-and-signing-credentials.html
options.AddDevelopmentEncryptionAndSigningCertificate = false;
});
// 禁用https
builder.UseAspNetCore()
.DisableTransportSecurityRequirement();
});
}
else
PreConfigure<OpenIddictServerBuilder>(builder =>
{
PreConfigure<AbpIdentityServerBuilderOptions>(options =>
{
options.AddDeveloperSigningCredential = false;
});
builder.AddSigningCertificate(certificate);
builder.AddEncryptionCertificate(certificate);
PreConfigure<IIdentityServerBuilder>(builder =>
{
builder.AddSigningCredential(certificate);
});
}
builder.UseDataProtection();
// 禁用https
builder.UseAspNetCore()
.DisableTransportSecurityRequirement();
});
}
}
else
@ -516,14 +503,6 @@ public partial class MicroServiceApplicationsSingleModule
});
}
private void ConfigureDataSeeder()
{
Configure<CustomIdentityResourceDataSeederOptions>(options =>
{
options.Resources.Add(new CustomIdentityResources.AvatarUrl());
});
}
private void ConfigureExceptionHandling()
{
// 自定义需要处理的异常
@ -669,6 +648,8 @@ public partial class MicroServiceApplicationsSingleModule
Configure<AbpClaimsPrincipalFactoryOptions>(options =>
{
options.IsDynamicClaimsEnabled = true;
options.DynamicClaims.AddIfNotContains(AbpClaimTypes.Picture);
});
Configure<IdentitySessionCleanupOptions>(options =>
{
@ -716,8 +697,7 @@ public partial class MicroServiceApplicationsSingleModule
typeof(TencentCloudResource),
typeof(WeChatResource),
typeof(PlatformResource),
typeof(AbpOpenIddictResource),
typeof(AbpIdentityServerResource));
typeof(AbpOpenIddictResource));
});
Configure<AbpLocalizationCultureMapOptions>(options =>
@ -745,6 +725,8 @@ public partial class MicroServiceApplicationsSingleModule
options.IsEnabled = true;
// options.IsWrapUnauthorizedEnabled = true;
options.IgnoreNamespaces.Add("Elsa");
// 微信消息不能包装
options.IgnoreNamespaces.Add("LINGYUN.Abp.WeChat");
});
}
@ -799,11 +781,6 @@ public partial class MicroServiceApplicationsSingleModule
// 用于消息中心短信集中发送
services.Replace<ISmsSender, PlatformSmsSender>(ServiceLifetime.Transient);
services.AddKeyedSingleton<ISmsSender, AliyunSmsSender>("DefaultSmsSender");
if (isDevelopment)
{
services.AddHostedService<DataSeederWorker>();
}
}
private void ConfigureUrls(IConfiguration configuration)
@ -829,29 +806,57 @@ public partial class MicroServiceApplicationsSingleModule
options.AutoValidate = false;
});
services.ForwardIdentityAuthenticationForBearer(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
services.AddAuthentication()
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
.AddAbpJwtBearer(options =>
{
configuration.GetSection("AuthServer").Bind(options);
options.Events ??= new JwtBearerEvents();
options.Events.OnMessageReceived = context =>
{
options.ExpireTimeSpan = TimeSpan.FromDays(365);
})
.AddAbpJwtBearer(options =>
var accessToken = context.Request.Query["access_token"];
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) &&
(path.StartsWithSegments("/api/files")))
{
context.Token = accessToken;
}
return Task.CompletedTask;
};
})
.AddWeChatWork(options =>
{
options.SignInScheme = IdentityConstants.ExternalScheme;
});
services.ConfigureApplicationCookie(options =>
{
options.Events.OnRedirectToLogin = (ctx) =>
{
if (ctx.Request.Path.Value.StartsWith("/api") ||
ctx.Request.Path.Value.StartsWith("/connect"))
{
configuration.GetSection("AuthServer").Bind(options);
ctx.Response.Clear();
ctx.Response.StatusCode = 401;
return Task.FromResult(0);
}
options.Events ??= new JwtBearerEvents();
options.Events.OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) &&
(path.StartsWithSegments("/api/files")))
{
context.Token = accessToken;
}
return Task.CompletedTask;
};
});
string authorization = ctx.Request.Headers.Authorization;
if (!authorization.IsNullOrWhiteSpace() &&
authorization.StartsWith("Bearer ", StringComparison.InvariantCultureIgnoreCase))
{
ctx.Response.Clear();
ctx.Response.ContentType = "application/json";
ctx.Response.StatusCode = StatusCodes.Status401Unauthorized;
return Task.CompletedTask;
}
ctx.Response.Redirect(ctx.RedirectUri);
return Task.CompletedTask;
};
});
if (!isDevelopment)
{
@ -863,8 +868,6 @@ public partial class MicroServiceApplicationsSingleModule
}
services.AddSameSiteCookiePolicy();
// 处理cookie中过时的ajax请求判断
services.Replace(ServiceDescriptor.Scoped<CookieAuthenticationHandler, AbpCookieAuthenticationHandler>());
}
private void ConfigureCors(IServiceCollection services, IConfiguration configuration)

15
aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.cs

@ -1,10 +1,3 @@
using LINGYUN.Abp.Account.Web.OpenIddict;
using LINGYUN.Abp.Gdpr;
using LINGYUN.Abp.Gdpr.EntityFrameworkCore;
using LINGYUN.Abp.Gdpr.Identity;
using LINGYUN.Abp.Gdpr.Web;
using LINGYUN.Abp.OpenIddict.QrCode;
namespace LY.MicroService.Applications.Single;
[DependsOn(
@ -101,11 +94,6 @@ namespace LY.MicroService.Applications.Single;
// 通知模块 实体框架
typeof(AbpNotificationsEntityFrameworkCoreModule),
//typeof(AbpIdentityServerSessionModule),
//typeof(AbpIdentityServerApplicationModule),
//typeof(AbpIdentityServerHttpApiModule),
//typeof(AbpIdentityServerEntityFrameworkCoreModule),
// OpenIddict扩展模块 自定义身份标识
typeof(LINGYUN.Abp.OpenIddict.AspNetCore.AbpOpenIddictAspNetCoreModule),
// OpenIddict扩展模块 会话
@ -339,6 +327,8 @@ namespace LY.MicroService.Applications.Single;
typeof(AbpWeChatOfficialHandlersModule),
// 微信模块 企业微信 事件处理
typeof(AbpWeChatWorkHandlersModule),
// 认证模块 企业微信
typeof(AbpWeChatWorkAspNetCoreModule),
// 微信模块 设置管理
typeof(AbpWeChatSettingManagementModule),
@ -401,7 +391,6 @@ public partial class MicroServiceApplicationsSingleModule : AbpModule
ConfigureAuditing();
ConfigureIdempotent();
ConfigureMvcUiTheme();
ConfigureDataSeeder();
ConfigureLocalization();
ConfigureKestrelServer();
ConfigureBackgroundTasks();

8
aspnet-core/services/LY.MicroService.Applications.Single/Program.cs

@ -51,22 +51,24 @@ var app = builder.Build();
await app.InitializeApplicationAsync();
app.UseMapRequestLocalization();
app.UseCookiePolicy();
app.UseForwardedHeaders();
app.UseAbpSecurityHeaders();
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
// app.UseAbpExceptionHandling();
app.UseCookiePolicy();
app.UseMapRequestLocalization();
app.UseCorrelationId();
app.MapAbpStaticAssets();
app.UseRouting();
app.UseCors();
app.UseAuthentication();
app.UseAbpOpenIddictValidation();
app.UseMultiTenancy();
app.UseUnitOfWork();
app.UseAbpOpenIddictValidation();
app.UseAbpSession();
app.UseDynamicClaims();
app.UseAuthorization();

14
aspnet-core/services/LY.MicroService.AuthServer/AuthServerModule.Configure.cs

@ -9,22 +9,24 @@ using LINGYUN.Abp.OpenIddict.WeChat;
using LINGYUN.Abp.Serilog.Enrichers.Application;
using LINGYUN.Abp.Serilog.Enrichers.UniqueId;
using LINGYUN.Abp.WeChat.Work;
using LY.MicroService.AuthServer.Authentication;
using Medallion.Threading;
using Medallion.Threading.Redis;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Caching.StackExchangeRedis;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.IdentityModel.Logging;
using OpenIddict.Validation.AspNetCore;
using OpenTelemetry.Metrics;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
@ -426,6 +428,8 @@ public partial class AuthServerModule
}
private void ConfigureSecurity(IServiceCollection services, IConfiguration configuration, bool isDevelopment = false)
{
services.ForwardIdentityAuthenticationForBearer(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
services
.AddAuthentication()
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
@ -435,6 +439,10 @@ public partial class AuthServerModule
.AddJwtBearer(options =>
{
configuration.GetSection("AuthServer").Bind(options);
})
.AddWeChatWork(options =>
{
options.SignInScheme = IdentityConstants.ExternalScheme;
});
if (!isDevelopment)
@ -446,8 +454,6 @@ public partial class AuthServerModule
.PersistKeysToStackExchangeRedis(redis, "LINGYUN.Abp.Application:DataProtection:Protection-Keys");
}
services.AddSameSiteCookiePolicy();
// 处理cookie中过时的ajax请求判断
services.Replace(ServiceDescriptor.Scoped<CookieAuthenticationHandler, AbpCookieAuthenticationHandler>());
}
private void ConfigureMultiTenancy(IConfiguration configuration)
{

2
aspnet-core/services/LY.MicroService.AuthServer/AuthServerModule.cs

@ -24,6 +24,7 @@ using LINGYUN.Abp.OpenIddict.WeChat.Work;
using LINGYUN.Abp.Serilog.Enrichers.Application;
using LINGYUN.Abp.Serilog.Enrichers.UniqueId;
using LINGYUN.Abp.Sms.Platform;
using LINGYUN.Abp.WeChat.Work.AspNetCore;
using LY.MicroService.AuthServer.EntityFrameworkCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
@ -60,6 +61,7 @@ namespace LY.MicroService.AuthServer;
typeof(AbpOpenIddictLinkUserModule),
typeof(AbpOpenIddictPortalModule),
typeof(AbpOpenIddictWeChatWorkModule),
typeof(AbpWeChatWorkAspNetCoreModule), // 实现企业微信登录
typeof(AbpAuthenticationQQModule),
typeof(AbpAuthenticationWeChatModule),
typeof(AbpIdentityOrganizaztionUnitsModule),

89
aspnet-core/services/LY.MicroService.AuthServer/Authentication/AbpCookieAuthenticationHandler.cs

@ -1,89 +0,0 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Volo.Abp.Http;
namespace LY.MicroService.AuthServer.Authentication;
public class AbpCookieAuthenticationHandler : CookieAuthenticationHandler
{
public AbpCookieAuthenticationHandler(
IOptionsMonitor<CookieAuthenticationOptions> options,
ILoggerFactory logger,
UrlEncoder encoder) : base(options, logger, encoder)
{
}
public AbpCookieAuthenticationHandler(
IOptionsMonitor<CookieAuthenticationOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock) : base(options, logger, encoder, clock)
{
}
protected override Task InitializeEventsAsync()
{
var events = new CookieAuthenticationEvents
{
OnRedirectToLogin = ctx =>
{
if (ctx.Request.CanAccept(MimeTypes.Application.Json))
{
ctx.Response.Headers.Location = ctx.RedirectUri;
ctx.Response.StatusCode = 401;
}
else
{
ctx.Response.Redirect(ctx.RedirectUri);
}
return Task.CompletedTask;
},
OnRedirectToAccessDenied = ctx =>
{
if (ctx.Request.CanAccept(MimeTypes.Application.Json))
{
ctx.Response.Headers.Location = ctx.RedirectUri;
ctx.Response.StatusCode = 403;
}
else
{
ctx.Response.Redirect(ctx.RedirectUri);
}
return Task.CompletedTask;
},
OnRedirectToLogout = ctx =>
{
if (ctx.Request.CanAccept(MimeTypes.Application.Json))
{
ctx.Response.Headers.Location = ctx.RedirectUri;
}
else
{
ctx.Response.Redirect(ctx.RedirectUri);
}
return Task.CompletedTask;
},
OnRedirectToReturnUrl = ctx =>
{
if (ctx.Request.CanAccept(MimeTypes.Application.Json))
{
ctx.Response.Headers.Location = ctx.RedirectUri;
}
else
{
ctx.Response.Redirect(ctx.RedirectUri);
}
return Task.CompletedTask;
}
};
Events = events;
return Task.CompletedTask;
}
}

1
aspnet-core/services/LY.MicroService.AuthServer/LY.MicroService.AuthServer.csproj

@ -65,6 +65,7 @@
<ProjectReference Include="..\..\framework\logging\LINGYUN.Abp.Serilog.Enrichers.Application\LINGYUN.Abp.Serilog.Enrichers.Application.csproj" />
<ProjectReference Include="..\..\framework\logging\LINGYUN.Abp.Serilog.Enrichers.UniqueId\LINGYUN.Abp.Serilog.Enrichers.UniqueId.csproj" />
<ProjectReference Include="..\..\framework\mvc\LINGYUN.Abp.AspNetCore.Mvc.Wrapper\LINGYUN.Abp.AspNetCore.Mvc.Wrapper.csproj" />
<ProjectReference Include="..\..\framework\wechat\LINGYUN.Abp.WeChat.Work.AspNetCore\LINGYUN.Abp.WeChat.Work.AspNetCore.csproj" />
</ItemGroup>
<ItemGroup>

Loading…
Cancel
Save