我已经创建了一个带有OAuth令牌身份验证的Web API应用程序。当令牌服务器与服务在同一个应用程序上运行时,这没有问题。但是,我想将授权服务移动到它自己的应用程序(VS项目)中,并在我正在处理的几个Web API项目中使用它。然而,当我将授权逻辑隔离到它自己的项目中时,原始服务不再将生成的令牌视为有效的。我的问题是,一个Web API项目是否可以生成令牌供另一个项目验证?这是我的身份验证服务和原始服务的OWIN启动代码
认证服务:
public void Configuration(IAppBuilder app)
{
// For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=316888
HttpConfiguration config = new HttpConfiguration();
ConfigureOAuth(app);
WebApiConfig.Register(config);
app.UseWebApi(config);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
}
private void ConfigureOAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new SimpleAuthorizationServerProvider()
};
// Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
原始服务:
public void Configuration(IAppBuilder app)
{
ConfigureOAuth(app);
// For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=316888
HttpConfiguration config = new HttpConfiguration();
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
WebApiConfig.Register(config);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
}
public void ConfigureOAuth(IAppBuilder app)
{
var oauthBearerOptions = new OAuthBearerAuthenticationOptions();
app.UseOAuthBearerAuthentication(oauthBearerOptions);
}
如果您不想使用MahineKey
,这就有点棘手了,我希望它可以跨不同的服务器和用户使用每个服务器唯一的MahineKey
。
跨Asp.NET核心和框架的数据保护提供商(生成密码重置链接)
我开始在DataProtectionTokenProvider的帮助下实现
我自己的ValidateAsync
.cs用于 ASP.NET 核心身份。这门课真的帮助我找到了解决方案。
https://github . com/aspnet/Identity/blob/master/src/Identity/dataprotectiontokenprovider . cs
使用< code > data protectortokenprovider时,令牌从< code>SecurityStamp生成
System.Security.Cryptography.ProtectedData.Protect(userData, entropy, DataProtectionScope.CurrentUser);
假设如果所有站点都使用相同的应用程序池标识
,则它也可以正常工作。它也可以是带有保护描述符
“LOCAL=user”
的数据保护提供程序
。
new DataProtectionProvider("LOCAL=user")
https://learn.microsoft.com/en-us/previous-versions/aspnet/dn613280(v=vs.108)
https://learn . Microsoft . com/en-us/dot net/API/system . security . cryptography . data protector?view=netframework-4.7.2
https://learn.microsoft.com/en-us/uwp/api/windows.security.cryptography.dataprotection.dataprotectionprovider
当阅读<code>DpapiDataProtectionProvider</code>(DPAPI代表数据保护应用程序编程接口)时,描述如下:
用于提供从data protection API派生的数据保护服务。当应用程序不由ASP托管时,它是数据保护的最佳选择。NET和所有进程都以相同的域标识运行。
创建方法用途描述为:
用于确保受保护数据的额外熵只能出于正确的目的不受保护。
https://learn . Microsoft . com/en-us/previous-versions/aspnet/dn 253784(v = vs . 113)
考虑到这些信息,我认为在尝试使用Microsoft
提供的普通类时没有任何进展。
我最终实现了自己的<code>IUserTokenProvider
我选择用证书实现< code>IDataProtector,因为我可以相对容易地在服务器之间传输这些证书。我也可以使用运行网站的< code >应用程序池标识从< code>X509Store中获取它,这样应用程序本身就不会存储任何密钥。
public class CertificateProtectorTokenProvider<TUser, TKey> : IUserTokenProvider<TUser, TKey>
where TUser : class, IUser<TKey>
where TKey : IEquatable<TKey>
{
private IDataProtector protector;
public CertificateProtectorTokenProvider(IDataProtector protector)
{
this.protector = protector;
}
public virtual async Task<string> GenerateAsync(string purpose, UserManager<TUser, TKey> manager, TUser user)
{
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
var ms = new MemoryStream();
using (var writer = new BinaryWriter(ms, new UTF8Encoding(false, true), true))
{
writer.Write(DateTimeOffset.UtcNow.UtcTicks);
writer.Write(Convert.ToInt32(user.Id));
writer.Write(purpose ?? "");
string stamp = null;
if (manager.SupportsUserSecurityStamp)
{
stamp = await manager.GetSecurityStampAsync(user.Id);
}
writer.Write(stamp ?? "");
}
var protectedBytes = protector.Protect(ms.ToArray());
return Convert.ToBase64String(protectedBytes);
}
public virtual async Task<bool> ValidateAsync(string purpose, string token, UserManager<TUser, TKey> manager, TUser user)
{
try
{
var unprotectedData = protector.Unprotect(Convert.FromBase64String(token));
var ms = new MemoryStream(unprotectedData);
using (var reader = new BinaryReader(ms, new UTF8Encoding(false, true), true))
{
var creationTime = new DateTimeOffset(reader.ReadInt64(), TimeSpan.Zero);
var expirationTime = creationTime + TimeSpan.FromDays(1);
if (expirationTime < DateTimeOffset.UtcNow)
{
return false;
}
var userId = reader.ReadInt32();
var actualUser = await manager.FindByIdAsync(user.Id);
var actualUserId = Convert.ToInt32(actualUser.Id);
if (userId != actualUserId)
{
return false;
}
var purp = reader.ReadString();
if (!string.Equals(purp, purpose))
{
return false;
}
var stamp = reader.ReadString();
if (reader.PeekChar() != -1)
{
return false;
}
if (manager.SupportsUserSecurityStamp)
{
return stamp == await manager.GetSecurityStampAsync(user.Id);
}
return stamp == "";
}
}
catch (Exception e)
{
// Do not leak exception
}
return false;
}
public Task NotifyAsync(string token, UserManager<TUser, TKey> manager, TUser user)
{
throw new NotImplementedException();
}
public Task<bool> IsValidProviderForUserAsync(UserManager<TUser, TKey> manager, TUser user)
{
throw new NotImplementedException();
}
}
public class CertificateProtectionProvider : IDataProtectionProvider
{
public IDataProtector Create(params string[] purposes)
{
return new CertificateDataProtector(purposes);
}
}
public class CertificateDataProtector : IDataProtector
{
private readonly string[] _purposes;
private X509Certificate2 cert;
public CertificateDataProtector(string[] purposes)
{
_purposes = purposes;
X509Store store = null;
store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
var certificateThumbprint = ConfigurationManager.AppSettings["CertificateThumbprint"].ToUpper();
cert = store.Certificates.Cast<X509Certificate2>()
.FirstOrDefault(x => x.GetCertHashString()
.Equals(certificateThumbprint, StringComparison.InvariantCultureIgnoreCase));
}
public byte[] Protect(byte[] userData)
{
using (RSA rsa = cert.GetRSAPrivateKey())
{
// OAEP allows for multiple hashing algorithms, what was formermly just "OAEP" is
// now OAEP-SHA1.
return rsa.Encrypt(userData, RSAEncryptionPadding.OaepSHA1);
}
}
public byte[] Unprotect(byte[] protectedData)
{
// GetRSAPrivateKey returns an object with an independent lifetime, so it should be
// handled via a using statement.
using (RSA rsa = cert.GetRSAPrivateKey())
{
return rsa.Decrypt(protectedData, RSAEncryptionPadding.OaepSHA1);
}
}
}
客户网站重置:
var provider = new CertificateProtectionProvider();
var protector = provider.Create("ResetPassword");
userManager.UserTokenProvider = new CertificateProtectorTokenProvider<ApplicationUser, int>(protector);
if (!await userManager.UserTokenProvider.ValidateAsync("ResetPassword", model.Token, UserManager, user))
{
return GetErrorResult(IdentityResult.Failed());
}
var result = await userManager.ResetPasswordAsync(user.Id, model.Token, model.NewPassword);
后台办公室:
var createdUser = userManager.FindByEmail(newUser.Email);
var provider = new CertificateProtectionProvider();
var protector = provider.Create("ResetPassword");
userManager.UserTokenProvider = new CertificateProtectorTokenProvider<ApplicationUser, int>(protector);
var token = userManager.GeneratePasswordResetToken(createdUser.Id);
关于普通Data保护令牌提供者如何使用的更多信息
https://stackoverflow.com/a/53390287/3850405
只是在自己研究这个时偶然发现了这个 Q。TL;DR 的答案是令牌是使用 machine.config 文件中的 machineKey 属性生成的:如果要在多个服务器上托管,则需要覆盖它。
MachineKey可以在web.config中重写:
<system.web>
<machineKey validationKey="VALUE GOES HERE"
decryptionKey="VALUE GOES HERE"
validation="SHA1"
decryption="AES"/>
</system.web>
计算机密钥应在本地生成 - 使用在线服务不安全。有关生成密钥的知识库文章
这一切的原始裁判http://bitoftech.net/2014/09/24/decouple-owin-authorization-server-resource-server-oauth-2-0-web-api
问题内容: 我们正在考虑将ci从jenkins迁移到gitlab。我们有几个项目具有相同的构建工作流程。现在,我们使用一个定义了管道的共享库,而项目内部的jenkinsfile仅调用在共享库中定义的定义实际管道的方法。因此,仅需在单个点上进行更改即可影响多个项目。 我想知道gitlab ci是否也可以做到?据我发现,无法在存储库外部定义gitlab- ci.yml。还有另一种定义管道并与几个项目共
问题内容: 有没有办法在Netbeans和Eclipse之间共享相同的* .java文件? 问题答案: 您可以将eclipse项目导入netbeans, 或者,您可以从现有资源创建Eclipse项目。
在Java中,对于两个JVM(运行在同一台物理机器上),是否有办法使用/共享相同的内存地址空间?假设JVM-1中的生产者将消息放在特定的预定义内存位置,如果JVM-2上的消费者知道要查看哪个内存位置,那么它是否可以检索消息?
问题内容: 我有一些称为的数据,该数据位于三个孩子的父对象的范围内: 在这三个指令之间共享的最佳方法是什么?选项包括: 使用隔离的范围传递三遍,从而跨四个范围复制它 让子指示继承父范围,并找到,或在 把上并注入到这一点的子指示 还是有另一种更好的方法? 问题答案: 您可以创建一个工厂,该工厂可以传递给每个指令或控制器。这样可以确保在任何给定时间只有一个数组实例。编辑:这里唯一的陷阱是确保您在指令作
问题内容: 我想在以下两个指令之间共享: 在HTML中,我有: 我创建了具有隔离范围的名为“ directive1”的指令,并将名称“ table”分配给该属性。我无法在其他指令中访问此作用域属性。 那么,如何访问另一个指令的作用域呢? 问题答案: 您可以对需要跨指令同步的项目执行操作。 或者,您可以将对象传递给指令1隔离范围,该范围将充当通信机制。在此对象上,如果更改子属性(如),则会影响父范围
问题内容: 我有两个线程。可以调用修改变量的类的update方法。另一个调用读取该变量的类的update方法。只有一个线程写入,一个(或多个)线程读取该变量。由于我是多线程技术的新手,我需要在并发方面做什么? 谢谢, 问题答案: 如果有且仅有一个写线程,你可以逃脱使得它。否则,请查看答案。 仅在只有一个写线程的情况下才起作用,因为只有一个写线程,因此它始终具有的正确值。