webapi中使用token验证(JWT验证)

本文介绍如何在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";
        }
    到此一切搞定。

原文地址:https://www.cnblogs.com/ye-hcj/p/8151385.html

时间: 2024-08-02 02:49:29

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

基于Token的身份验证——JWT(转)

本文转自:http://www.cnblogs.com/zjutzz/p/5790180.html 感谢作者 初次了解JWT,很基础,高手勿喷.基于Token的身份验证用来替代传统的cookie+session身份验证方法中的session. JWT是啥? JWT就是一个字符串,经过加密处理与校验处理的字符串,形式为: A.B.C A由JWT头部信息header加密得到B由JWT用到的身份验证信息json数据加密得到C由A和B加密得到,是校验部分 怎样生成A? header格式为: { "typ

WebApi_基于Token的身份验证——JWT(z)

基于Token的身份验证——JWT JWT是啥? JWT就是一个字符串,经过加密处理与校验处理的字符串,形式为: A.B.C A由JWT头部信息header加密得到B由JWT用到的身份验证信息json数据加密得到C由A和B加密得到,是校验部分 怎样生成A? header格式为: { "typ": "JWT", "alg": "HS256" } 它就是一个json串,两个字段是必须的,不能多也不能少.alg字段指定了生成C的算法

JAVA中的Token 基于Token的身份验证

来源:转载 最近在做项目开始,涉及到服务器与安卓之间的接口开发,在此开发过程中发现了安卓与一般浏览器不同,安卓在每次发送请求的时候并不会带上上一次请求的SessionId,导致服务器每次接收安卓发送的请求访问时都新建一个Session进行处理,无法通过传统的绑定Session来进行保持登录状态和通讯状态. 基于传统方法无法判断安卓的每次请求访问状态,故查询资料了解到Token,特殊的身份证验证.以下是网上搜寻资料所得,作为学习总结资料. 令牌是一种能够控制站点占有媒体的特殊帧,以区别数据帧及其他

使用JWT 进行基于 Token 的身份验证方法

一般weby应用服务都使用有状态的session来存储用户状态以便认证有权限的用户进行的操作.但服务器在做横向扩展时,这种有状态的session解决方案就受到了限制.所以在一些通常的大型web应用场景越来越多在采用没有状态的token来进行用户签权处理. 需要把Web应用做成无状态的,即服务器端无状态,就是说服务器端不会存储像会话这种东西,而是每次请求时access_token进行资源访问.这里我们将使用 JWT 1,基于散列的消息认证码,使用一个密钥和一个消息作为输入,生成它们的消息摘要.该密

在asp.net WebAPI 中 使用Forms认证和ModelValidata(模型验证)

一.Forms认证 1.在webapi项目中启用Forms认证 Why:为什么要在WebAPI中使用Forms认证?因为其它项目使用的是Forms认证. What:什么是Forms认证?它在WebAPI中就是一个MessageHandle,具体请查找关键字“ASP.NET Forms” How:如何启动Forms认证? 最简单的是通过配置启动Forms认证: 1 <system.web> 2 <authentication mode="Forms"> 3 <

关于vue的前端项目中token使用以及验证机制 携带token登录详情 vue-router的跳转说明

在login.vue中通过发送http请求获取token//根据api接口获取tokenvar url = ‘http://www.baidudd.com’ + "/session";this.$axios.post(url, {username: this.loginForm.username,password: this.loginForm.pass}).then(res => {// console.log(res.data);this.$message.success('

jwt验证

什么是JWT Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景.JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密. Token认证和Session认证的区别 传统的session认证 我们知道

Shiro&amp;Jwt验证

此篇基于 SpringBoot 整合 Shiro & Jwt 进行鉴权 相关代码编写及解析 首先我们创建 JwtFilter 类 继承自 BasicHttpAuthenticationFilter org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter 此类是一个过滤器,后期会通过Shiro配置进去 重写4个重要的方法 其执行顺序亦是如下 1. preHandle(..) 前置处理 2. isAccessAllowed(..

WebApi中的Session与Token间的处理对接

首先,说起来创建session,一般会针对注册登录或者授权等情况: session 从字面上讲,就是会话.这个就类似于你和一个人交谈,你怎么知道当前和你交谈的是张三而不是李四呢?对方肯定有某种特征(长相等)表明他就是张三.session 也是类似的道理,服务器要知道当前发请求给自己的是谁.为了做这种区分,服务器就要给每个客户端分配不同的"身份标识",然后客户端每次向服务器发请求的时候,都带上这个"身份标识",服务器就知道这个请求来自于谁了.至于客户端怎么保存这个&q