• C#分布式登录——jwt


    一、传统的session登录

    在服务器存储一份用户登录的信息,这份登录信息会在响应时传递给浏览器,告诉其保存为cookie,以便下次请求时发送给我们的应用,这样我们的应用就能识别请求来自哪个用户了,这就是传统的基于session认证。

    在asp.net core中可以简单实现:

     1   // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
     2         public void Configure(IApplicationBuilder app, IHostingEnvironment env)
     3         {
     4             if (env.IsDevelopment())
     5             {
     6                 app.UseDeveloperExceptionPage();
     7             }
     8             else
     9             {
    10                 app.UseExceptionHandler("/Home/Error");
    11                 app.UseHsts();
    12             }
    13  
    14             app.UseHttpsRedirection();
    15             //使用静态文件
    16             app.UseStaticFiles();
    17             //Cookie策略
    18             //app.UseCookiePolicy();
    19             //Session
    20             app.UseSession();
    21  
    22             app.UseMvc(routes =>
    23             {
    24                 routes.MapRoute(
    25                     name: "default",
    26                    // template: "{controller=Home}/{action=Index}/{id?}");
    27                    //template: "{controller=Home}/{action=Server}/{id?}");
    28                    template: "{controller=Login}/{action=SignIn}/{id?}");
    29             });
    30         }
     1         public IActionResult SignIn(UserModel userModel)
     2         {
     3             if (ModelState.IsValid)
     4             {
     5                 //检查用户信息
     6                 if (userModel.Username.Equals("yswenli") && userModel.Password.Equals("yswenli"))
     7                 {
     8                     //记录Session
     9                     HttpContext.Session.Set("User", ByteConvertHelper.Object2Bytes(userModel));
    10                     //跳转到系统首页
    11                     return RedirectToAction("Server", "Home");
    12                 }
    13                 ViewBag.ErrorInfo = "用户名或密码错误";
    14                 return View(userModel);
    15             }
    16             ViewBag.ErrorInfo = ModelState.Values.First().Errors[0].ErrorMessage;
    17             return View(userModel);

    但是这种基于session的认证使应用本身很难得到扩展,随着不同客户端用户的增加,独立的服务器已无法承载更多的用户,而这时候基于session认证应用的问题就会暴露出来。

    传统session的主要问题如下:

    1.服务器压力: 每个用户经过我们的应用认证之后,我们的应用都要在服务端做一次记录,以方便用户下次请求的鉴别,通常而言session都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大。

    2.扩展性: 用户认证之后,服务端做认证记录,如果认证的记录被保存在内存中的话,这意味着用户下次请求还必须要请求在这台服务器上,这样才能拿到授权的资源,这样在分布式的应用上,相应的限制了负载均衡器的能力。这也意味着限制了应用的扩展能力。

    3.CSRF: 因为是基于cookie来进行用户识别的, cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击。

    二、基于token的鉴权机制

    基于token的鉴权机制是无状态的,它不需要在服务端去保留用户的认证信息或者会话信息,而是基于token去运算而实现鉴权。这就意味着基于token认证机制的应用不需要去考虑用户在哪一台服务器登录了,这就为服务实现大规模分布式提供了基础。

    上图是一种用token登录的实现方式,类似的还有很多,虽然实现了分布式的登录处理,但是由于不同的系统之间的不同实现,导致开发量剧增。

    三、Json web token

    这里推荐使用JWT——Json web token(官网链接)。一个典型的JWT看起来如下图:

    jwt为一个字符串,字符之间通过"."分隔符分为三个子串。注意JWT对象为一个长字串,各字串之间也没有换行符,此处为了演示需要,特意分行并用不同颜色表示了。每一个子串表示了一个功能块,总共有以下三个部分:JWT头、有效载荷和签名,将它们写成一行如下:

     1 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InRlc3QiLCJpYXQiOjE1OTM5NTU5NDMsInVpZCI6MTAsImV4cCI6MTU5Mzk1NTk3Mywic2NvcGVzIjpbImFkbWluIiwidXNlciJdfQ.VHpxmxKVKpsn2Iytqc_6Z1U1NtiX3EgVki4PmA-J3Pg 

    一般是将它放入HTTP请求的Header Authorization字段中

     1 Authorization: Bearer 

    这里可以打开nuget:https://www.nuget.org/packages/JWT.Standard/,或者在vs中使用

    输入jwt.standard找到nuget包下载

    1.生成jwt数据

    在需要使用的地方输入如下C#代码:

    var jwtp = new JWTPackage<UserModel>(new UserModel()
    {
        Id = "1",
        Name = "yswenli",
        Role = "Admin"
    }, 180, _pwd);
    var keyValuePair = jwtp.GetAuthorizationBearer();
    context.HttpContext.Response.Headers[keyValuePair.Key] = keyValuePair.Value;

    这样就将需要的jwt内容信息加入到Http头部中,当然可以使用如下方式,以参数数据的方式传递,从而避免跨域问题

     1     var password = Guid.NewGuid().ToString("N");
     2 
     3     var jwtp1 = new JWTPackage<User>(new User()
     4     {
     5         Id = "1",
     6         Name = "yswenli",
     7         Role = "Admin"
     8     }, 180, password);
     9 
    10     var sign = jwtp1.Signature;    

    2.jwt解析验证

    JWTPackage<T>中使用JWTPackage<T>.Parse方法解析jwt的内容,如果内容是header中的参数,则快捷解析验证代码如下:

     1 var result = string.Empty;
     2 try
     3 {
     4     if (context.HttpContext.Request.Headers.ContainsKey(keyValuePair.Key))
     5     {
     6         var val = context.HttpContext.Request.Headers[keyValuePair.Key].ToString();
     7 
     8         val = val.Replace(JWTPackage.Prex, "");
     9 
    10         var jwt = JWTPackage<UserModel>.Parse(val, pwd);
    11 
    12         result = "OK";
    13     }
    14 }
    15 catch (IllegalTokenException iex)
    16 {
    17     result = $"解析失败:{iex.Message}";
    18 }
    19 catch (TokenExpiredException tex)
    20 {
    21     result = $"解析失败:{tex.Message}";
    22 }
    23 catch (SignatureVerificationException sex)
    24 {
    25     result = $"解析失败:{sex.Message}";
    26 }
    27 catch (Exception ex)
    28 {
    29     result = $"解析失败:{ex.Message}";
    30 }
    31 return result;

    四、JWT的问题

    经过上述的简单介绍,JWT不仅可用于认证,还可用于信息交换,善用JWT有助于减少服务器请求数据的次数。但是如果不正确的使用JWT也会造成安全问题,主要几点如下:

    1.保护好secret私钥,加密的密码不能泄漏,否则就失去了签名的意义了

    2.Replay Attacks,JWT的消息体中最好加入生成时间,在后端中进行时间判定,小于规定时间的直接拦截

    3.不应该在JWT的payload部分存放敏感信息,因为该部分是客户端可解密的部分

    4.建议的方式是通过SSL加密的传输(https协议),从而避免敏感信息被嗅探


    转载请标明本文来源:https://www.cnblogs.com/yswenli/p/13510050.html
    更多内容欢迎star我的的github:https://github.com/cnwenli/JWT.Net
    如果发现本文有什么问题和任何建议,也随时欢迎交流~

     
  • 相关阅读:
    第三次作业
    最后一次作业
    第14.15周作业
    第七周作业
    第六周作业
    第四周作业
    第三周作业
    第二周作业
    第一周作业
    第0次作业
  • 原文地址:https://www.cnblogs.com/yswenli/p/13510050.html
Copyright © 2020-2023  润新知