本文介绍如何在webapi中使用JWT验证

  1. 准备

    安装JWT安装包 System.IdentityModel.Tokens.Jwt
    你的前端api登录请求的方法,参考
    axios.get("api/token?username=cuong&password=1").then(function (res) {
    // 返回一个token
    /*
    token示例如下
    "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6Inllamlhd2VpIiwibmJmIjoxNTE0NjQyNTA0LCJleHAiOjE1MTQ2NDk3MDQsImlhdCI6MTUxNDY0MjUwNH0.ur97ZRviC_sfeFgDOHgaRpDePcYED6qmlfOvauPt9EA"
    */
    }).catch(function (err) {
    console.log(err);
    })
    你的前端请求后端数据执行的任意方法,传递token,参考
    var axiosInstance = window.axios.create({
    headers: {
    common: {
    Authorization: "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6ImN1b25nIiwibmJmIjoxNTE0NjE4MDgzLCJleHAiOjE1MTQ2MjUyODMsImlhdCI6MTUxNDYxODA4M30.khgxAzTEgQ86uoxJjACygTkB0Do6i_9YcmLLh97eZtE"
    }
    /*
    上面Authorization会自动映射成后端request.Headers.Authorization对象
    {
    Parameter: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6ImN1b25nIiwibmJmIjoxNTE0NjE4MDgzLCJleHAiOjE1MTQ2MjUyODMsImlhdCI6MTUxNDYxODA4M30.khgxAzTEgQ86uoxJjACygTkB0Do6i_9YcmLLh97eZtE"
    Scheme: "Bearer"
    }
    */ }
    })
    axiosInstance.get("api/value").then(function (res) {
    }).catch(function (err) {
    console.log(err);
    })
  2. 创建TokenHelper类

    在项目跟目录下创建一个TokenHelper.cs类,代码如下
    using System;
    using System.IdentityModel.Tokens.Jwt;
    using System.Security.Claims;
    using Microsoft.IdentityModel.Tokens;
    namespace TokenTest
    {
    public class TokenHelper
    {
    /// <summary>
    /// Use the below code to generate symmetric Secret Key
    /// var hmac = new HMACSHA256();
    /// var key = Convert.ToBase64String(hmac.Key);
    /// </summary>
    private const string Secret = "db3OIsj+BXE9NZDy0t8W3TcNekrF+2d/1sFnWG4HnV8TZY30iTOdtVWJG8abWvB1GlOgJuQZdcF2Luqm/hccMw==";
    public static string GenerateToken(string username, int expireMinutes = 120)
    { // 此方法用来生成 Token
    var symmetricKey = Convert.FromBase64String(Secret); // 生成二进制字节数组
    var tokenHandler = new JwtSecurityTokenHandler(); // 创建一个JwtSecurityTokenHandler类用来生成Token
    var now = DateTime.UtcNow; // 获取当前时间
    var tokenDescriptor = new SecurityTokenDescriptor // 创建一个 Token 的原始对象
    {
    Subject = new ClaimsIdentity(new[] // Token的身份证,类似一个人可以有身份证,户口本
    {
    new Claim(ClaimTypes.Name, username) // 可以创建多个
    }), Expires = now.AddMinutes(Convert.ToInt32(expireMinutes)), // Token 有效期 SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(symmetricKey), SecurityAlgorithms.HmacSha256)
    // 生成一个Token证书,第一个参数是根据预先的二进制字节数组生成一个安全秘钥,说白了就是密码,第二个参数是编码方式
    };
    var stoken = tokenHandler.CreateToken(tokenDescriptor); // 生成一个编码后的token对象实例
    var token = tokenHandler.WriteToken(stoken); // 生成token字符串,给前端使用
    return token;
    }
    public static ClaimsPrincipal GetPrincipal(string token)
    { // 此方法用解码字符串token,并返回秘钥的信息对象
    try
    {
    var tokenHandler = new JwtSecurityTokenHandler(); // 创建一个JwtSecurityTokenHandler类,用来后续操作
    var jwtToken = tokenHandler.ReadToken(token) as JwtSecurityToken; // 将字符串token解码成token对象
    if (jwtToken == null)
    return null;
    var symmetricKey = Convert.FromBase64String(Secret); // 生成编码对应的字节数组
    var validationParameters = new TokenValidationParameters() // 生成验证token的参数
    {
    RequireExpirationTime = true, // token是否包含有效期
    ValidateIssuer = false, // 验证秘钥发行人,如果要验证在这里指定发行人字符串即可
    ValidateAudience = false, // 验证秘钥的接受人,如果要验证在这里提供接收人字符串即可
    IssuerSigningKey = new SymmetricSecurityKey(symmetricKey) // 生成token时的安全秘钥
    };
    SecurityToken securityToken; // 接受解码后的token对象
    var principal = tokenHandler.ValidateToken(token, validationParameters, out securityToken);
    return principal; // 返回秘钥的主体对象,包含秘钥的所有相关信息
    } catch (Exception ex)
    {
    return null;
    }
    }
    }
    }
  3. 创建过滤器类

    当前端发送一个请求,需要接收并处理token
    在当前项目下创建一个名为Filter的文件夹
    创建一个AuthenticationAttribute类,代码如下
    using System;
    using System.Collections.Generic;
    using System.Security.Claims;
    using System.Security.Principal;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Web.Http.Filters;
    namespace TokenTest.Filter
    {
    // IAuthenticationFilter用来自定义一个webapi控制器方法属性
    public class AuthenticationAttribute : Attribute, IAuthenticationFilter
    {
    public bool AllowMultiple => false;
    public string Realm { get; set; }
    public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
    {
    // 当api发送请求,自动调用这个方法
    var request = context.Request; // 获取请求的请求体
    var authorization = request.Headers.Authorization; // 获取请求的token对象
    if (authorization == null || authorization.Scheme != "Bearer") return;
    if(string.IsNullOrEmpty(authorization.Parameter))
    {
    // 给ErrorResult赋值需要一个类实现了IHttpActionResult接口
    // 此类声明在AuthenticationFailureResult.cs文件中,此文件用来处理错误信息。
    context.ErrorResult = new AuthenticationFailureResult("Missing Jwt Token", request);
    return;
    }
    var token = authorization.Parameter; // 获取token字符串
    var principal = await AuthenticateJwtToken(token); // 调用此方法,根据token生成对应的"身份证持有人"
    if(principal == null)
    {
    context.ErrorResult = new AuthenticationFailureResult("Invalid token", request);
    }
    else
    {
    context.Principal = principal; // 设置身份验证的主体
    }
    // 此法调用完毕后,会调用ChallengeAsync方法,从而来完成WWW-Authenticate验证
    }
    private Task<IPrincipal> AuthenticateJwtToken(string token)
    {
    string userName;
    if(ValidateToken(token, out userName))
    {
    // 这里就是验证成功后要做的逻辑,也就是处理WWW-Authenticate验证
    var info = new List<Claim>
    {
    new Claim(ClaimTypes.Name, userName)
    }; // 根据验证token后获取的用户名重新在建一个声明,你个可以在这里创建多个声明
    // 作者注: claims就像你身份证上面的信息,一个Claim就是一条信息,将这些信息放在ClaimsIdentity就构成身份证了
    var infos = new ClaimsIdentity(info, "Jwt");
    // 将上面的身份证放在ClaimsPrincipal里面,相当于把身份证给持有人
    IPrincipal user = new ClaimsPrincipal(infos);
    return Task.FromResult(user);
    }
    return Task.FromResult<IPrincipal>(null);
    }
    private bool ValidateToken(string token, out string userName)
    {
    userName = null;
    var simplePrinciple = TokenHelper.GetPrincipal(token); // 调用自定义的GetPrincipal获取Token的信息对象
    var identity = simplePrinciple?.Identity as ClaimsIdentity; // 获取主声明标识
    if (identity == null) return false;
    if (!identity.IsAuthenticated) return false;
    var userNameClaim = identity.FindFirst(ClaimTypes.Name); // 获取声明类型是ClaimTypes.Name的第一个声明
    userName = userNameClaim?.Value; // 获取声明的名字,也就是用户名
    if (string.IsNullOrEmpty(userName)) return false;
    return true;
    // 到这里token本身的验证工作已经完成了,因为用户名可以解码出来
    // 后续要验证的就是浏览器的 WWW-Authenticate
    /*
    什么是WWW-Authenticate验证???
    WWW-Authenticate是早期的一种验证方式,很容易被破解,浏览器发送请求给后端,后端服务器会解析传过来的Header验证
    如果没有类似于本文格式的token,那么会发送WWW-Authenticate: Basic realm= "." 到前端浏览器,并返回401
    */
    }
    public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
    {
    // 此方法在AuthenticateAsync方法调用完成之后自动调用
    ChallengeAsync(context);
    return Task.FromResult(0);
    }
    private void ChallengeAsync(HttpAuthenticationChallengeContext context)
    {
    string parameter = null;
    if (!string.IsNullOrEmpty(Realm))
    {
    parameter = "realm=\"" + Realm + "\"";
    } // token的parameter部分已经通过jwt验证成功,这里只需要验证scheme即可
    context.ChallengeWith("Bearer", parameter); // 这个自定义扩展方法定义在HttpAuthenticationChallengeContextExtensions.cs文件中
    // 主要用来验证token的Schema是不是Bearer
    }
    }
    }
    创建AuthenticationFailureResult类,代码如下
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Net.Http;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Web.Http;
    namespace TokenTest.Filter
    {
    // 此类比较简单不做过多注释
    public class AuthenticationFailureResult : IHttpActionResult
    {
    public string _FailureReason { get; }
    public HttpRequestMessage _Request { get; }
    public AuthenticationFailureResult(string FailureReason, HttpRequestMessage request)
    {
    _FailureReason = FailureReason;
    _Request = request;
    }
    HttpResponseMessage HandleResponseMessage()
    {
    HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.Unauthorized)
    {
    RequestMessage = _Request,
    ReasonPhrase = _FailureReason
    };
    return response;
    }
    public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
    return Task.FromResult(HandleResponseMessage());
    }
    }
    }
    创建HttpAuthenticationChallengeContextExtensions类,写的context的扩展方法,代码如下
    using System;
    using System.Net.Http.Headers;
    using System.Web.Http.Filters;
    namespace TokenTest.Filter
    {
    public static class HttpAuthenticationChallengeContextExtensions
    {
    public static void ChallengeWith(this HttpAuthenticationChallengeContext context, string scheme)
    {
    ChallengeWith(context, new AuthenticationHeaderValue(scheme));
    }
    private static void ChallengeWith(HttpAuthenticationChallengeContext context, AuthenticationHeaderValue challenge)
    {
    if(context == null)
    {
    throw new ArgumentNullException(nameof(context));
    }
    context.Result = new AddChallengeOnUnauthorizedResult(challenge, context.Result);
    }
    public static void ChallengeWith(this HttpAuthenticationChallengeContext context, string scheme, string parameter)
    {
    // 第二个参数的作用是根据传进来的scheme也就是"Bearer"和parameter这里为null,创建一个验证头,和前端传过来的token是一样的
    ChallengeWith(context, new AuthenticationHeaderValue(scheme, parameter));
    }
    }
    }
    创建AddChallengeOnUnauthorizedResult类,代码如下
    using System.Linq;
    using System.Net;
    using System.Net.Http;
    using System.Net.Http.Headers;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Web.Http;
    namespace TokenTest.Filter
    {
    public class AddChallengeOnUnauthorizedResult: IHttpActionResult
    {
    public AuthenticationHeaderValue _Challenge { get; }
    public IHttpActionResult _InnerResult { get; }
    public AddChallengeOnUnauthorizedResult(AuthenticationHeaderValue challenge, IHttpActionResult innerResult)
    {
    _Challenge = challenge;
    _InnerResult = innerResult;
    }
    public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
    // 这里讲schemee也就是"Bearer"生成后的response返回给浏览器去做判断,如果浏览器请求的Authenticate中含有含有名为"Bearer"的scheme会返回200状态码否则返回401状态码
    HttpResponseMessage response = await _InnerResult.ExecuteAsync(cancellationToken);
    if(response.StatusCode == HttpStatusCode.Unauthorized)
    {
    // 如果这里不成立,但是我们之前做的验证都是成功的,这是不对的,可能出现意外情况啥的
    // 这时我们手动添加一个名为"Bearer"的sheme,让请求走通
    // 到此,完毕。
    if (response.Headers.WwwAuthenticate.All(h => h.Scheme != _Challenge.Scheme))
    {
    response.Headers.WwwAuthenticate.Add(_Challenge);
    }
    }
    return response;
    }
    }
    }
  4. 配置

    在你的WebApiConfig.cs文件中添加
    config.Filters.Add(new AuthorizeAttribute()); // 开启全局验证服务
  5. 代码使用

    创建一个webapi的控制器
    测试,用户登录
    [Route("yejiawei/haha")]
    [HttpGet]
    [AllowAnonymous] // 这个属性是必须的,表示这个类是不需要token验证的
    public string Get(string username, string password)
    {
    if (CheckUser(username, password))
    {
    return TokenHelper.GenerateToken(username);
    } throw new HttpResponseException(HttpStatusCode.Unauthorized);
    }
    public bool CheckUser(string username, string password)
    {
    // 在这里你可以在数据库中查看用户名是否存在
    return true;
    }
    测试,访问后端api数据
    [Route("yejiawei/haha")]
    [HttpGet]
    [Authentication] // 此方法验证的token需要调用Authentication属性方法
    public string Get()
    {
    return "value";
    }
    到此一切搞定。

