Browse Source

feat: 增加企业微信登录

pull/1133/head
colin 1 year 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 LINGYUN.Abp.Encryption.SM4;
using System.Text; using System.Text;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.Abp.Security;
using Volo.Abp.Security.Encryption; using Volo.Abp.Security.Encryption;
namespace LINGYUN.Abp.Encryption.Console; namespace LINGYUN.Abp.Encryption.Console;
[DependsOn(typeof(AbpEncryptionSM4Module))] //[DependsOn(typeof(AbpEncryptionSM4Module))]
[DependsOn(typeof(AbpSecurityModule))]
public class AbpEncryptionConsoleModule : AbpModule public class AbpEncryptionConsoleModule : AbpModule
{ {
public override void ConfigureServices(ServiceConfigurationContext context) 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(); application.Initialize();
WriteLine("D:解密 E:加密"); while (true)
var opt = ReadLine();
bool en = false;
if ("E".Equals(opt, StringComparison.InvariantCultureIgnoreCase))
{
en = true;
WriteLine("请输入需要加密的字符串");
}
else
{ {
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(); 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 public interface IWeChatWorkAuthorizeAppService : IApplicationService
{ {
Task<string> GenerateOAuth2AuthorizeAsync( Task<string> GenerateOAuth2AuthorizeAsync(
string agentid,
string redirectUri, string redirectUri,
string responseType = "code", string responseType = "code",
string scope = "snsapi_base"); string scope = "snsapi_base");
Task<string> GenerateOAuth2LoginAsync( Task<string> GenerateOAuth2LoginAsync(
string redirectUri, string redirectUri,
string loginType = "ServiceApp", string loginType = "ServiceApp");
string agentid = "");
} }

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> /// <remarks>
/// 参考文档:<see cref="https://developer.work.weixin.qq.com/document/path/90238"/> /// 参考文档:<see cref="https://developer.work.weixin.qq.com/document/path/90238"/>
/// </remarks> /// </remarks>
/// <param name="agentId"></param>
/// <param name="input"></param> /// <param name="input"></param>
/// <returns></returns> /// <returns></returns>
Task<string> Handle(string agentId, MessageValidationInput input); Task<string> Handle(MessageValidationInput input);
/// <summary> /// <summary>
/// 处理企业微信消息 /// 处理企业微信消息
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// 参考文档:<see cref="https://developer.work.weixin.qq.com/document/path/90238"/> /// 参考文档:<see cref="https://developer.work.weixin.qq.com/document/path/90238"/>
/// </remarks> /// </remarks>
/// <param name="agentId"></param>
/// <param name="input"></param> /// <param name="input"></param>
/// <returns></returns> /// <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; _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); var corpId = await SettingProvider.GetOrNullAsync(WeChatWorkSettingNames.Connection.CorpId);
Check.NotNullOrEmpty(corpId, nameof(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.Common.Messages;
using LINGYUN.Abp.WeChat.Work.Settings; using LINGYUN.Abp.WeChat.Work.Settings;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp; using Volo.Abp;
using Volo.Abp.Application.Services; using Volo.Abp.Application.Services;
@ -13,33 +13,40 @@ namespace LINGYUN.Abp.WeChat.Work.Message;
public class WeChatWorkMessageAppService : ApplicationService, IWeChatWorkMessageAppService public class WeChatWorkMessageAppService : ApplicationService, IWeChatWorkMessageAppService
{ {
private readonly IWeChatCryptoService _cryptoService; private readonly IWeChatCryptoService _cryptoService;
private readonly WeChatWorkOptions _options;
private readonly IDistributedEventBus _distributedEventBus; private readonly IDistributedEventBus _distributedEventBus;
private readonly IMessageResolver _messageResolver; private readonly IMessageResolver _messageResolver;
public WeChatWorkMessageAppService( public WeChatWorkMessageAppService(
IMessageResolver messageResolver, IMessageResolver messageResolver,
IWeChatCryptoService cryptoService, IWeChatCryptoService cryptoService,
IDistributedEventBus distributedEventBus, IDistributedEventBus distributedEventBus)
IOptionsMonitor<WeChatWorkOptions> options)
{ {
_cryptoService = cryptoService; _cryptoService = cryptoService;
_messageResolver = messageResolver; _messageResolver = messageResolver;
_distributedEventBus = distributedEventBus; _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(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( var echoData = new WeChatCryptoEchoData(
input.EchoStr, input.EchoStr,
corpId, corpId,
cryptoConfiguration.Token, token,
cryptoConfiguration.EncodingAESKey, aesKey,
input.Msg_Signature, input.Msg_Signature,
input.TimeStamp.ToString(), input.TimeStamp.ToString(),
input.Nonce); input.Nonce);
@ -49,18 +56,27 @@ public class WeChatWorkMessageAppService : ApplicationService, IWeChatWorkMessag
return echoStr; 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); var allSettings = await SettingProvider.GetAllAsync(
Check.NotNullOrEmpty(corpId, nameof(corpId)); new[] {
WeChatWorkSettingNames.Connection.CorpId,
WeChatWorkSettingNames.Connection.Token,
WeChatWorkSettingNames.Connection.EncodingAESKey,
});
var applicationConfiguration = _options.Applications.GetConfiguration(agentId); var corpId = allSettings.FirstOrDefault(x => x.Name == WeChatWorkSettingNames.Connection.CorpId)?.Value;
var cryptoConfiguration = applicationConfiguration.GetCryptoConfiguration("Message"); 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( var messageData = new MessageResolveData(
corpId, corpId,
cryptoConfiguration.Token, token,
cryptoConfiguration.EncodingAESKey, aesKey,
input.Msg_Signature, input.Msg_Signature,
input.TimeStamp, input.TimeStamp,
input.Nonce, 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] [HttpGet]
[Route("oauth2")] [Route("oauth2")]
public virtual Task<string> GenerateOAuth2AuthorizeAsync( public virtual Task<string> GenerateOAuth2AuthorizeAsync(
[FromQuery(Name = "agent_id")] string agentid,
[FromQuery(Name = "redirect_uri")] string redirectUri, [FromQuery(Name = "redirect_uri")] string redirectUri,
[FromQuery(Name = "response_type")] string responseType = "code", [FromQuery(Name = "response_type")] string responseType = "code",
[FromQuery] string scope = "snsapi_base") [FromQuery] string scope = "snsapi_base")
{ {
return _service.GenerateOAuth2AuthorizeAsync(agentid, redirectUri, responseType, scope); return _service.GenerateOAuth2AuthorizeAsync(redirectUri, responseType, scope);
} }
/// <summary> /// <summary>
@ -64,12 +63,11 @@ public class WeChatWorkAuthorizeController : AbpControllerBase, IWeChatWorkAutho
[HttpGet] [HttpGet]
[Route("oauth2/authorize")] [Route("oauth2/authorize")]
public async virtual Task<IActionResult> OAuth2AuthorizeAsync( public async virtual Task<IActionResult> OAuth2AuthorizeAsync(
[FromQuery(Name = "agent_id")] string agentid,
[FromQuery(Name = "redirect_uri")] string redirectUri, [FromQuery(Name = "redirect_uri")] string redirectUri,
[FromQuery(Name = "response_type")] string responseType = "code", [FromQuery(Name = "response_type")] string responseType = "code",
[FromQuery] string scope = "snsapi_base") [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); return Redirect(url);
} }
@ -88,10 +86,9 @@ public class WeChatWorkAuthorizeController : AbpControllerBase, IWeChatWorkAutho
[Route("oauth2/login")] [Route("oauth2/login")]
public virtual Task<string> GenerateOAuth2LoginAsync( public virtual Task<string> GenerateOAuth2LoginAsync(
[FromQuery(Name = "redirect_uri")] string redirectUri, [FromQuery(Name = "redirect_uri")] string redirectUri,
[FromQuery(Name = "login_type")] string loginType = "ServiceApp", [FromQuery(Name = "login_type")] string loginType = "ServiceApp")
[FromQuery(Name = "agent_id")] string agentid = "")
{ {
return _service.GenerateOAuth2LoginAsync(redirectUri, loginType, agentid); return _service.GenerateOAuth2LoginAsync(redirectUri, loginType);
} }
/// <summary> /// <summary>
@ -108,10 +105,9 @@ public class WeChatWorkAuthorizeController : AbpControllerBase, IWeChatWorkAutho
[Route("oauth2/login/sso")] [Route("oauth2/login/sso")]
public async virtual Task<IActionResult> OAuth2LoginAsync( public async virtual Task<IActionResult> OAuth2LoginAsync(
[FromQuery(Name = "redirect_uri")] string redirectUri, [FromQuery(Name = "redirect_uri")] string redirectUri,
[FromQuery(Name = "login_type")] string loginType = "ServiceApp", [FromQuery(Name = "login_type")] string loginType = "ServiceApp")
[FromQuery(Name = "agent_id")] string agentid = "")
{ {
var url = await _service.GenerateOAuth2LoginAsync(redirectUri, loginType, agentid); var url = await _service.GenerateOAuth2LoginAsync(redirectUri, loginType);
return Redirect(url); 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> /// <param name="input">企业微信服务器传递的验证消息参数</param>
/// <returns></returns> /// <returns></returns>
[HttpGet] [HttpGet]
[Route("{agentId}")] public virtual Task<string> Handle([FromQuery] MessageValidationInput input)
public virtual Task<string> Handle([FromRoute] string agentId, [FromQuery] MessageValidationInput input)
{ {
return _service.Handle(agentId, input); return _service.Handle(input);
} }
/// <summary> /// <summary>
@ -50,14 +49,13 @@ public class WeChatWorkMessageController : AbpControllerBase, IWeChatWorkMessage
/// <param name="input">企业微信服务器传递的消息参数</param> /// <param name="input">企业微信服务器传递的消息参数</param>
/// <returns></returns> /// <returns></returns>
[HttpPost] [HttpPost]
[Route("{agentId}")] public async virtual Task<string> Handle([FromQuery] MessageHandleInput input)
public async virtual Task<string> Handle([FromRoute] string agentId, [FromQuery] MessageHandleInput input)
{ {
using var reader = new StreamReader(Request.Body, Encoding.UTF8); using var reader = new StreamReader(Request.Body, Encoding.UTF8);
var content = await reader.ReadToEndAsync(); var content = await reader.ReadToEndAsync();
input.Data = content; 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> /// <summary>
/// 企业微信对应的Provider名称 /// 企业微信对应的Provider名称
/// </summary> /// </summary>
public static string ProviderName { get; set; } = "WeChat.Work"; public static string ProviderName { get; set; } = "WeChat.WeCom";
/// <summary> /// <summary>
/// 企业微信授权类型 /// 企业微信授权类型
/// </summary> /// </summary>
public static string GrantType { get; set; } = "wx-work"; public static string GrantType { get; set; } = "wecom";
/// <summary> /// <summary>
/// 企业微信授权名称 /// 企业微信授权名称
/// </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) public override void ConfigureServices(ServiceConfigurationContext context)
{ {
var configuration = context.Services.GetConfiguration();
Configure<WeChatWorkOptions>(configuration.GetSection("WeChat:Work"));
Configure<AbpVirtualFileSystemOptions>(options => Configure<AbpVirtualFileSystemOptions>(options =>
{ {
options.FileSets.AddEmbedded<AbpWeChatWorkModule>(); 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> /// <remarks>
/// 参考:https://developer.work.weixin.qq.com/document/path/91022 /// 参考:https://developer.work.weixin.qq.com/document/path/91022
/// </remarks> /// </remarks>
/// <param name="agentid"></param>
/// <param name="redirectUri"></param> /// <param name="redirectUri"></param>
/// <param name="state"></param> /// <param name="state"></param>
/// <param name="responseType"></param> /// <param name="responseType"></param>
/// <param name="scope"></param> /// <param name="scope"></param>
/// <returns></returns> /// <returns></returns>
Task<string> GenerateOAuth2AuthorizeAsync( Task<string> GenerateOAuth2AuthorizeAsync(
string agentid,
string redirectUri, string redirectUri,
string state, string state,
string responseType = "code", string responseType = "code",
@ -24,7 +22,6 @@ public interface IWeChatWorkAuthorizeGenerator
/// <summary> /// <summary>
/// 构建网页登录链接 /// 构建网页登录链接
/// </summary> /// </summary>
/// <param name="appid"></param>
/// <param name="redirectUri"></param> /// <param name="redirectUri"></param>
/// <param name="state"></param> /// <param name="state"></param>
/// <param name="loginType"></param> /// <param name="loginType"></param>
@ -32,7 +29,6 @@ public interface IWeChatWorkAuthorizeGenerator
/// <param name="lang"></param> /// <param name="lang"></param>
/// <returns></returns> /// <returns></returns>
Task<string> GenerateOAuth2LoginAsync( Task<string> GenerateOAuth2LoginAsync(
string appid,
string redirectUri, string redirectUri,
string state, string state,
string loginType = "ServiceApp", 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 public interface IWeChatWorkUserFinder
{ {
Task<WeChatWorkUserInfo> GetUserInfoAsync( Task<WeChatWorkUserInfo> GetUserInfoAsync(
string agentId,
string code, string code,
CancellationToken cancellationToken = default); CancellationToken cancellationToken = default);
Task<WeChatWorkUserDetail> GetUserDetailAsync( Task<WeChatWorkUserDetail> GetUserDetailAsync(
string agentId,
string userTicket, string userTicket,
CancellationToken cancellationToken = default); 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( public async virtual Task<string> GenerateOAuth2AuthorizeAsync(
string agentid,
string redirectUri, string redirectUri,
string state, string state,
string responseType = "code", string responseType = "code",
string scope = "snsapi_base") string scope = "snsapi_base")
{ {
var corpId = await SettingProvider.GetOrNullAsync(WeChatWorkSettingNames.Connection.CorpId); var corpId = await SettingProvider.GetOrNullAsync(WeChatWorkSettingNames.Connection.CorpId);
var agentId = await SettingProvider.GetOrNullAsync(WeChatWorkSettingNames.Connection.AgentId);
Check.NotNullOrEmpty(corpId, nameof(corpId)); Check.NotNullOrEmpty(corpId, nameof(corpId));
Check.NotNullOrEmpty(agentId, nameof(agentId));
var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.OAuthClient); var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.OAuthClient);
@ -49,20 +50,23 @@ public class WeChatWorkAuthorizeGenerator : IWeChatWorkAuthorizeGenerator, ISing
.AppendFormat("&response_type={0}", responseType) .AppendFormat("&response_type={0}", responseType)
.AppendFormat("&scope={0}", scope) .AppendFormat("&scope={0}", scope)
.AppendFormat("&state={0}", state) .AppendFormat("&state={0}", state)
.AppendFormat("&agentid={0}", agentid) .AppendFormat("&agentid={0}", agentId)
.Append("#wechat_redirect"); .Append("#wechat_redirect");
return generatedUrlBuilder.ToString(); return generatedUrlBuilder.ToString();
} }
public virtual Task<string> GenerateOAuth2LoginAsync( public async virtual Task<string> GenerateOAuth2LoginAsync(
string appid, string appid,
string redirectUri, string redirectUri,
string state, string state,
string loginType = "ServiceApp", string loginType = "ServiceApp",
string agentid = "",
string lang = "zh") string lang = "zh")
{ {
var agentId = await SettingProvider.GetOrNullAsync(WeChatWorkSettingNames.Connection.AgentId);
Check.NotNullOrEmpty(agentId, nameof(agentId));
var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.LoginClient); var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.LoginClient);
var generatedUrlBuilder = new StringBuilder(); var generatedUrlBuilder = new StringBuilder();
@ -72,11 +76,11 @@ public class WeChatWorkAuthorizeGenerator : IWeChatWorkAuthorizeGenerator, ISing
.Append("wwlogin/sso/login") .Append("wwlogin/sso/login")
.AppendFormat("?login_type={0}", loginType) .AppendFormat("?login_type={0}", loginType)
.AppendFormat("&appid={0}", appid) .AppendFormat("&appid={0}", appid)
.AppendFormat("&agentid={0}", agentid) .AppendFormat("&agentid={0}", agentId)
.AppendFormat("&redirect_uri={0}", HttpUtility.UrlEncode(redirectUri)) .AppendFormat("&redirect_uri={0}", HttpUtility.UrlEncode(redirectUri))
.AppendFormat("&state={0}", state) .AppendFormat("&state={0}", state)
.AppendFormat("&lang={0}", lang); .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( public async virtual Task<WeChatWorkUserInfo> GetUserInfoAsync(
string agentId,
string code, string code,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
var token = await WeChatWorkTokenProvider.GetTokenAsync(agentId, cancellationToken); var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken);
var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient); var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient);
using var response = await client.GetUserInfoAsync(token.AccessToken, code, cancellationToken); 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( public async virtual Task<WeChatWorkUserDetail> GetUserDetailAsync(
string agentId,
string userTicket, string userTicket,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
var token = await WeChatWorkTokenProvider.GetTokenAsync(agentId, cancellationToken); var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken);
var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient); var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient);
var request = new WeChatWorkUserDetailRequest(userTicket); 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, WeChatWorkAppChatCreateRequest request,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
var token = await WeChatWorkTokenProvider.GetTokenAsync(request.AgentId, cancellationToken); var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken);
var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient); var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient);
using var response = await client.CreateAppChatAsync(token.AccessToken, request, cancellationToken); using var response = await client.CreateAppChatAsync(token.AccessToken, request, cancellationToken);
@ -40,7 +40,7 @@ public class WeChatWorkAppChatManager : IWeChatWorkAppChatManager, ISingletonDep
string chatId, string chatId,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
var token = await WeChatWorkTokenProvider.GetTokenAsync(agentId, cancellationToken); var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken);
var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient); var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient);
using var response = await client.GetAppChatAsync(token.AccessToken, agentId, cancellationToken); using var response = await client.GetAppChatAsync(token.AccessToken, agentId, cancellationToken);
@ -51,7 +51,7 @@ public class WeChatWorkAppChatManager : IWeChatWorkAppChatManager, ISingletonDep
WeChatWorkAppChatUpdateRequest request, WeChatWorkAppChatUpdateRequest request,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
var token = await WeChatWorkTokenProvider.GetTokenAsync(request.AgentId, cancellationToken); var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken);
var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient); var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient);
using var response = await client.UpdateAppChatAsync(token.AccessToken, request, cancellationToken); 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": "企业微信应用集成", "Description:WeChatWork": "企业微信应用集成",
"DisplayName:Connection": "连接参数", "DisplayName:Connection": "连接参数",
"Description:Connection": "企业微信连接参数", "Description:Connection": "企业微信连接参数",
"DisplayName:WeChatWork.Connection.CorpId": "Corp Id", "DisplayName:WeChatWork.Connection.CorpId": "企业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).", "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", "DisplayName:WeChatWork.EnabledQuickLogin": "Enabled Quick Login",
"Description:WeChatWork.EnabledQuickLogin": "Users can log in directly by scanning the code obtained when they are not registered.", "Description:WeChatWork.EnabledQuickLogin": "Users can log in directly by scanning the code obtained when they are not registered.",
"WeChatWork:100400": "处理企业微信服务器消息失败,请检查应用签名配置!", "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": "企业微信连接参数", "Description:Connection": "企业微信连接参数",
"DisplayName:WeChatWork.Connection.CorpId": "企业Id", "DisplayName:WeChatWork.Connection.CorpId": "企业Id",
"Description:WeChatWork.Connection.CorpId": "每个企业都拥有唯一的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": "启用快捷登录", "DisplayName:WeChatWork.EnabledQuickLogin": "启用快捷登录",
"Description:WeChatWork.EnabledQuickLogin": "用户可在未注册时通过扫码得到的code直接登录", "Description:WeChatWork.EnabledQuickLogin": "用户可在未注册时通过扫码得到的code直接登录",
"WeChatWork:100400": "处理企业微信服务器消息失败,请检查应用签名配置!", "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> /// <remarks>
/// API: <see cref="https://developer.work.weixin.qq.com/document/path/90253"/> /// API: <see cref="https://developer.work.weixin.qq.com/document/path/90253"/>
/// </remarks> /// </remarks>
/// <param name="agentId">应用标识</param>
/// <param name="type">媒体文件类型</param> /// <param name="type">媒体文件类型</param>
/// <param name="media">待上传文件</param> /// <param name="media">待上传文件</param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
Task<WeChatWorkMediaResponse> UploadAsync( Task<WeChatWorkMediaResponse> UploadAsync(
string agentId,
string type, string type,
IRemoteStreamContent media, IRemoteStreamContent media,
CancellationToken cancellationToken = default); CancellationToken cancellationToken = default);
@ -34,13 +32,11 @@ public interface IWeChatWorkMediaProvider
/// <remarks> /// <remarks>
/// API: <see cref="https://developer.work.weixin.qq.com/document/path/90254"/> /// API: <see cref="https://developer.work.weixin.qq.com/document/path/90254"/>
/// </remarks> /// </remarks>
/// <param name="agentId">应用标识</param>
/// <param name="mediaId">媒体文件id</param> /// <param name="mediaId">媒体文件id</param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
/// ///
Task<IRemoteStreamContent> GetAsync( Task<IRemoteStreamContent> GetAsync(
string agentId,
string mediaId, string mediaId,
CancellationToken cancellationToken = default); CancellationToken cancellationToken = default);
/// <summary> /// <summary>
@ -49,12 +45,10 @@ public interface IWeChatWorkMediaProvider
/// <remarks> /// <remarks>
/// API: <see cref="https://developer.work.weixin.qq.com/document/path/90256"/> /// API: <see cref="https://developer.work.weixin.qq.com/document/path/90256"/>
/// </remarks> /// </remarks>
/// <param name="agentId">应用标识</param>
/// <param name="image">待上传图片</param> /// <param name="image">待上传图片</param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
Task<WeChatWorkImageResponse> UploadImageAsync( Task<WeChatWorkImageResponse> UploadImageAsync(
string agentId,
IRemoteStreamContent image, IRemoteStreamContent image,
CancellationToken cancellationToken = default); 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( public async virtual Task<IRemoteStreamContent> GetAsync(
string agentId,
string mediaId, string mediaId,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
var token = await WeChatWorkTokenProvider.GetTokenAsync(agentId, cancellationToken); var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken);
var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient); var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient);
using var response = await client.GetMediaAsync( using var response = await client.GetMediaAsync(
@ -75,12 +74,11 @@ public class WeChatWorkMediaProvider : IWeChatWorkMediaProvider, ISingletonDepen
} }
public async virtual Task<WeChatWorkMediaResponse> UploadAsync( public async virtual Task<WeChatWorkMediaResponse> UploadAsync(
string agentId,
string type, string type,
IRemoteStreamContent media, IRemoteStreamContent media,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
var token = await WeChatWorkTokenProvider.GetTokenAsync(agentId, cancellationToken); var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken);
var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient); var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient);
var request = new WeChatWorkMediaRequest( var request = new WeChatWorkMediaRequest(
@ -95,11 +93,10 @@ public class WeChatWorkMediaProvider : IWeChatWorkMediaProvider, ISingletonDepen
} }
public async virtual Task<WeChatWorkImageResponse> UploadImageAsync( public async virtual Task<WeChatWorkImageResponse> UploadImageAsync(
string agentId,
IRemoteStreamContent image, IRemoteStreamContent image,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
var token = await WeChatWorkTokenProvider.GetTokenAsync(agentId, cancellationToken); var token = await WeChatWorkTokenProvider.GetTokenAsync(cancellationToken);
var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient); var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient);
var request = new WeChatWorkMediaRequest( var request = new WeChatWorkMediaRequest(
token.AccessToken, 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> /// <remarks>
/// 参考:https://developer.work.weixin.qq.com/document/path/94867 /// 参考:https://developer.work.weixin.qq.com/document/path/94867
/// </remarks> /// </remarks>
/// <param name="agentId">应用标识</param>
/// <param name="messageId">消息ID。从应用发送消息接口 <see cref="IWeChatWorkMessageSender"/> 处获得。</param> /// <param name="messageId">消息ID。从应用发送消息接口 <see cref="IWeChatWorkMessageSender"/> 处获得。</param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <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; 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 client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient);
var request = new WeChatWorkMessageReCallRequest( 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)] LimitPolicy.Days)]
public async virtual Task<WeChatWorkMessageResponse> SendAsync(WeChatWorkMessage message, CancellationToken cancellationToken = default) 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 client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient);
var request = new WeChatWorkMessageRequest<WeChatWorkMessage>( var request = new WeChatWorkMessageRequest<WeChatWorkMessage>(
@ -64,7 +64,7 @@ public class WeChatWorkMessageSender : IWeChatWorkMessageSender, ISingletonDepen
LimitPolicy.Minute)] LimitPolicy.Minute)]
public async virtual Task<WeChatWorkResponse> SendAsync(WeChatWorkAppChatMessage message, CancellationToken cancellationToken = default) 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 client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient);
var request = new WeChatWorkMessageRequest<WeChatWorkAppChatMessage>( 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 public static class AbpWeChatWorkClaimTypes
{ {
/// <summary> /// <summary>
/// 用户的唯一标识 /// 唯一标识
/// </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, ConfigurationSettingValueProvider.ProviderName,
GlobalSettingValueProvider.ProviderName, GlobalSettingValueProvider.ProviderName,
TenantSettingValueProvider.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 static class Connection
{ {
public const string Prefix = WeChatWorkSettingNames.Prefix + ".Connection"; public const string Prefix = WeChatWorkSettingNames.Prefix + ".Connection";
/// <summary>
/// 企业Id
/// </summary>
public static string CorpId = Prefix + ".CorpId"; 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="agentId">应用标识</param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
Task<WeChatWorkToken> GetTokenAsync(string agentId, CancellationToken cancellationToken = default); Task<WeChatWorkToken> GetTokenAsync(CancellationToken cancellationToken = default);
/// <summary> /// <summary>
/// 获取应用Token /// 获取应用Token
/// </summary> /// </summary>
@ -26,7 +26,8 @@ public interface IWeChatWorkTokenProvider
/// </remarks> /// </remarks>
/// <param name="corpId">企业标识</param> /// <param name="corpId">企业标识</param>
/// <param name="agentId">应用标识</param> /// <param name="agentId">应用标识</param>
/// <param name="secret">应用密钥</param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <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 Newtonsoft.Json;
using System.Text.Json.Serialization;
namespace LINGYUN.Abp.WeChat.Work.Token.Models; namespace LINGYUN.Abp.WeChat.Work.Token.Models;
@ -11,11 +12,14 @@ public class WeChatWorkTokenResponse : WeChatWorkResponse
/// 访问令牌 /// 访问令牌
/// </summary> /// </summary>
[JsonProperty("access_token")] [JsonProperty("access_token")]
[JsonPropertyName("access_token")]
public string AccessToken { get; set; } public string AccessToken { get; set; }
/// <summary> /// <summary>
/// 过期时间,单位(s) /// 过期时间,单位(s)
/// </summary> /// </summary>
[JsonProperty("expires_in")] [JsonProperty("expires_in")]
[JsonPropertyName("expires_in")]
[System.Text.Json.Serialization.JsonConverter(typeof(NumberToStringConverter))]
public int ExpiresIn { get; set; } public int ExpiresIn { get; set; }
public WeChatWorkToken ToWeChatWorkToken() 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.Settings;
using LINGYUN.Abp.WeChat.Work.Token.Models; using LINGYUN.Abp.WeChat.Work.Token.Models;
using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using System; using System;
using System.Net.Http; using System.Net.Http;
using System.Threading; using System.Threading;
@ -21,38 +19,35 @@ public class WeChatWorkTokenProvider : IWeChatWorkTokenProvider, ISingletonDepen
public ILogger<WeChatWorkTokenProvider> Logger { get; set; } public ILogger<WeChatWorkTokenProvider> Logger { get; set; }
protected ISettingProvider SettingProvider { get; } protected ISettingProvider SettingProvider { get; }
protected IHttpClientFactory HttpClientFactory { get; } protected IHttpClientFactory HttpClientFactory { get; }
protected IMemoryCache MemoryCache { get; }
protected IDistributedCache<WeChatWorkTokenCacheItem> WeChatWorkTokenCache { get; } protected IDistributedCache<WeChatWorkTokenCacheItem> WeChatWorkTokenCache { get; }
protected WeChatWorkOptions WeChatWorkOptions { get; }
public WeChatWorkTokenProvider( public WeChatWorkTokenProvider(
ISettingProvider settingProvider, ISettingProvider settingProvider,
IHttpClientFactory httpClientFactory, IHttpClientFactory httpClientFactory,
IMemoryCache memoryCache, IDistributedCache<WeChatWorkTokenCacheItem> cache)
IDistributedCache<WeChatWorkTokenCacheItem> cache,
IOptionsMonitor<WeChatWorkOptions> weChatWorkOptions)
{ {
HttpClientFactory = httpClientFactory; HttpClientFactory = httpClientFactory;
SettingProvider = settingProvider; SettingProvider = settingProvider;
MemoryCache = memoryCache;
WeChatWorkTokenCache = cache; WeChatWorkTokenCache = cache;
WeChatWorkOptions = weChatWorkOptions.CurrentValue;
Logger = NullLogger<WeChatWorkTokenProvider>.Instance; 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 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( public async virtual Task<WeChatWorkToken> GetTokenAsync(
string corpId, string corpId,
string agentId, string agentId,
string secret,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
return (await GetCacheItemAsync("WeChatWorkToken", corpId, agentId, cancellationToken)).Token; return (await GetCacheItemAsync("WeChatWorkToken", corpId, agentId, secret, cancellationToken)).Token;
} }
/// <summary> /// <summary>
/// 获取缓存中的Token配置 /// 获取缓存中的Token配置
@ -60,55 +55,23 @@ public class WeChatWorkTokenProvider : IWeChatWorkTokenProvider, ISingletonDepen
/// <param name="provider"></param> /// <param name="provider"></param>
/// <param name="corpId"></param> /// <param name="corpId"></param>
/// <param name="agentId"></param> /// <param name="agentId"></param>
/// <param name="secret"></param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
protected async virtual Task<WeChatWorkTokenCacheItem> GetCacheItemAsync( protected async virtual Task<WeChatWorkTokenCacheItem> GetCacheItemAsync(
string provider, string provider,
string corpId, string corpId,
string agentId, string agentId,
string secret,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
Check.NotNullOrEmpty(corpId, nameof(corpId)); Check.NotNullOrEmpty(corpId, nameof(corpId));
Check.NotNullOrEmpty(agentId, nameof(agentId)); Check.NotNullOrEmpty(agentId, nameof(agentId));
Check.NotNullOrEmpty(secret, nameof(secret));
var cacheKey = WeChatWorkTokenCacheItem.CalculateCacheKey(provider, corpId, agentId); var cacheKey = WeChatWorkTokenCacheItem.CalculateCacheKey(provider, corpId, agentId);
return await GetOrRefreshMemoryCacheAsync(cacheKey, provider, corpId, agentId); return await GetCacheItemAsync(cacheKey, provider, corpId, agentId, secret);
}
/// <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);
} }
/// <summary> /// <summary>
/// 获取或刷新分布式缓存中的Token配置 /// 获取或刷新分布式缓存中的Token配置
@ -117,13 +80,15 @@ public class WeChatWorkTokenProvider : IWeChatWorkTokenProvider, ISingletonDepen
/// <param name="provider"></param> /// <param name="provider"></param>
/// <param name="corpId"></param> /// <param name="corpId"></param>
/// <param name="agentId"></param> /// <param name="agentId"></param>
/// <param name="secret"></param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
protected async virtual Task<WeChatWorkTokenCacheItem> GetOrRefreshDistributedCacheAsync( protected async virtual Task<WeChatWorkTokenCacheItem> GetCacheItemAsync(
string cacheKey, string cacheKey,
string provider, string provider,
string corpId, string corpId,
string agentId, string agentId,
string secret,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
var cacheItem = await WeChatWorkTokenCache.GetAsync(cacheKey, token: cancellationToken); 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}"); Logger.LogDebug($"Not found WeChatWorkToken in the cache, getting from the httpClient: {cacheKey}");
var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient); var client = HttpClientFactory.CreateClient(AbpWeChatWorkGlobalConsts.ApiClient);
var applicationConfiguration = WeChatWorkOptions.Applications.GetConfiguration(agentId);
var request = new WeChatWorkTokenRequest var request = new WeChatWorkTokenRequest
{ {
CorpId = corpId, CorpId = corpId,
CorpSecret = applicationConfiguration.Secret, CorpSecret = secret,
}; };
using var response = await client.GetTokenAsync(request, cancellationToken); 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 OpenIddict.Abstractions;
using System; using System;
using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.Authorization.Permissions; using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Data; using Volo.Abp.Data;
using Volo.Abp.DependencyInjection; 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.MultiTenancy;
using Volo.Abp.PermissionManagement; using Volo.Abp.PermissionManagement;
@ -24,13 +16,6 @@ public class ClientDataSeederContributor : IDataSeedContributor, ITransientDepen
private readonly IOpenIddictApplicationManager _applicationManager; private readonly IOpenIddictApplicationManager _applicationManager;
private readonly IOpenIddictScopeManager _scopeManager; 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 IPermissionDataSeeder _permissionDataSeeder;
private readonly IConfiguration _configuration; private readonly IConfiguration _configuration;
private readonly ICurrentTenant _currentTenant; private readonly ICurrentTenant _currentTenant;
@ -38,24 +23,12 @@ public class ClientDataSeederContributor : IDataSeedContributor, ITransientDepen
public ClientDataSeederContributor( public ClientDataSeederContributor(
IOpenIddictApplicationManager applicationManager, IOpenIddictApplicationManager applicationManager,
IOpenIddictScopeManager scopeManager, IOpenIddictScopeManager scopeManager,
IClientRepository clientRepository,
IApiResourceRepository apiResourceRepository,
IApiScopeRepository apiScopeRepository,
ICustomIdentityResourceDataSeeder customIdentityResourceDataSeeder,
IIdentityResourceDataSeeder identityResourceDataSeeder,
IGuidGenerator guidGenerator,
IPermissionDataSeeder permissionDataSeeder, IPermissionDataSeeder permissionDataSeeder,
IConfiguration configuration, IConfiguration configuration,
ICurrentTenant currentTenant) ICurrentTenant currentTenant)
{ {
_applicationManager = applicationManager; _applicationManager = applicationManager;
_scopeManager = scopeManager; _scopeManager = scopeManager;
_clientRepository = clientRepository;
_apiResourceRepository = apiResourceRepository;
_apiScopeRepository = apiScopeRepository;
_customIdentityResourceDataSeeder = customIdentityResourceDataSeeder;
_identityResourceDataSeeder = identityResourceDataSeeder;
_guidGenerator = guidGenerator;
_permissionDataSeeder = permissionDataSeeder; _permissionDataSeeder = permissionDataSeeder;
_configuration = configuration; _configuration = configuration;
_currentTenant = currentTenant; _currentTenant = currentTenant;
@ -65,13 +38,7 @@ public class ClientDataSeederContributor : IDataSeedContributor, ITransientDepen
{ {
using (_currentTenant.Change(context.TenantId)) using (_currentTenant.Change(context.TenantId))
{ {
if (_configuration.GetValue<bool>("AuthServer:UseOpenIddict")) await SeedOpenIddictAsync();
{
await SeedOpenIddictAsync();
return;
}
await SeedIdentityServerAsync();
} }
} }
@ -231,239 +198,4 @@ public class ClientDataSeederContributor : IDataSeedContributor, ITransientDepen
} }
#endregion #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\saas\LINGYUN.Abp.Saas.EntityFrameworkCore\LINGYUN.Abp.Saas.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\..\modules\platform\LINGYUN.Platform.EntityFrameworkCore\LINGYUN.Platform.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\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\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\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" /> <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.EntityFrameworkCore;
using Volo.Abp.FeatureManagement.EntityFrameworkCore; using Volo.Abp.FeatureManagement.EntityFrameworkCore;
using Volo.Abp.Identity.EntityFrameworkCore; using Volo.Abp.Identity.EntityFrameworkCore;
using Volo.Abp.IdentityServer.EntityFrameworkCore;
using Volo.Abp.OpenIddict.EntityFrameworkCore; using Volo.Abp.OpenIddict.EntityFrameworkCore;
using Volo.Abp.PermissionManagement.EntityFrameworkCore; using Volo.Abp.PermissionManagement.EntityFrameworkCore;
using Volo.Abp.SettingManagement.EntityFrameworkCore; using Volo.Abp.SettingManagement.EntityFrameworkCore;
@ -37,7 +36,6 @@ public class SingleMigrationsDbContext : AbpDbContext<SingleMigrationsDbContext>
modelBuilder.ConfigureAuditLogging(); modelBuilder.ConfigureAuditLogging();
modelBuilder.ConfigureIdentity(); modelBuilder.ConfigureIdentity();
modelBuilder.ConfigureIdentityServer();
modelBuilder.ConfigureOpenIddict(); modelBuilder.ConfigureOpenIddict();
modelBuilder.ConfigureSaas(); modelBuilder.ConfigureSaas();
modelBuilder.ConfigureFeatureManagement(); 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.Data.DbMigrator;
using LINGYUN.Abp.Gdpr.EntityFrameworkCore; using LINGYUN.Abp.Gdpr.EntityFrameworkCore;
using LINGYUN.Abp.Identity.EntityFrameworkCore; using LINGYUN.Abp.Identity.EntityFrameworkCore;
using LINGYUN.Abp.IdentityServer.EntityFrameworkCore;
using LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore; using LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore;
using LINGYUN.Abp.MessageService.EntityFrameworkCore; using LINGYUN.Abp.MessageService.EntityFrameworkCore;
using LINGYUN.Abp.Notifications.EntityFrameworkCore; using LINGYUN.Abp.Notifications.EntityFrameworkCore;
@ -32,7 +31,6 @@ namespace LY.MicroService.Applications.Single.EntityFrameworkCore;
typeof(PlatformEntityFrameworkCoreModule), typeof(PlatformEntityFrameworkCoreModule),
typeof(AbpLocalizationManagementEntityFrameworkCoreModule), typeof(AbpLocalizationManagementEntityFrameworkCoreModule),
typeof(AbpIdentityEntityFrameworkCoreModule), typeof(AbpIdentityEntityFrameworkCoreModule),
typeof(AbpIdentityServerEntityFrameworkCoreModule),
typeof(AbpOpenIddictEntityFrameworkCoreModule), typeof(AbpOpenIddictEntityFrameworkCoreModule),
typeof(AbpTextTemplatingEntityFrameworkCoreModule), typeof(AbpTextTemplatingEntityFrameworkCoreModule),
typeof(WebhooksManagementEntityFrameworkCoreModule), 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 class QrCodeUserInfoResult : QrCodeInfoResult
{ {
public Guid? TenantId { get; set; }
public string UserId { get; set; } public string UserId { get; set; }
public string UserName { get; set; } public string UserName { get; set; }
public string Picture { 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, Picture = qrCodeInfo.Picture,
UserId = qrCodeInfo.UserId, UserId = qrCodeInfo.UserId,
UserName = qrCodeInfo.UserName, UserName = qrCodeInfo.UserName,
TenantId = qrCodeInfo.TenantId
}; };
} }
@ -66,22 +67,26 @@ public class QrCodeLoginController : AbpControllerBase
[Authorize] [Authorize]
public async Task<QrCodeUserInfoResult> ScanCodeAsync(string key) 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 userName = CurrentUser.FindClaim(AbpClaimTypes.Name)?.Value ?? currentUser.UserName;
var userId = await _userManager.GetUserIdAsync(currentUser); var userId = await _userManager.GetUserIdAsync(currentUser);
var qrCodeInfo = await _qrCodeLoginProvider.ScanCodeAsync(key, var qrCodeInfo = await _qrCodeLoginProvider.ScanCodeAsync(key,
new QrCodeScanParams(userId, userName, currentUser.TenantId)); new QrCodeScanParams(userId, userName, currentUser.TenantId));
return new QrCodeUserInfoResult return new QrCodeUserInfoResult
{ {
Key = qrCodeInfo.Key, Key = qrCodeInfo.Key,
Status = qrCodeInfo.Status, Status = qrCodeInfo.Status,
Picture = qrCodeInfo.Picture, Picture = qrCodeInfo.Picture,
UserId = qrCodeInfo.UserId, UserId = qrCodeInfo.UserId,
UserName = qrCodeInfo.UserName UserName = qrCodeInfo.UserName,
}; TenantId = qrCodeInfo.TenantId
};
}
} }
[HttpPost] [HttpPost]
@ -89,15 +94,19 @@ public class QrCodeLoginController : AbpControllerBase
[Authorize] [Authorize]
public async Task<QrCodeUserInfoResult> ConfirmCodeAsync(string key) public async Task<QrCodeUserInfoResult> ConfirmCodeAsync(string key)
{ {
var qrCodeInfo = await _qrCodeLoginProvider.ConfirmCodeAsync(key); using (CurrentTenant.Change(CurrentUser.TenantId))
return new QrCodeUserInfoResult
{ {
Key = qrCodeInfo.Key, var qrCodeInfo = await _qrCodeLoginProvider.ConfirmCodeAsync(key);
Status = qrCodeInfo.Status,
Picture = qrCodeInfo.Picture, return new QrCodeUserInfoResult
UserId = qrCodeInfo.UserId, {
UserName = qrCodeInfo.UserName 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); 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, UserId = UserId,
UserName = UserName, UserName = UserName,
Picture = Picture, Picture = Picture,
TenantId = TenantId,
}; };
qrCodeInfo.SetToken(Token); qrCodeInfo.SetToken(Token);
qrCodeInfo.SetStatus(Status); 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 string Token { get; private set; }
public QrCodeStatus Status { get; private set; } public QrCodeStatus Status { get; private set; }
public string UserId { get; set; } public string UserId { get; set; }
public Guid? TenantId { get; set; }
public string UserName { get; set; } public string UserName { get; set; }
public string Picture { 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; return;
} }
var agentId = raw.Get(AbpWeChatWorkGlobalConsts.AgentId);
var code = raw.Get(AbpWeChatWorkGlobalConsts.Code); 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"]); context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, WeChatWorkLocalizer["InvalidGrant:AgentIdOrCodeNotFound"]);
return; return;
} }
try try
{ {
var userInfo = await WeChatWorkUserFinder.GetUserInfoAsync(agentId, code); var userInfo = await WeChatWorkUserFinder.GetUserInfoAsync(code);
var currentUser = await UserManager.FindByLoginAsync(AbpWeChatWorkGlobalConsts.ProviderName, userInfo.UserId); var currentUser = await UserManager.FindByLoginAsync(AbpWeChatWorkGlobalConsts.ProviderName, userInfo.UserId);
if (currentUser == null) 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 logger = GetRequiredService<ILogger<WeChatWorkTokenExtensionGrant>>(context);
var localizer = GetRequiredService<IStringLocalizer<AbpOpenIddictResource>>(context); var localizer = GetRequiredService<IStringLocalizer<AbpOpenIddictResource>>(context);
var agentId = context.Request.GetParameter(AbpWeChatWorkGlobalConsts.AgentId)?.ToString();
var code = context.Request.GetParameter(AbpWeChatWorkGlobalConsts.Code)?.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> var properties = new AuthenticationProperties(new Dictionary<string, string>
{ {
@ -63,7 +62,7 @@ public class WeChatWorkTokenExtensionGrant : ITokenExtensionGrant
try try
{ {
var userInfo = await userFinder.GetUserInfoAsync(agentId, code); var userInfo = await userFinder.GetUserInfoAsync(code);
var userManager = GetRequiredService<IdentityUserManager>(context); var userManager = GetRequiredService<IdentityUserManager>(context);
var currentUser = await userManager.FindByLoginAsync(AbpWeChatWorkGlobalConsts.ProviderName, userInfo.UserId); 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.RealTime.Localization;
using LINGYUN.Abp.WeChat.Work;
using LINGYUN.Abp.WeChat.Work.Authorize; using LINGYUN.Abp.WeChat.Work.Authorize;
using LINGYUN.Abp.WeChat.Work.Features; using LINGYUN.Abp.WeChat.Work.Features;
using LINGYUN.Abp.WeChat.Work.Messages; using LINGYUN.Abp.WeChat.Work.Messages;
using LINGYUN.Abp.WeChat.Work.Messages.Models; using LINGYUN.Abp.WeChat.Work.Messages.Models;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -25,21 +23,18 @@ public class WeChatWorkNotificationPublishProvider : NotificationPublishProvider
protected IWeChatWorkMessageSender WeChatWorkMessageSender { get; } protected IWeChatWorkMessageSender WeChatWorkMessageSender { get; }
protected IWeChatWorkInternalUserFinder WeChatWorkInternalUserFinder { get; } protected IWeChatWorkInternalUserFinder WeChatWorkInternalUserFinder { get; }
protected INotificationDefinitionManager NotificationDefinitionManager { get; } protected INotificationDefinitionManager NotificationDefinitionManager { get; }
protected WeChatWorkOptions WeChatWorkOptions { get; }
public WeChatWorkNotificationPublishProvider( public WeChatWorkNotificationPublishProvider(
IFeatureChecker featureChecker, IFeatureChecker featureChecker,
IStringLocalizerFactory localizerFactory, IStringLocalizerFactory localizerFactory,
IWeChatWorkMessageSender weChatWorkMessageSender, IWeChatWorkMessageSender weChatWorkMessageSender,
IWeChatWorkInternalUserFinder weChatWorkInternalUserFinder, IWeChatWorkInternalUserFinder weChatWorkInternalUserFinder,
INotificationDefinitionManager notificationDefinitionManager, INotificationDefinitionManager notificationDefinitionManager)
IOptionsMonitor<WeChatWorkOptions> weChatWorkOptions)
{ {
FeatureChecker = featureChecker; FeatureChecker = featureChecker;
LocalizerFactory = localizerFactory; LocalizerFactory = localizerFactory;
WeChatWorkMessageSender = weChatWorkMessageSender; WeChatWorkMessageSender = weChatWorkMessageSender;
WeChatWorkInternalUserFinder = weChatWorkInternalUserFinder; WeChatWorkInternalUserFinder = weChatWorkInternalUserFinder;
NotificationDefinitionManager = notificationDefinitionManager; NotificationDefinitionManager = notificationDefinitionManager;
WeChatWorkOptions = weChatWorkOptions.CurrentValue;
} }
protected async override Task<bool> CanPublishAsync(NotificationInfo notification, CancellationToken cancellationToken = default) protected async override Task<bool> CanPublishAsync(NotificationInfo notification, CancellationToken cancellationToken = default)
@ -69,18 +64,6 @@ public class WeChatWorkNotificationPublishProvider : NotificationPublishProvider
{ {
return; return;
} }
// 发送到所有应用
if (agentId.Contains("@all"))
{
foreach (var application in WeChatWorkOptions.Applications)
{
sendToAgentIds.Add(application.Key);
}
}
else
{
sendToAgentIds.AddRange(agentId.Split(';'));
}
var title = ""; var title = "";
var message = ""; var message = "";
@ -112,27 +95,24 @@ public class WeChatWorkNotificationPublishProvider : NotificationPublishProvider
} }
} }
foreach (var sendToAgentId in sendToAgentIds) var findUserList = await WeChatWorkInternalUserFinder
{ .FindUserIdentifierListAsync(agentId, identifiers.Select(id => id.UserId));
var findUserList = await WeChatWorkInternalUserFinder
.FindUserIdentifierListAsync(sendToAgentId, identifiers.Select(id => id.UserId));
if (!findUserList.Any())
{
continue;
}
await PublishToAgentAsync( if (!findUserList.Any())
sendToAgentId, {
notification, return;
findUserList.JoinAsString("|"),
title,
message,
description,
toParty,
toTag,
cancellationToken);
} }
await PublishToAgentAsync(
agentId,
notification,
findUserList.JoinAsString("|"),
title,
message,
description,
toParty,
toTag,
cancellationToken);
} }
protected async virtual Task PublishToAgentAsync( 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;
global using Elsa.Options; global using Elsa.Options;
global using LINGYUN.Abp.Account; global using LINGYUN.Abp.Account;
global using LINGYUN.Abp.Account.Web.OpenIddict;
global using LINGYUN.Abp.Aliyun.Localization; global using LINGYUN.Abp.Aliyun.Localization;
global using LINGYUN.Abp.Aliyun.SettingManagement; global using LINGYUN.Abp.Aliyun.SettingManagement;
global using LINGYUN.Abp.AspNetCore.HttpOverrides; 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.Exporter.MiniExcel;
global using LINGYUN.Abp.Features.LimitValidation; global using LINGYUN.Abp.Features.LimitValidation;
global using LINGYUN.Abp.Features.LimitValidation.Redis.Client; 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.Http.Client.Wrapper;
global using LINGYUN.Abp.Idempotent; global using LINGYUN.Abp.Idempotent;
global using LINGYUN.Abp.Identity; 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;
global using LINGYUN.Abp.Identity.Session.AspNetCore; global using LINGYUN.Abp.Identity.Session.AspNetCore;
global using LINGYUN.Abp.Identity.WeChat; global using LINGYUN.Abp.Identity.WeChat;
global using LINGYUN.Abp.IdentityServer.IdentityResources;
global using LINGYUN.Abp.IdGenerator; global using LINGYUN.Abp.IdGenerator;
global using LINGYUN.Abp.IM.SignalR; global using LINGYUN.Abp.IM.SignalR;
global using LINGYUN.Abp.IP2Region; 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.LinkUser;
global using LINGYUN.Abp.OpenIddict.Permissions; global using LINGYUN.Abp.OpenIddict.Permissions;
global using LINGYUN.Abp.OpenIddict.Portal; global using LINGYUN.Abp.OpenIddict.Portal;
global using LINGYUN.Abp.OpenIddict.QrCode;
global using LINGYUN.Abp.OpenIddict.Sms; global using LINGYUN.Abp.OpenIddict.Sms;
global using LINGYUN.Abp.OpenIddict.WeChat; global using LINGYUN.Abp.OpenIddict.WeChat;
global using LINGYUN.Abp.OpenIddict.WeChat.Work; 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.Official.Handlers;
global using LINGYUN.Abp.WeChat.SettingManagement; global using LINGYUN.Abp.WeChat.SettingManagement;
global using LINGYUN.Abp.WeChat.Work; global using LINGYUN.Abp.WeChat.Work;
global using LINGYUN.Abp.WeChat.Work.AspNetCore;
global using LINGYUN.Abp.WeChat.Work.Handlers; global using LINGYUN.Abp.WeChat.Work.Handlers;
global using LINGYUN.Abp.Wrapper; global using LINGYUN.Abp.Wrapper;
global using LINGYUN.Platform; global using LINGYUN.Platform;
@ -119,20 +125,16 @@ global using LINGYUN.Platform.HttpApi;
global using LINGYUN.Platform.Localization; global using LINGYUN.Platform.Localization;
global using LINGYUN.Platform.Settings.VueVbenAdmin; global using LINGYUN.Platform.Settings.VueVbenAdmin;
global using LINGYUN.Platform.Theme.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.EntityFrameworkCore.MySql;
global using LY.MicroService.Applications.Single.IdentityResources;
global using LY.MicroService.Applications.Single.Messages; global using LY.MicroService.Applications.Single.Messages;
global using Medallion.Threading; global using Medallion.Threading;
global using Medallion.Threading.Redis; global using Medallion.Threading.Redis;
global using Microsoft.AspNetCore.Authentication.Cookies;
global using Microsoft.AspNetCore.Authentication.JwtBearer; global using Microsoft.AspNetCore.Authentication.JwtBearer;
global using Microsoft.AspNetCore.Cors; global using Microsoft.AspNetCore.Cors;
global using Microsoft.AspNetCore.DataProtection; global using Microsoft.AspNetCore.DataProtection;
global using Microsoft.AspNetCore.Identity; global using Microsoft.AspNetCore.Identity;
global using Microsoft.AspNetCore.Server.Kestrel.Core; global using Microsoft.AspNetCore.Server.Kestrel.Core;
global using Microsoft.Extensions.Caching.StackExchangeRedis; global using Microsoft.Extensions.Caching.StackExchangeRedis;
global using Microsoft.Extensions.DependencyInjection.Extensions;
global using Microsoft.IdentityModel.Logging; global using Microsoft.IdentityModel.Logging;
global using Microsoft.OpenApi.Models; global using Microsoft.OpenApi.Models;
global using MiniExcelLibs.Attributes; global using MiniExcelLibs.Attributes;
@ -166,8 +168,6 @@ global using Volo.Abp.Features;
global using Volo.Abp.GlobalFeatures; global using Volo.Abp.GlobalFeatures;
global using Volo.Abp.Http.Client; global using Volo.Abp.Http.Client;
global using Volo.Abp.Identity.Localization; 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.Imaging;
global using Volo.Abp.Json; global using Volo.Abp.Json;
global using Volo.Abp.Json.SystemTextJson; 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="Swashbuckle.AspNetCore" />
<PackageReference Include="Quartz.Serialization.Json" /> <PackageReference Include="Quartz.Serialization.Json" />
<PackageReference Include="Volo.Abp.Account.Web.OpenIddict" /> <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.Serilog" />
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy" /> <PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy" />
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic" /> <PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic" />
@ -50,13 +49,11 @@
<PackageReference Include="Volo.Abp.FeatureManagement.HttpApi" /> <PackageReference Include="Volo.Abp.FeatureManagement.HttpApi" />
<PackageReference Include="Volo.Abp.PermissionManagement.Application" /> <PackageReference Include="Volo.Abp.PermissionManagement.Application" />
<PackageReference Include="Volo.Abp.PermissionManagement.Domain.Identity" /> <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.Domain.OpenIddict" />
<PackageReference Include="Volo.Abp.PermissionManagement.HttpApi" /> <PackageReference Include="Volo.Abp.PermissionManagement.HttpApi" />
<PackageReference Include="Volo.Abp.Identity.AspNetCore" /> <PackageReference Include="Volo.Abp.Identity.AspNetCore" />
<PackageReference Include="Volo.Abp.Imaging.ImageSharp" /> <PackageReference Include="Volo.Abp.Imaging.ImageSharp" />
<PackageReference Include="Volo.Abp.MailKit" /> <PackageReference Include="Volo.Abp.MailKit" />
<!--<PackageReference Include="Volo.Abp.IdentityServer.EntityFrameworkCore" />-->
<PackageReference Include="Volo.Abp.SettingManagement.EntityFrameworkCore" /> <PackageReference Include="Volo.Abp.SettingManagement.EntityFrameworkCore" />
<PackageReference Include="Volo.Abp.PermissionManagement.EntityFrameworkCore" /> <PackageReference Include="Volo.Abp.PermissionManagement.EntityFrameworkCore" />
<PackageReference Include="Volo.Abp.OpenIddict.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.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.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.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.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.HttpApi\LINGYUN.Abp.WeChat.Work.HttpApi.csproj" />
<ProjectReference Include="..\..\framework\wechat\LINGYUN.Abp.WeChat.Work\LINGYUN.Abp.WeChat.Work.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.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.HttpApi\LINGYUN.Abp.Gdpr.HttpApi.csproj" />
<ProjectReference Include="..\..\modules\gdpr\LINGYUN.Abp.Gdpr.Web\LINGYUN.Abp.Gdpr.Web.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.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.Application\LINGYUN.Abp.Identity.Application.csproj" />
<ProjectReference Include="..\..\modules\identity\LINGYUN.Abp.Identity.AspNetCore.Session\LINGYUN.Abp.Identity.AspNetCore.Session.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.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.Notifications\LINGYUN.Abp.Identity.Notifications.csproj" />
<ProjectReference Include="..\..\modules\identity\LINGYUN.Abp.Identity.OrganizaztionUnits\LINGYUN.Abp.Identity.OrganizaztionUnits.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\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.Contracts\LINGYUN.Abp.LocalizationManagement.Application.Contracts.csproj" />
<ProjectReference Include="..\..\modules\localization-management\LINGYUN.Abp.LocalizationManagement.Application\LINGYUN.Abp.LocalizationManagement.Application.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 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; using VoloAbpExceptionHandlingOptions = Volo.Abp.AspNetCore.ExceptionHandling.AbpExceptionHandlingOptions;
namespace LY.MicroService.Applications.Single; namespace LY.MicroService.Applications.Single;
@ -97,38 +99,23 @@ public partial class MicroServiceApplicationsSingleModule
{ {
var certificate = new X509Certificate2(cerPath, cerConfig["Password"]); 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;
//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 PreConfigure<OpenIddictServerBuilder>(builder =>
builder.UseAspNetCore()
.DisableTransportSecurityRequirement();
});
}
else
{ {
PreConfigure<AbpIdentityServerBuilderOptions>(options => builder.AddSigningCertificate(certificate);
{ builder.AddEncryptionCertificate(certificate);
options.AddDeveloperSigningCredential = false;
});
PreConfigure<IIdentityServerBuilder>(builder => builder.UseDataProtection();
{
builder.AddSigningCredential(certificate); // 禁用https
}); builder.UseAspNetCore()
} .DisableTransportSecurityRequirement();
});
} }
} }
else else
@ -516,14 +503,6 @@ public partial class MicroServiceApplicationsSingleModule
}); });
} }
private void ConfigureDataSeeder()
{
Configure<CustomIdentityResourceDataSeederOptions>(options =>
{
options.Resources.Add(new CustomIdentityResources.AvatarUrl());
});
}
private void ConfigureExceptionHandling() private void ConfigureExceptionHandling()
{ {
// 自定义需要处理的异常 // 自定义需要处理的异常
@ -669,6 +648,8 @@ public partial class MicroServiceApplicationsSingleModule
Configure<AbpClaimsPrincipalFactoryOptions>(options => Configure<AbpClaimsPrincipalFactoryOptions>(options =>
{ {
options.IsDynamicClaimsEnabled = true; options.IsDynamicClaimsEnabled = true;
options.DynamicClaims.AddIfNotContains(AbpClaimTypes.Picture);
}); });
Configure<IdentitySessionCleanupOptions>(options => Configure<IdentitySessionCleanupOptions>(options =>
{ {
@ -716,8 +697,7 @@ public partial class MicroServiceApplicationsSingleModule
typeof(TencentCloudResource), typeof(TencentCloudResource),
typeof(WeChatResource), typeof(WeChatResource),
typeof(PlatformResource), typeof(PlatformResource),
typeof(AbpOpenIddictResource), typeof(AbpOpenIddictResource));
typeof(AbpIdentityServerResource));
}); });
Configure<AbpLocalizationCultureMapOptions>(options => Configure<AbpLocalizationCultureMapOptions>(options =>
@ -745,6 +725,8 @@ public partial class MicroServiceApplicationsSingleModule
options.IsEnabled = true; options.IsEnabled = true;
// options.IsWrapUnauthorizedEnabled = true; // options.IsWrapUnauthorizedEnabled = true;
options.IgnoreNamespaces.Add("Elsa"); 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.Replace<ISmsSender, PlatformSmsSender>(ServiceLifetime.Transient);
services.AddKeyedSingleton<ISmsSender, AliyunSmsSender>("DefaultSmsSender"); services.AddKeyedSingleton<ISmsSender, AliyunSmsSender>("DefaultSmsSender");
if (isDevelopment)
{
services.AddHostedService<DataSeederWorker>();
}
} }
private void ConfigureUrls(IConfiguration configuration) private void ConfigureUrls(IConfiguration configuration)
@ -829,29 +806,57 @@ public partial class MicroServiceApplicationsSingleModule
options.AutoValidate = false; options.AutoValidate = false;
}); });
services.ForwardIdentityAuthenticationForBearer(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
services.AddAuthentication() 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); var accessToken = context.Request.Query["access_token"];
}) var path = context.HttpContext.Request.Path;
.AddAbpJwtBearer(options => 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(); string authorization = ctx.Request.Headers.Authorization;
options.Events.OnMessageReceived = context => if (!authorization.IsNullOrWhiteSpace() &&
{ authorization.StartsWith("Bearer ", StringComparison.InvariantCultureIgnoreCase))
var accessToken = context.Request.Query["access_token"]; {
var path = context.HttpContext.Request.Path; ctx.Response.Clear();
if (!string.IsNullOrEmpty(accessToken) && ctx.Response.ContentType = "application/json";
(path.StartsWithSegments("/api/files"))) ctx.Response.StatusCode = StatusCodes.Status401Unauthorized;
{ return Task.CompletedTask;
context.Token = accessToken; }
}
return Task.CompletedTask; ctx.Response.Redirect(ctx.RedirectUri);
}; return Task.CompletedTask;
}); };
});
if (!isDevelopment) if (!isDevelopment)
{ {
@ -863,8 +868,6 @@ public partial class MicroServiceApplicationsSingleModule
} }
services.AddSameSiteCookiePolicy(); services.AddSameSiteCookiePolicy();
// 处理cookie中过时的ajax请求判断
services.Replace(ServiceDescriptor.Scoped<CookieAuthenticationHandler, AbpCookieAuthenticationHandler>());
} }
private void ConfigureCors(IServiceCollection services, IConfiguration configuration) 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; namespace LY.MicroService.Applications.Single;
[DependsOn( [DependsOn(
@ -101,11 +94,6 @@ namespace LY.MicroService.Applications.Single;
// 通知模块 实体框架 // 通知模块 实体框架
typeof(AbpNotificationsEntityFrameworkCoreModule), typeof(AbpNotificationsEntityFrameworkCoreModule),
//typeof(AbpIdentityServerSessionModule),
//typeof(AbpIdentityServerApplicationModule),
//typeof(AbpIdentityServerHttpApiModule),
//typeof(AbpIdentityServerEntityFrameworkCoreModule),
// OpenIddict扩展模块 自定义身份标识 // OpenIddict扩展模块 自定义身份标识
typeof(LINGYUN.Abp.OpenIddict.AspNetCore.AbpOpenIddictAspNetCoreModule), typeof(LINGYUN.Abp.OpenIddict.AspNetCore.AbpOpenIddictAspNetCoreModule),
// OpenIddict扩展模块 会话 // OpenIddict扩展模块 会话
@ -339,6 +327,8 @@ namespace LY.MicroService.Applications.Single;
typeof(AbpWeChatOfficialHandlersModule), typeof(AbpWeChatOfficialHandlersModule),
// 微信模块 企业微信 事件处理 // 微信模块 企业微信 事件处理
typeof(AbpWeChatWorkHandlersModule), typeof(AbpWeChatWorkHandlersModule),
// 认证模块 企业微信
typeof(AbpWeChatWorkAspNetCoreModule),
// 微信模块 设置管理 // 微信模块 设置管理
typeof(AbpWeChatSettingManagementModule), typeof(AbpWeChatSettingManagementModule),
@ -401,7 +391,6 @@ public partial class MicroServiceApplicationsSingleModule : AbpModule
ConfigureAuditing(); ConfigureAuditing();
ConfigureIdempotent(); ConfigureIdempotent();
ConfigureMvcUiTheme(); ConfigureMvcUiTheme();
ConfigureDataSeeder();
ConfigureLocalization(); ConfigureLocalization();
ConfigureKestrelServer(); ConfigureKestrelServer();
ConfigureBackgroundTasks(); ConfigureBackgroundTasks();

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

@ -51,22 +51,24 @@ var app = builder.Build();
await app.InitializeApplicationAsync(); await app.InitializeApplicationAsync();
app.UseMapRequestLocalization();
app.UseCookiePolicy();
app.UseForwardedHeaders(); app.UseForwardedHeaders();
app.UseAbpSecurityHeaders();
if (app.Environment.IsDevelopment()) if (app.Environment.IsDevelopment())
{ {
app.UseDeveloperExceptionPage(); app.UseDeveloperExceptionPage();
} }
// app.UseAbpExceptionHandling(); // app.UseAbpExceptionHandling();
app.UseCookiePolicy();
app.UseMapRequestLocalization();
app.UseCorrelationId(); app.UseCorrelationId();
app.MapAbpStaticAssets(); app.MapAbpStaticAssets();
app.UseRouting(); app.UseRouting();
app.UseCors(); app.UseCors();
app.UseAuthentication(); app.UseAuthentication();
app.UseAbpOpenIddictValidation();
app.UseMultiTenancy(); app.UseMultiTenancy();
app.UseUnitOfWork(); app.UseUnitOfWork();
app.UseAbpOpenIddictValidation();
app.UseAbpSession(); app.UseAbpSession();
app.UseDynamicClaims(); app.UseDynamicClaims();
app.UseAuthorization(); 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.Application;
using LINGYUN.Abp.Serilog.Enrichers.UniqueId; using LINGYUN.Abp.Serilog.Enrichers.UniqueId;
using LINGYUN.Abp.WeChat.Work; using LINGYUN.Abp.WeChat.Work;
using LY.MicroService.AuthServer.Authentication;
using Medallion.Threading; using Medallion.Threading;
using Medallion.Threading.Redis; using Medallion.Threading.Redis;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Caching.StackExchangeRedis; using Microsoft.Extensions.Caching.StackExchangeRedis;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.IdentityModel.Logging; using Microsoft.IdentityModel.Logging;
using OpenIddict.Validation.AspNetCore;
using OpenTelemetry.Metrics; using OpenTelemetry.Metrics;
using OpenTelemetry.Resources; using OpenTelemetry.Resources;
using OpenTelemetry.Trace; using OpenTelemetry.Trace;
@ -426,6 +428,8 @@ public partial class AuthServerModule
} }
private void ConfigureSecurity(IServiceCollection services, IConfiguration configuration, bool isDevelopment = false) private void ConfigureSecurity(IServiceCollection services, IConfiguration configuration, bool isDevelopment = false)
{ {
services.ForwardIdentityAuthenticationForBearer(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
services services
.AddAuthentication() .AddAuthentication()
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options => .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
@ -435,6 +439,10 @@ public partial class AuthServerModule
.AddJwtBearer(options => .AddJwtBearer(options =>
{ {
configuration.GetSection("AuthServer").Bind(options); configuration.GetSection("AuthServer").Bind(options);
})
.AddWeChatWork(options =>
{
options.SignInScheme = IdentityConstants.ExternalScheme;
}); });
if (!isDevelopment) if (!isDevelopment)
@ -446,8 +454,6 @@ public partial class AuthServerModule
.PersistKeysToStackExchangeRedis(redis, "LINGYUN.Abp.Application:DataProtection:Protection-Keys"); .PersistKeysToStackExchangeRedis(redis, "LINGYUN.Abp.Application:DataProtection:Protection-Keys");
} }
services.AddSameSiteCookiePolicy(); services.AddSameSiteCookiePolicy();
// 处理cookie中过时的ajax请求判断
services.Replace(ServiceDescriptor.Scoped<CookieAuthenticationHandler, AbpCookieAuthenticationHandler>());
} }
private void ConfigureMultiTenancy(IConfiguration configuration) 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.Application;
using LINGYUN.Abp.Serilog.Enrichers.UniqueId; using LINGYUN.Abp.Serilog.Enrichers.UniqueId;
using LINGYUN.Abp.Sms.Platform; using LINGYUN.Abp.Sms.Platform;
using LINGYUN.Abp.WeChat.Work.AspNetCore;
using LY.MicroService.AuthServer.EntityFrameworkCore; using LY.MicroService.AuthServer.EntityFrameworkCore;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
@ -60,6 +61,7 @@ namespace LY.MicroService.AuthServer;
typeof(AbpOpenIddictLinkUserModule), typeof(AbpOpenIddictLinkUserModule),
typeof(AbpOpenIddictPortalModule), typeof(AbpOpenIddictPortalModule),
typeof(AbpOpenIddictWeChatWorkModule), typeof(AbpOpenIddictWeChatWorkModule),
typeof(AbpWeChatWorkAspNetCoreModule), // 实现企业微信登录
typeof(AbpAuthenticationQQModule), typeof(AbpAuthenticationQQModule),
typeof(AbpAuthenticationWeChatModule), typeof(AbpAuthenticationWeChatModule),
typeof(AbpIdentityOrganizaztionUnitsModule), 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.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\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\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>
<ItemGroup> <ItemGroup>

Loading…
Cancel
Save