转自:https://yq.aliyun.com/articles/335021/
首先说下什么是 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生成的具体代码如下:
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、将生成的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;
请发表评论