我正在关注这篇文章以撤销用户访问:
http://bitoftech.net/2014/07/16/enable-oauth-refresh-tokens-angularjs-app-using-asp-net-web-api-2-owin/
现在考虑在验证用户后,我已经发布了一个生命周期为 30 分钟的访问 token ,如上面的文章所示,刷新 token 为 1 天,但是如果管理员在 10 分钟内删除该用户,还剩下 20 分钟,那么现在在这种情况下我需要撤销该用户的访问权限。
为了做到这一点,我需要从刷新 token 表中删除该用户条目以禁止进一步的访问 token 请求,但由于访问 token 过期时间仍有 20 分钟,因此用户将能够访问完全错误的 protected 资源。
所以我想实现缓存机制到在服务器上缓存访问 token 并保存在数据库中 .因此,当该用户被撤销时,我可以简单地从缓存和数据库中删除该用户条目,以阻止该用户访问 protected 资源。
但是下面的这 2 个答案是说这不是 oauth2 的设计方式:
Revoke access token of OAuthBearerAuthentication
OAuth2 - unnecessary complexity with refresh token
所以我的问题是:
1)为什么缓存访问 token 不被认为比刷新 token 机制更好,也是一种糟糕的方法?
我的第二个问题基于 给出的以下答案@Hans Z。他在其中说:
This necessarily would involve the Resource Server (RS) consulting the Authorization Server (AS) which is a huge overhead.
[Authorize] 的帮助下验证访问 token 属性最佳答案
这里似乎有两个不同的问题:关于访问 token 和关于角色的大列表。
访问 token
OAuth2 旨在能够处理高负载,这需要一些权衡。特别是这就是为什么 OAuth2 一方面明确分离“资源服务器”和“授权服务器”角色,另一方面明确分离“访问 token ”和“刷新 token ”的原因。如果对于每个请求都必须检查用户授权,则意味着您的授权服务器应该能够处理系统中的所有请求。对于高负载系统,这是不可行的。
OAuth2 允许您进行以下操作 权衡 性能和安全之间的关系:授权服务器生成一个访问 token ,资源服务器可以验证该访问 token ,而无需访问授权服务器(在授权服务器的生命周期内完全或至少不超过一次)。这是对授权信息的有效缓存。因此,通过这种方式,您可以大大减少授权服务器上的负载。缺点与缓存一样:授权信息可能会停滞。通过改变访问 token 的生命周期,您可以调整性能与安全平衡。
如果您采用微服务架构,其中每个服务都有自己的存储并且不访问彼此的存储,这种方法也可能会有所帮助。
尽管如此,如果您没有太多负载并且您只有一个资源服务器,而不是使用不同技术实现的大量不同服务,那么没有什么可以阻止您对每个请求进行全面验证。 IE。是的,您可以将访问 token 存储在数据库中,在每次访问资源服务器时对其进行验证,并在用户被删除时删除所有访问 token 等。但正如@Evk 所注意到的,如果这是您的情况 - OAuth2 对您来说是过头了.
大名单
AFAIU OAuth2 没有为用户角色提供明确的功能。有“范围”功能也可能用于角色,它的典型实现会为 250 个角色生成太长的字符串。 OAuth2 仍然没有明确指定访问 token 的任何特定格式,因此您可以创建一个自定义 token ,将角色信息作为位掩码保存。使用 base-64 编码,您可以将 6 个角色转换为单个字符 (64 = 2^6)。所以 250-300 个角色将是可管理的 40-50 个字符。
智威汤逊
由于无论如何您可能都需要一些自定义 token ,因此您可能对 JSON Web Tokens 感兴趣。又名 JWT。简而言之,JWT 允许您指定自定义附加负载(私有(private)声明)并将您的角色位掩码放在那里。
如果您真的不需要任何 OAuth2 高级功能(例如范围),您实际上可以单独使用 JWT 而没有完整的 OAuth2 内容。尽管 JWT token 应该仅通过这些内容进行验证,但您仍然可以将它们存储在本地数据库中,并对数据库进行额外的验证(就像您将使用访问刷新 token 一样)。
2017 年 12 月 1 日更新
如果您想使用 OWIN OAuth 基础架构,您可以通过 AccessTokenFormat 自定义 token 格式提供自定义格式化程序在 OAuthBearerAuthenticationOptions 和 OAuthAuthorizationServerOptions .您也可以覆盖 RefreshTokenFormat .
这是一个草图,显示了如何将角色声明“压缩”为单个位掩码:
CustomRoles列出您拥有的所有角色的枚举 [Flags]
public enum CustomRoles
{
Role1,
Role2,
Role3,
MaxRole // fake, for convenience
}
EncodeRoles和 DecodeRoles在 IEnumerable<string> 之间转换的方法基于 CustomRoles 的角色和 base64 编码位掩码的格式以上定义如: 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;
}
SecureDataFormat<AuthenticationTicket> 的自定义实现中使用这些方法.为了在这个草图中简单起见,我将大部分工作委托(delegate)给标准的 OWIN 组件,并只实现我的 CustomTicketSerializer这会创建另一个 AuthenticationTicket并使用标准 DataSerializers.Ticket .这显然不是最有效的方法,但它显示了您可以做什么: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);
}
}
Startup.cs配置 OWIN 以使用您的自定义格式,例如:var customTicketFormat = CustomTicketSerializer.CreateCustomTicketFormat(app);
OAuthBearerOptions.AccessTokenFormat = customTicketFormat;
OAuthServerOptions.AccessTokenFormat = customTicketFormat;
OAuthAuthorizationServerProvider添加 ClaimTypes.Role到 ClaimsIdentity对于分配给用户的每个角色。 AuthorizeAttribute如[Authorize(Roles = "Role1")]
[Route("")]
public IHttpActionResult Get()
AuthorizeAttribute 子类化接受类(class)CustomRoles枚举而不是字符串作为角色配置。
关于c# - 为什么在 oauth2 中缓存访问 token 被认为是不好的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47412971/
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返
我希望我的UserPrice模型的属性在它们为空或不验证数值时默认为0。这些属性是tax_rate、shipping_cost和price。classCreateUserPrices8,:scale=>2t.decimal:tax_rate,:precision=>8,:scale=>2t.decimal:shipping_cost,:precision=>8,:scale=>2endendend起初,我将所有3列的:default=>0放在表格中,但我不想要这样,因为它已经填充了字段,我想使用占位符。这是我的UserPrice模型:classUserPrice回答before_val
它不等于主线程的binding,这个toplevel作用域是什么?此作用域与主线程中的binding有何不同?>ruby-e'putsTOPLEVEL_BINDING===binding'false 最佳答案 事实是,TOPLEVEL_BINDING始终引用Binding的预定义全局实例,而Kernel#binding创建的新实例>Binding每次封装当前执行上下文。在顶层,它们都包含相同的绑定(bind),但它们不是同一个对象,您无法使用==或===测试它们的绑定(bind)相等性。putsTOPLEVEL_BINDINGput
我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串
如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象
关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion为什么SecureRandom.uuid创建一个唯一的字符串?SecureRandom.uuid#=>"35cb4e30-54e1-49f9-b5ce-4134799eb2c0"SecureRandom.uuid方法创建的字符串从不重复?