使用Owin身份验证进行服务器端声明缓存

32

我有一个应用程序,以前使用了FormsAuthentication,一段时间以前我将其切换到使用WindowsIdentityFramework中的IdentityModel,以便我可以从基于声明的身份验证中受益,但它使用和实现都相当丑陋。所以现在我正在研究OwinAuthentication

我正在研究OwinAuthenticationAsp.Net Identity框架。但是目前Asp.Net Identity框架的唯一实现使用EntityModel,而我正在使用nHibernate。因此,现在我希望尝试绕过Asp.Net Identity,直接使用Owin Authentication。最后我通过参考《如何忽略Identity Framework的魔法,并只使用OWIN auth中间件获取我需要的声明?》中的建议成功实现了登录,但是现在保存声明的cookie变得非常大。当我使用IdentityModel时,我能够使用服务器端缓存机制在服务器上缓存声明,并且cookie只保留了缓存信息的简单令牌。在OwinAuthentication中是否有类似的功能,还是我需要自己实现?

我预计会有以下情况之一...

  1. cookie保持为3KB,哦,它有点大。
  2. 启用类似于IdentityModelOwin中的SessionCaching功能,这是我不知道的。
  3. 编写自己的实现来缓存导致cookie膨胀的信息,并查看是否可以在应用程序启动时将其连接到配置Owin
  4. 我的方法都错了,可能还有其他处理方式,或者我在使用Owin的过程中出现了误用。


public class OwinConfiguration
{
    public void Configuration(IAppBuilder app)
    {
        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = "Application",
            AuthenticationMode = AuthenticationMode.Active,
            CookieHttpOnly = true,
            CookieName = "Application",
            ExpireTimeSpan = TimeSpan.FromMinutes(30),
            LoginPath = "/Login",
            LogoutPath = "/Logout",
            ReturnUrlParameter="ReturnUrl",
            SlidingExpiration = true,
            Provider = new CookieAuthenticationProvider()
            {
                OnValidateIdentity = async context =>
                {
                    //handle custom caching here??
                }
            }
            //CookieName = CookieAuthenticationDefaults.CookiePrefix + ExternalAuthentication.ExternalCookieName,
            //ExpireTimeSpan = TimeSpan.FromMinutes(5),
        });
    }
}

更新 我使用Hongye提供的信息成功实现了期望的效果,并得出以下逻辑…

Provider = new CookieAuthenticationProvider()
{
    OnValidateIdentity = async context =>
    {
        var userId = context.Identity.GetUserId(); //Just a simple extension method to get the ID using identity.FindFirst(x => x.Type == ClaimTypes.NameIdentifier) and account for possible NULLs
        if (userId == null) return;
        var cacheKey = "MyApplication_Claim_Roles_" + userId.ToString();
        var cachedClaims = System.Web.HttpContext.Current.Cache[cacheKey] as IEnumerable<Claim>;
        if (cachedClaims == null)
        {
            var securityService = DependencyResolver.Current.GetService<ISecurityService>(); //My own service to get the user's roles from the database
            cachedClaims = securityService.GetRoles(context.Identity.Name).Select(role => new Claim(ClaimTypes.Role, role.RoleName));
            System.Web.HttpContext.Current.Cache[cacheKey] = cachedClaims;
        }
        context.Identity.AddClaims(cachedClaims);
    }
}

为什么不使用自定义的ASP.NET Identity实现?NuGet上已经有了一些实现。 - Caleb Kiage
当我处理这个问题的时候,当时没有任何相关的内容,你指的是哪些? - Nick Albrecht
Nhibernate.AspNet.Identity 和 AspNet.Identity.NHibernate(我使用 SharpArchitecture 和 FluentNHibernate 创建了它。然而,这是一个预发布版本)。 - Caleb Kiage
2
CookieAuthenticationOptions对象上有一个名为“SessionStore”的字段,它被描述为“一个可选的容器,用于跨请求存储身份。使用时,仅向客户端发送会话标识符。这可以用于缓解非常大的身份可能带来的问题。” 这似乎是您尝试做的事情。不幸的是,我找不到任何关于如何实际创建这些SessionStores的参考。 - ThisGuy
“Claim” 是指 System.Security.Claims.Claim 吗?获取用户 ID 扩展的代码是什么? - Kiquenet
3个回答

15

OWIN cookie authentication middleware目前尚不支持类似会话缓存的功能。 #2不是一个选项。

#3是正确的方法。就像Prabu建议的那样,您应该在代码中执行以下操作:

OnResponseSignIn:

  • 使用唯一键(GUID)将context.Identity保存到缓存中
  • 创建一个嵌入了唯一键的新ClaimsIdentity
  • 用新的身份替换context.Identity

OnValidateIdentity:

  • 从context.Identity获取唯一键声明
  • 使用唯一键获取缓存的身份
  • 使用缓存的身份调用context.ReplaceIdentity

我本来想建议您压缩cookie,但我发现OWIN已经在其TicketSerializer中完成了这个功能。对于您来说不是一个选项。


导致我的 cookie 大小膨胀的声明是在整个网站中用于权限的角色。我是否应该缓存并替换整个身份,还是可以保持身份不变,只需在 OnValidateIdentity 任务中缓存并添加缺少的声明 ClaimTypes.Role - Nick Albrecht
当然可以。您绝对可以自定义代码以满足您的应用程序要求。我发布的是从服务器缓存引用cookie的通用方法。 - Hongye Sun
请提供“创建内嵌唯一密钥的新ClaimsIdentity”的完整源代码示例和“用新身份替换context.Identity”的代码? - Kiquenet

8
Provider = new CookieAuthenticationProvider()
{
    OnResponseSignIn = async context =>
    {
        // This is the last chance before the ClaimsIdentity get serialized into a cookie. 
        // You can modify the ClaimsIdentity here and create the mapping here. 
        // This event is invoked one time on sign in. 
    }, 
    OnValidateIdentity = async context => 
    {
        // This method gets invoked for every request after the cookie is converted 
        // into a ClaimsIdentity. Here you can look up your claims from the mapping table. 
    }
}

1
我已经知道了那一部分的代码,复制粘贴它并不能回答我的问题。 - Nick Albrecht
1
请查看我上面展示的OnResponseSignIn事件和其中的评论。像我之前提到的那样,OnValidateIdentity会为每个请求调用。基本上有两个关键点 - OnResponseSignIn创建映射,OnValidateIdentity查找声明。 - Praburaj

1
你可以实现IAuthenticationSessionStore接口将cookie存储到数据库中。
以下是在redis中存储cookie的示例。
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
SessionStore = new RedisSessionStore(new TicketDataFormat(dataProtector)),
LoginPath = new PathString("/Auth/LogOn"),
LogoutPath = new PathString("/Auth/LogOut"),

});

检查完整的示例在这里

如果认证 cookie 将保存在数据库中,那么客户端会存储什么? - Monojit Sarkar
仅在客户端存储会话标识符。 - Alex Nguyen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接