• owin 中间件 katana 如何解密cookie


    .NET MVC5 默认的用户登录组件是AspNet.Identity ,支持owin,并且微软自己实现的一套owin 中间件叫 katana

    补充一下 katana项目源码地址:https://katanaproject.codeplex.com/

    如何用owin做用户登录 授权等这里就不详细说了,大家可以自行搜索。

    登录就有用户状态,用户状态一般就是保存在cookie 里,cookie里肯定是保存的加密串了。

    那么这个katana是如何解密跟加密呢?

    翻了大半天源码,终于找到核心的2个类 CookieAuthenticationMiddleware,CookieAuthenticationHandler

    引用一下这篇比较全的文章,

    http://www.cnblogs.com/jesse2013/p/aspnet-identity-claims-based-authentication-and-owin.html?utm_source=tuicool&utm_medium=referral

    作者最后说下回分解CookieAuthenticationMiddleware这个东西,一直没等到下回,只能自己动手丰衣足食了。

    CookieAuthenticationHandler 里面有2个方法

    1,AuthenticateCoreAsync

    2,ApplyResponseGrantAsync

    前者是解密,后者是加密

    我们直接看解密的

      protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
            {
                AuthenticationTicket ticket = null;
                try
                {
                    string cookie = Options.CookieManager.GetRequestCookie(Context, Options.CookieName);
                    if (string.IsNullOrWhiteSpace(cookie))
                    {
                        return null;
                    }
    
                    ticket = Options.TicketDataFormat.Unprotect(cookie);
           //这里省略 …………

     Unprotect(cookie); 这个方法就是解密的核心方法了

    查找引用 找到SecureDataFormat<TData> 这个类

     public TData Unprotect(string protectedText)
            {
                try
                {
                    if (protectedText == null)
                    {
                        return default(TData);
                    }
    
                    byte[] protectedData = _encoder.Decode(protectedText);
                    if (protectedData == null)
                    {
                        return default(TData);
                    }
    
                    byte[] userData = _protector.Unprotect(protectedData);
                    if (userData == null)
                    {
                        return default(TData);
                    }
    
                    TData model = _serializer.Deserialize(userData);
                    return model;
                }
                catch
                {
                    // TODO trace exception, but do not leak other information
                    return default(TData);
                }
            }

    这里我们可以看到,解密步骤分成了三个步骤

    Decode(解码)
    Unprotect(解除保护)
    Deserialize(反序列化)

    分别查看源码后发现
    解码用的是 Base64 解保护用的windowsapi 里的CryptoAPI ,序列化用的是二进制序列化。

    到这里我就停了,需要的知识已经搞清楚了。

    那么我在项目里怎么解密呢?

    我没有直接用CookieAuthenticationMiddleware这个类,这个依赖较多,也可能是我没全看懂,反正没直接用。
    既然知道三个步骤是什么了,干脆我也是3个步骤了。
    Decode(解码)用Base64UrlTextEncoder
    Deserialize用TicketSerializer
    这两个类都是public 的,并且直接new 就能用的。katana源码里也是用这两个类。
    麻烦的地方在
    Unprotect

    需要在Startup里面通过IAppBuilder 来创建 IDataProtector
    不要问startup是什么,会用owin的都知道。
       public partial class Startup
        {
            public static IDataProtector dataProtector=null;
            private ILog loger = LogManager.GetLogger(typeof (Startup));
            public const string LoginCookieName = "xxx";
        
            public void ConfigureAuth(IAppBuilder app)
            {
               
                 var op = new CookieAuthenticationOptions
                {
                    AuthenticationType = LoginCookieName,
                    LoginPath = new PathString("/Login")
                        //,ExpireTimeSpan = TimeSpan.FromHours(1)
                    ,
                    ExpireTimeSpan = TimeSpan.FromDays(30)
                    ,
                    SlidingExpiration = true
    
                };         
                app.UseCookieAuthentication(op);
               app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
         
                  dataProtector = app.CreateDataProtector(
                          typeof(CookieAuthenticationMiddleware).FullName,
                          op.AuthenticationType, "v1");
    
            }
        }

    用了一个静态变量来装

    dataProtector 

    (直觉用静态变量不太好,有其他方案请不吝赐教啊)

    那么真正实现解密cookie的地方就在某个Controller里
       public async Task<ActionResult> DeCodeUser(string cookie)
            {
              
                byte[] protectedData = new Base64UrlTextEncoder().Decode(cookie);
    
                byte[] data = Startup.dataProtector.Unprotect(protectedData);
                var tick = new TicketSerializer().Deserialize(data);
                string userid = tick.Identity.GetUserId();
                AppUser currentUser = await UserManager.FindByIdAsync(userid);
                return Json(currentUser);
            }

    只要传入这个需要解密的cookie就能获取用户信息出来了。

    当然这个主要是用来探讨怎么解密 cookie而已,真正你要获取用户信息,直接调用 controller 里User就可以了。

    有人会担心,如果知道了解密方式,那岂不是只要cookie信息被截获 别人就能解出里面的信息?

    当然不行,我写的这个例子是因为加密 跟解密的方法都在同一台机器上运行,所以所创建的

    IDataProtector  其实包含着相同的密钥,相同的appname ,所以才能够加密解密对称。如果分开2台不同的机器就
    应该不行了
    (这个不太确定,如果知道了appname等信息不知道能不能模仿,但是还有winapi CryptoAPI 这个东西,底层实现我没细看,有心的读者可以去研究一下,记得把成果分享给我哟)。


     
  • 相关阅读:
    vue.js 第二课
    vue.js学习(第一课)
    2016-11-14看张大神的微博总结
    这几天的工作总结:
    调了一天的兼容总结下
    鸭式辩论
    prototype 原型
    前端ps常用的小技巧
    Android的开始之相对布局
    Android的开始之线性布局
  • 原文地址:https://www.cnblogs.com/7rhythm/p/4905134.html
Copyright © 2020-2023  润新知