• 【转】JSON WEB TOKEN,简单谈谈TOKEN的使用及在C#中的实现


    转https://www.cnblogs.com/chenwolong/p/Token.html

    首先说下什么是 JWT -- JSON WEB TOKEN,网上关于它的介绍已经很多很多啦,在此,推荐给大家一篇写的比较好的文章:什么是 JWT -- JSON WEB TOKEN  

    以及Token的组成部分Token存放的信息

    OK,今天我想介绍的不再是理论,而是如何在C#中应用,说白了就是怎么写程序呗。

    借用 什么是 JWT -- JSON WEB TOKEN 文章中一句话:

    基于token的鉴权机制类似于http协议也是无状态的,它不需要在服务端去保留用户的认证信息或者会话信息。这就意味着基于token认证机制的应用不需要去考虑用户在哪一台服务器登录了,这就为应用的扩展提供了便利。

    流程上是这样的:

    • 用户使用用户名密码来请求服务器
    • 服务器进行验证用户的信息
    • 服务器通过验证发送给用户一个token
    • 客户端存储token,并在每次请求时附送上这个token值
    • 服务端验证token值,并返回数据

    这个token必须要在每次请求时传递给服务端,它应该保存在请求头里。

    OK,按照上述的流程,首先我们应当拿到登录的账户,密码等信息,验证通过后,生成TOKEN并发送给客户端,之后客户端的每个请求只需带上这个TOKEN,服务器端对这个TOKEN验证,验证通过后即可访问服务器资源,。

    具体在C#中如何模仿这个流程呢?

    • 用户使用用户名密码来请求服务器
    • 服务器进行验证用户的信息

    上述二个步骤其实是个登录过程,在此不作说明!

    • 服务器通过验证发送给用户一个token

    发送给客户端一个Token,这个就需要我们生成Token了,那么怎样生成呢?理论模块可参考:Token的组成部分Token存放的信息

     1、用C#生成Token:

    首先引入JWT.dll

    Token生成的具体代码如下:

     View Code

    2、将生成的Token发送给客户端后,随后,客户端的每次请求只需带上这个Token即可

    一般都是将Token存放在Http请求的Headers中,也就是:context.Request.Headers,那么如何接收请求头中的Token呢?接收到Token后如何验证呢?

    验证TOKEN时就需要构建 MVC Action 过滤器(AuthorizeAttribute)了,不过在构建 AuthorizeAttribute 之前,有必要对 AuthorizeAttribute 说明下,如下:

    首先,AuthorizeAttribute 类位于System.Web.Http 命名空间下及System.Web.Mvc命名空间下,

    一般情况下,如果你需要对C# MVC 控制器的访问作认证与授权,你需要用System.Web.Mvc命名空间下的 AuthorizeAttribute ,如果你需要对C# API 控制器的访问作认证与授权,你需要用System.Web.Http 命名空间下的 AuthorizeAttribute !

    OK,知道了上述两种不同命名空间下的 AuthorizeAttribute ,下面以范例作为说明:

    2.1、自定义MVC ACTION 登录授权验证,(由于本篇博客主讲 Token 的验证与实现,因此,关于MVC 登录验证只做代码说明:

    2.1.1、新建一个MVC控制器,命名为BaseController,代码如下:

    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.Security;
    
    namespace TestForToken.Controllers
    {
        public class BaseController : Controller
        {
            #region 退出登录
            /// <summary>
            /// 退出登录
            /// </summary>
            public void ClearLogin()
            {
                FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(
                       1,
                       "",
                       DateTime.Now,
                       DateTime.Now.AddMinutes(-30),
                       false,
                       "",
                       "/"
                       );
                //.ASPXAUTH
                string encryptedTicket = FormsAuthentication.Encrypt(authTicket);
                System.Web.HttpCookie authCookie = new System.Web.HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
                System.Web.HttpContext.Current.Response.Cookies.Add(authCookie);
    
            }
            #endregion
    
            #region 自定义过滤器
            /// <summary>
            /// 自定义过滤器
            /// </summary>
            /// <param name="filterContext"></param>
            protected override void OnActionExecuting(System.Web.Mvc.ActionExecutingContext filterContext)
            {
                string cookieName = FormsAuthentication.FormsCookieName;
                HttpCookie authCookie = System.Web.HttpContext.Current.Request.Cookies[cookieName];
                FormsAuthenticationTicket authTicket = null;
                try
                {
                    authTicket = FormsAuthentication.Decrypt(authCookie.Value);
                }
                catch (Exception ex)
                {
                    return;
                }
                if (authTicket != null && filterContext.HttpContext.User.Identity.IsAuthenticated)
                {
                    string UserName = authTicket.Name;
                    
                    base.OnActionExecuting(filterContext);
                }
                else
                {
                    Content("<script >top.location.href='/Home/Login';</script >", "text/html");
                    //filterContext.HttpContext.Response.Redirect("/Home/Logins");
                }
            }
            #endregion
    
            #region 读取错误信息
            /// <summary>
            /// 读取错误信息
            /// </summary>
            /// <returns></returns>
            public string GetError()
            {
                var errors = ModelState.Values;
                foreach (var item in errors)
                {
                    foreach (var item2 in item.Errors)
                    {
                        if (!string.IsNullOrEmpty(item2.ErrorMessage))
                        {
                            return item2.ErrorMessage;
                        }
                    }
                }
                return "";
            }
            #endregion
    
        }
    }
    复制代码

    2.2.2、新建一个MVC控制器,命名为HomeController,代码如下:

    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    using TestForToken.Models;
    
    namespace TestForToken.Controllers
    {
        public class HomeController : BaseController
        {
            public ActionResult Login()
            {
                ClearLogin();
                string HX_userName = CommonMethod.getCookie("HX_userName");
                string HX_userPwd = CommonMethod.getCookie("HX_userPwd");
                ViewBag.HX_userName = HX_userName;
                ViewBag.HX_userPwd = HX_userPwd;
                return View();
            }
    
            [HttpPost]
            public object UserLogin(LoginsModel LoginMol)
            {
                if (ModelState.IsValid)//是否通过Model验证
                {
                    return LoginMol.LoginAction();
                }
                else
                {
                    return GetError();
                }
            }
        }
    }
    复制代码

    2.2.3、新建一个登录实体类,命名为:LoginsModel,代码如下:

    复制代码
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.Linq;
    using System.Web;
    using System.Web.Security;
    
    namespace TestForToken.Models
    {
        public class LoginsModel
        {
            private readonly object LOCK = new object();
            [Required(ErrorMessage = "请输入账户号码/手机号")]
            [RegularExpression(@"^1[34578][0-9]{9}$", ErrorMessage = "手机号格式不正确")]
            public string UserName { get; set; }
    
            [Required(ErrorMessage = "请输入账户密码")]
            [DataType(DataType.Password, ErrorMessage = "密码格式不正确")]
            public string UserPwd { get; set; }
    
            public bool remember { get; set; }
    
            public string LoginAction()
            {
                lock (LOCK)
                {
                    string userRole = string.Empty;
                    //数据库操作代码
                    int UserId = 0;
                    if (UserName == "18137070152" && UserPwd == "18137070152")
                    {
                        UserId = 1;
                        userRole = "HomeCare.Administrator";
                    }
                    else if (UserName == "18911695087" && UserPwd == "18911695087")
                    {
                        UserId = 2;
                        userRole = "HomeCare.Vip";
                    }
                    else
                    {
                        UserId = 3;
                        userRole = "HomeCare.User";
                    }
                    if (UserId != 0)
                    {
                        if (remember)
                        {
                            CommonMethod.setCookie("HX_userName", UserName, 7);
                            CommonMethod.setCookie("HX_userPwd", UserPwd, 7);
                        }
                        FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(
                               1,
                               UserName + "_" + UserId,
                               DateTime.Now,
                               DateTime.Now.AddMinutes(30),
                               false,
                               userRole,
                               "/"
                               );
                        //.ASPXAUTH
                        string encryptedTicket = FormsAuthentication.Encrypt(authTicket);
                        System.Web.HttpCookie authCookie = new System.Web.HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
                        System.Web.HttpContext.Current.Response.Cookies.Add(authCookie);
                        return "HomeCare.Administrator";
                    }
                    else
                    {
                        return "账户密码不存在";
                    }
                }
            }
    
        }
    
    }
    复制代码

    2.2.4、修改你的Global.asax文件,修改代码如下:

    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Security.Principal;
    using System.Web;
    using System.Web.Http;
    using System.Web.Mvc;
    using System.Web.Optimization;
    using System.Web.Routing;
    using System.Web.Security;
    
    namespace TestForToken
    {
        // 注意: 有关启用 IIS6 或 IIS7 经典模式的说明,
        // 请访问 http://go.microsoft.com/?LinkId=9394801
    
        public class MvcApplication : System.Web.HttpApplication
        {
            protected void Application_Start()
            {
                AreaRegistration.RegisterAllAreas();
    
                WebApiConfig.Register(GlobalConfiguration.Configuration);
                FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
                RouteConfig.RegisterRoutes(RouteTable.Routes);
                BundleConfig.RegisterBundles(BundleTable.Bundles);
            }
    
            /// <summary>
            /// 登录验证、s授权
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            protected void Application_AuthenticateRequest(Object sender, EventArgs e)
            {
                string cookieName = FormsAuthentication.FormsCookieName;
                HttpCookie authCookie = Context.Request.Cookies[cookieName];
                FormsAuthenticationTicket authTicket = null;
                try
                {
                    authTicket = FormsAuthentication.Decrypt(authCookie.Value);
                }
                catch (Exception ex)
                {
                    return;
                }
                string[] roles = authTicket.UserData.Split(',');
                FormsIdentity id = new FormsIdentity(authTicket);
                GenericPrincipal principal = new GenericPrincipal(id, roles);
                Context.User = principal;//存到HttpContext.User中     
            }
        }
    }
    复制代码

    2.2.5、公共访问类CommonCS部分代码如下:

    复制代码
    using System;
    using System.Collections.Generic;
    using System.Web;
    using System.Collections;
    using System.Text;
    using System.Text.RegularExpressions;
    using System.Data;
    using System.Drawing;
    
    
    namespace TestForToken
    {
        public class CommonMethod
        {
            #region cookie操作
            /// <summary>
            /// Cookies赋值
            /// </summary>
            /// <param name="strName">主键</param>
            /// <param name="strValue">键值</param>
            /// <param name="strDay">有效天数</param>
            /// <returns></returns>
            public static bool setCookieForMIn(string strName, string strValue, int Mintius)
            {
                try
                {
                    HttpCookie Cookie = new HttpCookie(strName);
                    //Cookie.Domain = ".xxx.com";//当要跨域名访问的时候,给cookie指定域名即可,格式为.xxx.com
                    Cookie.Expires = DateTime.Now.AddMinutes(Mintius);
                    Cookie.Value = strValue;
                    System.Web.HttpContext.Current.Response.Cookies.Add(Cookie);
                    return true;
                }
                catch
                {
                    return false;
                }
            }
    
            /// <summary>
            /// Cookies赋值
            /// </summary>
            /// <param name="strName">主键</param>
            /// <param name="strValue">键值</param>
            /// <param name="strDay">有效天数</param>
            /// <returns></returns>
            public static bool setCookie(string strName, string strValue, int strDay)
            {
                try
                {
                    HttpCookie Cookie = new HttpCookie(strName);
                    //Cookie.Domain = ".xxx.com";//当要跨域名访问的时候,给cookie指定域名即可,格式为.xxx.com
                    Cookie.Expires = DateTime.Now.AddDays(strDay);
                    Cookie.Value = strValue;
                    System.Web.HttpContext.Current.Response.Cookies.Add(Cookie);
                    return true;
                }
                catch
                {
                    return false;
                }
            }
    
            /// <summary>
            /// 读取Cookies
            /// </summary>
            /// <param name="strName">主键</param>
            /// <returns></returns>
    
            public static string getCookie(string strName)
            {
                HttpCookie Cookie = System.Web.HttpContext.Current.Request.Cookies[strName];
                if (Cookie != null)
                {
                    return Cookie.Value.ToString();
                }
                else
                {
                    return null;
                }
            }
    
            /// <summary>
            /// 删除Cookies
            /// </summary>
            /// <param name="strName">主键</param>
            /// <returns></returns>
            public static bool delCookie(string strName)
            {
                try
                {
                    HttpCookie Cookie = new HttpCookie(strName);
                    //Cookie.Domain = ".xxx.com";//当要跨域名访问的时候,给cookie指定域名即可,格式为.xxx.com
                    Cookie.Expires = DateTime.Now.AddDays(-1);
                    System.Web.HttpContext.Current.Response.Cookies.Add(Cookie);
                    return true;
                }
                catch
                {
                    return false;
                }
            }
            #endregion
        }
    }
    复制代码

    2.2.6、公共Token生成类代码如下:

    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    /*----------------------------------------------------------------
        Copyright (C) 2017 陈卧龙
        
        文件名:TestForToken.CommonCS
        文件功能描述:Token相关操作
    ----------------------------------------------------------------*/
    namespace TestForToken.CommonCS
    {
        public class CommonToken
        {
            public static string SecretKey = "This is a private key for Server";//这个服务端加密秘钥 属于私钥
    
            public static string GenToken(TokenInfo M)
            {
                var jwtcreated =
                   Math.Round((DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds + 5);
                var jwtcreatedOver =
                Math.Round((DateTime.UtcNow.AddHours(2) - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds + 5);
                var payload = new Dictionary<string, dynamic>
                    {
                        {"iss", M.iss},//非必须。issuer 请求实体,可以是发起请求的用户的信息,也可是jwt的签发者。
                        {"iat", jwtcreated},//非必须。issued at。 token创建时间,unix时间戳格式
                        {"exp", jwtcreatedOver},//非必须。expire 指定token的生命周期。unix时间戳格式
                        {"aud", M.aud},//非必须。接收该JWT的一方。
                        {"sub", M.sub},//非必须。该JWT所面向的用户
                        {"jti", M.jti},//非必须。JWT ID。针对当前token的唯一标识
                        {"UserName", M.UserName},//自定义字段 用于存放当前登录人账户信息
                        {"UserPwd", M.UserPwd},//自定义字段 用于存放当前登录人登录密码信息
                        {"UserRole", M.UserRole},//自定义字段 用于存放当前登录人登录权限信息
                    };
                return JWT.JsonWebToken.Encode(payload, SecretKey,
                    JWT.JwtHashAlgorithm.HS256);
            }
        }
    
        public class TokenInfo
        {
            public TokenInfo()
            {
                iss = "签发者信息";
                aud = "http://example.com";
                sub = "HomeCare.VIP";
                jti = DateTime.Now.ToString("yyyyMMddhhmmss");
                UserName = "jack.chen";
                UserPwd = "jack123456";
                UserRole = "HomeCare.Administrator";
            }
            //
            public string iss { get; set; }
            public string aud { get; set; }
            public string sub { get; set; }
            public string jti { get; set; }
            public string UserName { get; set; }
            public string UserPwd { get; set; }
            public string UserRole { get; set; }
        }
    }
    复制代码

    2.2.7、新建一个登录页面,Login.cshtml代码如下:

     View Code

    2.2.8、新建一个登录验证属性,继承自:System.Web.Mvc.AuthorizeAttribute,代码如下:(千呼万唤始出来啊......~_~)

     View Code

    2.2.9、新建一个MVC 控制器 ,命名为:MangerController,代码如下:

    上述代码就不做演示了,大致过程是这样的:

    Manger/Index的访问权限如下:

    登录用户:

    上述截图已经很清晰了,不再作重复说明。

    OK,上述代码便是整个MVC 控制器 登录验证/授权认证的全部代码。下面我们请出本文终极BOSS,如果接收并解析验证接收的TOKEN。

     3、下面介绍webAPI Controller 的认证授权

    3.1、首先,我们自定义一个继承自System.Web.Http 命名空间下的AuthorizeAttribute 属性来解析并验证TOKEN

    代码如下:

     View Code

    3.2、新增一个存储解析Token结果的类,命名为SysHelper.cs,代码如下:

     View Code

    3.3、公共Token生成方法修改如下:

     View Code

    3.4、定义一个MVC API Controller 代码如下:

     View Code

    OK,有了上述代码我们就可以模拟TOKEN验证了,模拟步骤如下:/

    3.5、模拟TOKEN验证:

    3.5.1、生成TOKEN,代码如下:

     View Code

    生成的TOKEN为:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiLnrb7lj5HogIXkv6Hmga8iLCJpYXQiOjE1MDk2OTEyODQsImV4cCI6MTUwOTY5ODQ4NCwiYXVkIjoiaHR0cDovL2V4YW1wbGUuY29tIiwic3ViIjoiSG9tZUNhcmUuVklQIiwianRpIjoiMjAxNzExMDMwMjQxMTkiLCJVc2VySWQiOjEsIlVzZXJOYW1lIjoiamFjay5jaGVuIiwiVXNlclB3ZCI6ImphY2sxMjM0NTYiLCJVc2VyUm9sZSI6IkhvbWVDYXJlLkFkbWluaXN0cmF0b3IifQ.IryLo19SSghi34LD1PNIOmzgzavQrnmGBD42pdojXtg

    3.5.2、将获取的TOKEN(有效期两个小时)返回至客户端,客户端将获取的TOKEN放在 请求头 Headers 中,模拟请求如下(PostMan):

    OK,将上述TOKEN随便去掉一个字母,请求结果如下:

    过期的TOKEN,请求如下:

    OK,关于TOKEN更详细的验证,还需要大家自行完善,本篇博客我仅仅只验证了TOKEN是否正确,没有作进一步的验证,大家可根据项目需求,主动完善TOKEN验证代码。

     例如:读取TOKEN存放的用户名,密码,角色等信息再作数据库验证。或者如同上述的MVC 控制器控制,验证角色等信息,总之,,,,,,,不写了,太累,还有任务没完成呢。

    哈!见谅!

    项目源码位置http://download.csdn.net/download/wolongbb/10102574

  • 相关阅读:
    搜广推04-信息检索任务&数据集&LeadBoard&评价指标
    搜广推&NLP03-顶会track记录
    搜广推02-DeepMatch 模型总结[SIGIR2019 tutorial]
    搜广推01-信息检索领域大佬总结
    计算机基础01-终端命令行、VIM、git、CICD
    【python】彼岸图网4K壁纸批量爬虫共1.48G(多线程/多进程)
    【python】不到500行代码实现flappybird小游戏
    解决pyinstaller打包程序太大的问题
    解决pipenv install报错FileNotFoundError: [Errno 2] No such file or directory: ‘d:\miniconda3\Lib\venv
    【python】如何将matplotlib的标题置于图片下方
  • 原文地址:https://www.cnblogs.com/janeaiai/p/11236347.html
Copyright © 2020-2023  润新知