我正在按照这篇文章撤销用户访问权限:
http://bitoftech . net/2014/07/16/enable-oauth-refresh-tokens-angular js-app-using-ASP-net-we b-API-2-owin/
现在考虑一下,在验证用户之后,我已经发布了一个访问令牌,使用期限为30分钟,如上面的文章所示,刷新令牌为1天,但是如果管理员在10分钟内删除该用户,还有20分钟,那该怎么办,所以在这种情况下,我需要撤销对该用户的访问。
为此,我需要从刷新令牌表中删除该用户条目,以禁止进一步的访问令牌请求,但由于访问令牌到期时间仍有20分钟,因此用户将能够访问受保护的资源,这是完全错误的。
所以我正在考虑实现缓存机制以在服务器上缓存访问令牌并保存在数据库中。因此,当该用户被撤销时,我可以简单地从缓存和数据库中删除该用户条目,以阻止该用户访问受保护的资源。
但下面的2个答案表明,oauth2不是这样设计的:
撤销 OAuthBearer身份验证的访问令牌
OAuth2-刷新令牌的不必要复杂性
所以我的问题是:
1) 为什么缓存访问令牌不被认为比刷新令牌机制更好,也是一种糟糕的方法?
我的第二个问题是基于@Hans Z的以下回答,他在回答中说:
这必然涉及资源服务器(RS)咨询授权服务器(AS ),这是一个巨大的开销。
2) 如果撤销用户的访问权限,为什么 RS 会咨询 AS,因为 AS 仅用于根据本文对用户进行身份验证和生成访问令牌?
3)文章中只有2个项目:
> < li>Authentication.api -验证用户并生成访问令牌 < li>
资源服务器-借助< code>[Authorize]属性验证accesstoken
在上述情况下,哪个是授权服务器?
更新:我已经决定使用刷新令牌来撤销用户访问权限,以防用户被删除,并且当用户注销时,我将从刷新令牌表中刷新令牌,因为您要求我们在用户单击注销后立即注销用户。
但这里的问题是我有 250 个与用户关联的角色,所以如果我将角色放在 accesstoken 中,那么访问令牌的大小将非常大,我们无法从标头传递如此巨大的访问令牌,但我无法查询角色以验证用户对endpoint的访问每次调用该endpoint时。
所以这是我面临的另一个问题。
刷新令牌方法的主要优点是减少数据库查询的数量,访问令牌具有声明和签名,因此无需查询数据库即可信任令牌。
缓存访问令牌将起作用,但随后您必须查询每个请求的缓存。
您必须在访问权限更改的n分钟延迟和检查访问令牌有效性的查询数量之间进行权衡
随着复杂性的增加,您几乎可以实现这两个目标。在这种情况下,您必须将缓存存储在服务器RAM中,并且只存储已撤销的令牌以保持列表较小。当您有多个服务器实例时,您将不得不在您的RS和AS之间保持已撤销令牌的缓存同步,这会带来复杂性。
基本上,当访问令牌被撤销时,AS必须通知所有RS将该访问令牌添加到撤销的令牌缓存中。
每当有资源请求时,RS会检查令牌是否被撤销,如果没有撤销,RS会服务器资源。这样,每个请求都有开销,但由于缓存在内存中,因此开销大大减少,撤销令牌的数量将比有效令牌的数量少得多。
我希望我回答对了你的问题,并能提供一些答案:
1) 如果您开发的AS要求在每次用户登录时进行验证,则可以将其兑现。
2)我认为@Hans Z.的意思是由AS撤销用户。当RS撤销用户时,它不会改变他们仍然是AS识别的用户的事实。但是当AS撤销用户时,它会阻止他们使用他们的身份。
3) 本文可能假设授权是由 RS 完成的,AS 只负责告诉您谁是用户,RS 将据此决定授权。
这里似乎有两个不同的问题:关于访问令牌和关于大角色列表。
访问令牌
OAuth2被设计成能够处理高负载,这需要一些权衡。特别是这就是为什么OAuth2一方面明确区分“资源服务器”和“授权服务器”角色,另一方面明确区分“访问令牌”和“刷新令牌”的原因。如果对于每个请求,您都必须检查用户授权,这意味着您的授权服务器应该能够处理系统中的所有请求。对于高负载系统,这是不可行的。
OAuth2允许您在性能和安全性之间进行以下权衡:授权服务器生成一个访问令牌,资源服务器可以在不访问授权服务器的情况下对其进行验证(在授权服务器的生命周期内完全或至少不超过一次)。这实际上是授权信息的缓存。因此,通过这种方式,您可以大大减少授权服务器的负载。与缓存一样,缺点也是一样的:授权信息可能会停止。通过改变访问令牌的生命周期,您可以调整性能与安全平衡。
如果您使用微服务架构,其中每个服务都有自己的存储并且不访问彼此的存储,这种方法也可能会有所帮助。
但是,如果您没有太多负载并且只有单个资源服务器,而不是使用不同技术实现的大量不同服务,则没有什么可以阻止您对每个请求进行实际的全面验证。即是的,您可以将访问令牌存储在数据库中,在每次访问资源服务器时对其进行验证,并在删除用户时删除所有访问令牌等。但正如@Evk注意到的那样,如果这是您的场景 - OAuth2 对您来说是一个超调。
角色大列表
AFAIU OAuth2没有为用户角色提供明确的特性。有一个“作用域”特性也可以用于角色,它的典型实现会产生250个角色的太长的字符串。OAuth2仍然没有为访问令牌明确指定任何特定的格式,所以您可以创建一个自定义令牌,将角色信息作为位掩码保存。使用base-64编码,您可以在一个字符中包含6个角色(64 = 2^6).所以250-300个角色可以用40-50个字符来管理。
JWT公司
由于您可能无论如何都需要一些自定义令牌,因此您可能会对JSON Web令牌(又名JWT)感兴趣。简而言之,JWT 允许您指定自定义附加有效负载(专用声明)并将您的角色位掩码放在那里。
如果您真的不需要任何 OAuth2 高级功能(例如作用域),您实际上可以单独使用 JWT,而无需完整的 OAuth2 内容。尽管 JWT 令牌应该仅通过内容进行验证,但您仍然可以将它们存储在本地数据库中,并对数据库进行其他验证(就像您将对访问刷新令牌所做的那样)。
更新2017年12月1日
如果您要使用OWIN OAuth基础结构,您可以通过< code > oauthbeareratuthenticationoptions 和< code > OAuthAuthorizationServerOptions 中的< code>AccessTokenFormat自定义令牌格式。您也可以覆盖< code>RefreshTokenFormat。
下面是一个草图,展示了如何将角色声明“压缩”为单个位掩码:
自定义角色
枚举[Flags]
public enum CustomRoles
{
Role1,
Role2,
Role3,
MaxRole // fake, for convenience
}
public static string EncodeRoles(IEnumerable<string> roles)
{
byte[] bitMask = new byte[(int)CustomRoles.MaxRole];
foreach (var role in roles)
{
CustomRoles roleIndex = (CustomRoles)Enum.Parse(typeof(CustomRoles), role);
var byteIndex = ((int)roleIndex) / 8;
var bitIndex = ((int)roleIndex) % 8;
bitMask[byteIndex] |= (byte)(1 << bitIndex);
}
return Convert.ToBase64String(bitMask);
}
public static IEnumerable<string> DecodeRoles(string encoded)
{
byte[] bitMask = Convert.FromBase64String(encoded);
var values = Enum.GetValues(typeof(CustomRoles)).Cast<CustomRoles>().Where(r => r != CustomRoles.MaxRole);
var roles = new List<string>();
foreach (var roleIndex in values)
{
var byteIndex = ((int)roleIndex) / 8;
var bitIndex = ((int)roleIndex) % 8;
if ((byteIndex < bitMask.Length) && (0 != (bitMask[byteIndex] & (1 << bitIndex))))
{
roles.Add(Enum.GetName(typeof(CustomRoles), roleIndex));
}
}
return roles;
}
public class CustomTicketSerializer : IDataSerializer<AuthenticationTicket>
{
public const string RoleBitMaskType = "RoleBitMask";
private readonly IDataSerializer<AuthenticationTicket> _standardSerializers = DataSerializers.Ticket;
public static SecureDataFormat<AuthenticationTicket> CreateCustomTicketFormat(IAppBuilder app)
{
var tokenProtector = app.CreateDataProtector(typeof(OAuthAuthorizationServerMiddleware).Namespace, "Access_Token", "v1");
var customTokenFormat = new SecureDataFormat<AuthenticationTicket>(new CustomTicketSerializer(), tokenProtector, TextEncodings.Base64Url);
return customTokenFormat;
}
public byte[] Serialize(AuthenticationTicket ticket)
{
var identity = ticket.Identity;
var otherClaims = identity.Claims.Where(c => c.Type != identity.RoleClaimType);
var roleClaims = identity.Claims.Where(c => c.Type == identity.RoleClaimType);
var encodedRoleClaim = new Claim(RoleBitMaskType, EncodeRoles(roleClaims.Select(rc => rc.Value)));
var modifiedClaims = otherClaims.Concat(new Claim[] { encodedRoleClaim });
ClaimsIdentity modifiedIdentity = new ClaimsIdentity(modifiedClaims, identity.AuthenticationType, identity.NameClaimType, identity.RoleClaimType);
var modifiedTicket = new AuthenticationTicket(modifiedIdentity, ticket.Properties);
return _standardSerializers.Serialize(modifiedTicket);
}
public AuthenticationTicket Deserialize(byte[] data)
{
var ticket = _standardSerializers.Deserialize(data);
var identity = ticket.Identity;
var otherClaims = identity.Claims.Where(c => c.Type != RoleBitMaskType);
var encodedRoleClaim = identity.Claims.SingleOrDefault(c => c.Type == RoleBitMaskType);
if (encodedRoleClaim == null)
return ticket;
var roleClaims = DecodeRoles(encodedRoleClaim.Value).Select(r => new Claim(identity.RoleClaimType, r));
var modifiedClaims = otherClaims.Concat(roleClaims);
var modifiedIdentity = new ClaimsIdentity(modifiedClaims, identity.AuthenticationType, identity.NameClaimType, identity.RoleClaimType);
return new AuthenticationTicket(modifiedIdentity, ticket.Properties);
}
}
var customTicketFormat = CustomTicketSerializer.CreateCustomTicketFormat(app);
OAuthBearerOptions.AccessTokenFormat = customTicketFormat;
OAuthServerOptions.AccessTokenFormat = customTicketFormat;
在<code>OAuthAuthorizationServerProvider</code>中添加<code>ClaimTypes。分配给用户的每个角色的角色到ClaimsIdentity
在控制器中使用标准的< code>AuthorizeAttribute
,例如
[Authorize(Roles = "Role1")]
[Route("")]
public IHttpActionResult Get()
为了方便和一些安全,您可以子类 AuthorizeAttribute
类以接受 CustomRoles
枚举而不是字符串作为角色配置。
我有一个问题,关于什么是正确的做法,使用SwingU实用程序的调用稍后方法。 所以首先,我想确认我理解正确。 据我所知,对GUI的更改必须在EDT上完成,因为Swing组件不是线程安全的。invokeLater方法将Runnable作为参数,该Runnable中包含的任何内容都将在EDT上运行。因此,对Swing组件的任何调用都被放入一种队列中,在EDT上一次执行一个。 有了这些,我的问题是:使用
问题内容: 我问了一个一般性的Spring问题:自动播发Spring Bean,并让多个人回答说应尽可能避免调用Spring 。这是为什么? 我还应该如何访问配置了Spring创建的Bean? 我在非Web应用程序中使用Spring,并计划按照LiorH的描述访问共享对象。 修正案 我接受下面的答案,但这是Martin Fowler的另一种选择,他讨论了依赖注入与使用(本质上与调用相同)的优点。
在处理基于浏览器的应用程序时,关于安全存储JWT令牌的主题已经提出了很多问题。大家一致认为,应该使用仅限http的安全cookie。然而,当涉及短期访问令牌和长期刷新令牌时,存储JWT令牌似乎存在许多变化。 我发现了以下变化: 1.仅将JWT访问令牌和刷新令牌存储在http安全cookie中 赞成的意见: 无法从Javascript访问访问令牌和刷新令牌 欺骗: 引入CSRF漏洞,因此也必须添加C
我正在阅读 Auth0 站点上有关刷新令牌和 SPA 的文档,他们指出 SPA 不应使用刷新令牌,因为它们无法安全地存储在浏览器中,而是使用静默身份验证来检索新的访问令牌。 单页应用程序(通常实施 我很困惑。据我了解,检索新访问令牌的唯一方法是向身份验证服务器提交新请求,以及某种形式的 Auth0 会话 cookie,以对登录的用户进行身份验证。收到会话 cookie 后,Auth0 服务器将能够
我听到一些人说,即使在使用获得种子之后,使用也是很糟糕的。为什么会这样?我想知道事情是怎么发生的...抱歉,我又问了一个问题..但是,有什么办法可以替代这一点呢?
问题内容: 我遇到一种情况,我想自己创建一个访问令牌(因此不能通过通常的过程)。我想出了这样的东西: 唯一的问题是我不确定如何创建OAuth2Authentication(在我的代码中带有xxx的部分)。我有用户和客户信息,我知道我想授予该令牌的单位。 问题答案: 在这里,根据使用的流程,您的用例可能会略有不同。这适用于密码授予流程。有一些自定义类,如令牌存储,令牌增强器等。但这实际上只是为满足我