webapi中使用token验证(JWT验证)的更多相关文章

  1. WebApi中关于base64和jwt的联合验证

    用到了如鹏的代码 jwt验证 public class MyAuthoFilterPostOrgInfoAttribute: AuthorizationFilterAttribute { public ...

  2. IOS 中使用token机制来验证用户的安全性

    登录的业务逻辑{    http:是短连接.         服务器如何判断当前用户是否登录?    // 1. 如果是即时通信类:长连接.    // 如何保证服务器跟客户端保持长连接状态? // ...

  3. WebApi 中使用 Token

    1.登陆的时候根据用户信息生成Token var token = FormsAuthentication.Encrypt( new FormsAuthenticationTicket( , " ...

  4. 基于 Token 的身份验证:JSON Web Token(附:Node.js 项目)

    最近了解下基于 Token 的身份验证,跟大伙分享下.很多大型网站也都在用,比如 Facebook,Twitter,Google+,Github 等等,比起传统的身份验证方法,Token 扩展性更强, ...

  5. 基于 Token 的身份验证:JSON Web Token

    最近了解下基于 Token 的身份验证,跟大伙分享下.很多大型网站也都在用,比如 Facebook,Twitter,Google+,Github 等等,比起传统的身份验证方法,Token 扩展性更强, ...

  6. C# 获取Header中的token值

    public CurrentUser currentUser { get { CurrentUser result = new CurrentUser(); //jwt 解密token IJsonSe ...

  7. DDD实战11 在项目中使用JWT的token 进行授权验证

    步骤: 1.首先要在webapi的管道中 使用认证(Authentication) 2.要在webapi的服务中注册验证条件 代码如下: namespace Dealer.WebApi { publi ...

  8. c# asp.net 中使用token验证

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

  9. Blazor client-side + webapi (.net core 3.1) 添加jwt验证流程(非host)第二步 添加Identity

    添加Identity数据上下文 安装nuget包:Microsoft.AspNetCore.Identity.EntityFrameworkCore 创建ApplicationDbContext类 创 ...

随机推荐

  1. 【P1274】魔术数字游戏(搜索+剪枝+模拟)

    做完了这个题的我一口老血喷在屏幕上... 这个题难度不高(~~胡扯~~),就是爆搜就可以了,然而..判断条件灰常多,剪枝也就非常多..然而,这些判断条件又不得不必须满足,所以也就十分容易错... 说一 ...

  2. 【bzoj5085】最大(二分+乱搞)

    题目传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=5085 这道题我们可以先二分答案,然后转化为判定是否有四角权值>=mid的矩形. ...

  3. Spark基本概念快速入门

      Spark集群 一组计算机的集合,每个计算机节点作为独立的计算资源,又可以虚拟出多个具备计算能力的虚拟机,这些虚拟机是集群中的计算单元.Spark的核心模块专注于调度和管理虚拟机之上分布式计算任务 ...

  4. Ubuntu下用crontab 部署定时任务

    用php做了一个网站,其中一个统计工能,需要每周定时用行.想看看有什么方法,之前看别人的东西,一般有2中方式,一个是php自带的定时任务,一个是用系统 带的,linux下的crontab和window ...

  5. jsp: jstl标签库 uri标签

    与 URL 相关的标签主要是用来将其他文件包含进来,或者提供页面之间的重定位以及 URL 地址的生成.参数的输出等等.一般包括如下几个标签<c:import>标签:与传统 JSP 页面中的 ...

  6. 经典分水岭算法的 C++ 实现

    这个程序是研一下学期的计算机视觉课程大作业,完成于 2013/06/16,是对 Soille 和 Vincent(1991)提出的模拟浸没的分水岭算法的实现,详见下面的报告. 源码托管在 Github ...

  7. IDT 查询 hana SQL 聚合问题。

    因为业务需要,用HANA的数据做成DASHBOARD.工厂运营概况.结果发现奇怪的问题.明明是一个类型的但是不会聚合.(数据量特别大,一个月的应该就一条,但是有几千条做不下去.) 比如车辆类型是 焊装 ...

  8. Win7 Nginx启动失败 cmd命令失败

    Win7  Nginx启动失败 cmd命令失败 Nginx ("engine x") 是一个高性能的 HTTP 和 反向代理 服务器,也是一个 IMAP/POP3/SMTP 代理服 ...

  9. [1]移动端页面调试之“weinre大法”

    前言 这个也是从早读课转载的.如有侵权请联系我马上下架. 正文从这开始-- 移动端页面调试一般分两步.第一步我们需要把本地(pc端)写的页面效果展现在移动端,一个很方便的办法是用 fiddler 作为 ...

  10. Linux-软件安装管理

    1.软件包分类 源码包:脚本安装包 二进制包:RPM包.系统默认包 2.rpm命令管理 @rmp包在系统光盘中 mkdir /mnt/cdrom mount /dev/sr0 /mnt/cdrom c ...