zoukankan      html  css  js  c++  java
  • Aspnet Mvc 前后端分离项目手记(二)关于token认证

    在前后端分离的项目中,首先我们要解决的问题就是身份认证

    以往的时候,我们使用cookie+session,或者只用cookie来保持会话。

    一,先来复习一下cookie和session

    首先我们来复习一下在aspnet中cookie和session的关系,做一个简单试验

    这是一个普通的view没有任何处理

     

    可以看到,没有任何东西(cookie),然后当我们写入一个session之后

    会发现多了一个名为ASP.NET_SessionId的cookie。我们都知道在aspnet中,session是保存在服务器端的内存中的,而http协议是无状态的,那么他是怎么确定不同请求的session

    没错,session是借助cookie来实现的:cookie中保存着 session的key,当我们清除掉浏览器缓存时,会发现session也找不到了,就是这个原因。

    使用session来保持会话有几个很严重的缺点:1 session容易丢失;2无法支持分布式;3,cookie 对跨域的支持不好

    所以就用到了我们今天说的token

    二,token 

    1,token的产生

    一般是用户登录成功,服务器端产生一个token并返给前端,前端将token保存在cookie或者localStorage里面,然后每次请求时都带上这个token,一般都带在请求头里面

     2,token的内容

    一般的token里面必须有的是:1,会话用户的标识:比如userid。2,token的过期时间,如果想更完整一点,可以加上token的颁发者,签名等等

    3,token的生成算法,一般是由服务器端将token的主要内容,过期时间等等做非对称加密,然后进行签名算法(防止客户端更改),具体看后面jwt

    4,token校验

    当服务器端收到请求时,首先会校验token,校验有两种不同的方式

     一, token产生后保存在服务器端(redis或者其他比较速度快的缓存中) 。优点:可控性强,可以用这个来做单点登录,比如另一个地方登录,就remove掉之前的token。缺点:实现麻烦一点,而且要占服务器压力

    二, token产生后服务器端不保存,只负责校验。 优点:大大降低了服务器的压力,实现起来,也要相对简单一点。缺点:token一旦颁发,服务器端就不可控了,只能等它过期。

        具体用哪种看具体的需求。如果不是做可控性要求很强,个人建议第二种。

    5 jwt 

    jwt 全名Json Web Tokens,算是一种token的规范吧

    园子里面有很不不错的介绍 ,比如这篇:阮一峰 jwt介绍   http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html

     

    组成有三部分

    • Header(头部,一般包含了token的签名方式)
    • Payload(负载,也就是具体的有效部分)
    • Signature(签名,将前两部分进行签名算法,防止客户端篡改)

    实现方式,将header部分和payload部分分别进行base64算法,然后用点号“.”隔开拼接,然后进行签名算法,然后在将三部分拼接(点号隔开)就得到了jwt

    注意 ,jwt默认是采用base64编码的,也就是说 客户端也能解码得出具体内容的,所以除非特殊情况,重要敏感字段一定不能放在token中

     以下是具体实现

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Security.Cryptography;
    using System.Text;
    using System.Web;
     
    namespace Rk.JWT
    {
      
        public class Jwt
        {
     
            //参考自 阮一峰 jwt介绍  http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html
      
     
     
            public static string SALT = "OXpcRP8jmCfMKumY";
          
              
     
            /// <summary>
            ///
            /// </summary>
            /// <param name="ExraPayload">额外的信息</param>
            /// <returns></returns>
            public static string Create(Dictionary<string,object> ExraPayload)
            {
                var Header = new Dictionary<string, string>();
                Header.Add("tp", "MD5");
                var Payload = new Dictionary<string, object>();
     
                //JWT 规定了7个官方字段,供选用。
                Payload.Add("iss", "signBy"); //颁发人
                Payload.Add("jti", Guid.NewGuid().ToString()); //jwt的id
                Payload.Add("exp",System.DateTime.Now.AddMinutes(20));//过期时间
                Payload.Add("nbf", System.DateTime.Now);//生效时间
                Payload.Add("iat", System.DateTime.Now);//签发时间
                Payload.Add("sub", "subject");//主题
                Payload.Add("aud", "audience");//受众
     
                foreach (var item in ExraPayload)
                {
                    if (Payload.ContainsKey(item.Key))
                    {
     
                        throw new Exception($"{item.Key}键值已被占用 不能使用 ");
                    }
                    else
                    {
                        Payload.Add(item.Key, item.Value);
                    }
                }
                string base64Header = Base64Url(Newtonsoft.Json.JsonConvert.SerializeObject(Header));
                string base64Payload = Base64Url(Newtonsoft.Json.JsonConvert.SerializeObject(Payload));
                string tmp = base64Header + "." + base64Payload;
      
                string sign = Md5(tmp+ SALT);//加盐,重要
     
                return base64Header+"."+ base64Payload+"."+ sign;
            }
     
     
            //校验是否合法,是否过期
            public static bool Check(string token)
            {
                string base64Header = token.Split('.')[0];
                string base64Payload = token.Split('.')[1];
                string sign = token.Split('.')[2];
                string tmp = base64Header + "." + base64Payload;
                var signCheck = Md5(base64Header + "." + base64Payload + SALT);
                if(signCheck!= sign)
                {
                    return false;
                }
                var dic = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>(Base64UrlDecode(base64Payload));
                if(  Convert.ToDateTime(dic["exp"])<System.DateTime.Now)
                {
                    //过期了
                    return false;
     
                }
                return true;
     
            }
            //校验是否合法,是否过期
            public static Dictionary<string,object> GetPayLoad(string token)
            {
         
                string base64Payload = token.Split('.')[1];
              
                var dic = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>(Base64UrlDecode(base64Payload));
                
                return dic;
     
            }
     
     
            public static string Base64Url(string input)
            {
                //JWT 作为一个令牌(token),有些场合可能会放到 URL(比如 api.example.com/?token=xxx)。
                //Base64 有三个字符+、/和=,在 URL 里面有特殊含义,所以要被替换掉:=被省略、+替换成-,/替换成_ 。
                string output = "";
                    byte[] bytes = Encoding.UTF8.GetBytes(input);
                try
                {
                    output = Convert.ToBase64String(bytes).Replace('+', '-').Replace('/', '_').TrimEnd('=') ;
                  
     
                }
                catch (Exception e)
                {
                    throw e;
                }
                return output;
            }
            public static string Base64UrlDecode(string input)
            {
                string output = "";
              
                input = input.Replace('-', '+').Replace('_', '/');
                switch (input.Length % 4)
                {
                    case 2:
                        input += "==";
                        break;
                    case 3:
                        input += "=";
                        break;
                }
                byte[] bytes = Convert.FromBase64String(input);
                try
                {
                    output = Encoding.UTF8.GetString(bytes);
                }
                catch
                {
                    output = input;
                }
                return output;
            }
            public static string Md5(string input,int bit=16)
            {
                 
                MD5CryptoServiceProvider md5Hasher = new MD5CryptoServiceProvider();
                byte[] hashedDataBytes;
                hashedDataBytes = md5Hasher.ComputeHash(Encoding.GetEncoding("gb2312").GetBytes(input));
                StringBuilder tmp = new StringBuilder();
                foreach (byte i in hashedDataBytes)
                {
                    tmp.Append(i.ToString("x2"));
                }
                if (bit == 16)
                    return tmp.ToString().Substring(8, 16);
                else
                if (bit == 32) return tmp.ToString();//默认情况
                else return string.Empty;
                
            }
        }
         
    }
    

      

    使用方式

    public class HomeController : BaseController
       {
     
     
           public ActionResult Login(string username, string pwd)
           {
     
               /// 1, todo 验证用户名密码正确
     
     
               //2,//在token中加入用户id,创建token
               var dic = new Dictionary<string, object>();
               dic.Add("userid", "20125521225858");
               string token = JWT.Jwt.Create(dic);
     
     
               //验证token是否正确是否过期
               var isChecked = JWT.Jwt.Check(token);
     
               return Content("");
     
           }
       }
    

      

    下一篇我们将会聊一聊 rest 风格url在前后端分离项目中的使用

  • 相关阅读:
    Java 使用对话框选择文件并输出到控制台
    Java 数组 可变长参数 实例
    Java 返回一个整数的各个数字之和的一种方法
    weblogic控制台部署web项目图解
    合理的饮食+适当的运动=健康的身体
    Linux远程管理器xshell和xftp使用教程,以及遇到关闭Xshell后项目也停止的解决方法
    java使用freemarker 生成word文档
    MyEclipse 2014优化设置(禁用myeclipse updating indexes)
    ORACLE中查询被锁定的表,以及如何解锁
    MySql查询优化
  • 原文地址:https://www.cnblogs.com/wahson2019/p/10860973.html
Copyright © 2011-2022 走看看