zoukankan      html  css  js  c++  java
  • 实现JWT原理

    JWT学习文章:

    第一篇:JWT原理

    第二篇:JWT原理实现代码

    第三篇:在asp.net core中使用JWT


    上一篇学习了JWT的基本理论,这一篇将根据原理进行代码实现。

    要想实现jwt的加密解密,要先生成一个SecurityKey,大家可以在网上工具生成一个随机的密钥。我是在这里生成的。

    下面篇幅大量都是代码,因为注释写得很清楚,因此就不再有过多文字说明。

    代码实现

    新建常量类:Const

    public class Const
    {
         public const string SecurityKey= "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDSfLGu+kcFDcJUCV46J+SbgR0lNc2NqgCGzojQTWW9xqjuzPF3mpisvTggYZSGfBzN+88YLZYbBLrDTUMJ4nTieElbP6SHkBFu8F+7fFBi7w3UPsaAXDr2E2srQYU5ZlKAcFBoNajNWj3sfSVRoYRPdqDTj4WdJlUPSNGz0wgRrQIDAQAB";
         public const string Domain = "http://localhost:5000";
    }

    新建控制器:AuthController

        [ApiController]
        [Route("[controller]")]
        public class AuthController  : ControllerBase
        {
            [HttpGet]
            public IActionResult Get(string userName, string pwd)
            {
                //此处只简单的验证用户名和密码的不为空,实际中使用时不要这样
                if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(pwd))
                {
                    //Header
                    var header = "{"alg": "HS256","typ": "JWT"}";
                    var headerBase = Base64UrlTextEncoder.Encode(Encoding.UTF8.GetBytes(header));
    
                    //Payload
                    var payloadDic = new Dictionary<string, object>();
                    payloadDic["iss"]= Const.Domain;
                    //添加jwt可用时间
                    var now = DateTime.UtcNow;
                    payloadDic["nbf"] = now.ToUniversalTime();//可用时间起始
                    payloadDic["exp"] = now.AddMinutes(30).ToUniversalTime();//可用时间结束
                    var payload = JsonConvert.SerializeObject(payloadDic);
                    var payloadBase = Base64UrlTextEncoder.Encode(Encoding.UTF8.GetBytes(payload));
    
                    //Signature
                    //声明hs256对象
                    var hs256 = new HMACSHA256(Encoding.UTF8.GetBytes(Const.SecurityKey));
                    //生成signature
                    var signature = hs256.ComputeHash(Encoding.UTF8.GetBytes(headerBase + "." + payloadBase));
                    var signatureBase = Base64UrlTextEncoder.Encode(signature);
                    return Ok(new
                    {
                        token = headerBase + "." + payloadBase + "." + signatureBase
                    }) ;
                }
                else
                {
                    return BadRequest(new { message = "username or password is incorrect." });
                }
            }
        }

    为了过滤哪些接口需要验证,此处新建一个特性:AuthAttribute

        public class AuthAttribute : Attribute
        {
            public AuthAttribute()
            {
            }
        }

    修改原有的Home控制器:

        [ApiController]
        [Route("[controller]")]
        public class HomeController  : ControllerBase
        {
                [HttpGet]
                [Route("api/value1")]
                public ActionResult<IEnumerable<string>> Get()
                {
                    return new string[] { "value1", "value1" };
                }
    
                [HttpGet]
                [Route("api/value2")]
                [Auth]
                public ActionResult<IEnumerable<string>> Get2()
                {
                    return new string[] { "value2", "value2" };
                }
        }

    Value2接口标记了Auth特性,在下面验证时有Auth特性标记的接口才会被要求token。

    新建静态类:AuthExtension,并且增加一个IApplicationBuilder的扩展方法:

        public static class AuthExtension
        {
            public static void AddAuthorize(this IApplicationBuilder applicationBuilder)
            {
                applicationBuilder.Use(async (currentContext, nextContext) =>
                {
                    //获取是否具有自定义的auth特性
                    var authAttribute = currentContext.GetEndpoint()?.Metadata.GetMetadata<AuthAttribute>();
                    if (authAttribute != null)
                    {
                        if (currentContext.Request.Headers.ContainsKey("Authorization"))
                        {
                            var authorize = currentContext.Request.Headers["Authorization"].ToString();
                            if (authorize.Contains("Bearer"))
                            {
                                var info = authorize.Replace("Bearer ", string.Empty);
                                var jwtStr = info.Split('.').ToArray();
                                //声明hs256对象
                                var hs256 = new HMACSHA256(Encoding.UTF8.GetBytes(Const.SecurityKey));
                                //生成signature
                                var signature = Base64UrlTextEncoder.Encode(hs256.ComputeHash(Encoding.UTF8.GetBytes(jwtStr[0] + "." + jwtStr[1])));
                                //验证加密后是否相等
                                if (jwtStr[2] == signature)
                                {
                                    //验证是否在有效时间内
                                    var now = DateTime.UtcNow.ToUniversalTime();
                                    var payload = JsonConvert.DeserializeObject<Dictionary<string, object>>(Encoding.UTF8.GetString(Base64UrlTextEncoder.Decode(jwtStr[1])));
                                    if (now >= Convert.ToDateTime(payload["nbf"]) && now <= Convert.ToDateTime(payload["exp"]))
                                    {
                                        //await currentContext.Response.WriteAsync("验证通过").ConfigureAwait(true);
                                        await nextContext?.Invoke();
                                        return;
                                    }
                                    currentContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
                                    await currentContext.Response.WriteAsync("Authorization time has passed, please log in again!").ConfigureAwait(true);
                                }
                            }
                        }
                        currentContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
                        await currentContext.Response.WriteAsync("Verification failed, no permission to access!").ConfigureAwait(true);
                    }
                    await nextContext?.Invoke();
                });
            }
        }

    在启动类Startup的请求管道中(Configure)添加上面的扩展方法:

    //添加身份验证
    app.AddAuthorize();

    注意一定要把这句话添加在UseRouting()之后,因为在扩展方法中获取Auth特性只有在注册了Routing规则后才能获取到值。

    测试

    访问无需权限的Value1接口:

    访问成功!!!

    获取token:

    我启用了swagger,如果没有启用在postman中请求https://localhost:5001/Auth?userName=admin&pwd=admin也是一样的。

    代码中用户名和密码我只是简单的验证了下是否为空,所以这里填写什么都能通过。

    获取token成功说明获取token的代码没有问题,逻辑有没有问题还不能确定,所以要经过后面接口的确认看是否成功。

    访问要求权限验证的Value2接口:

    不带token:

    访问失败!!!

    带上token:

    成功!!!

    静待三十分钟(代码中设置token过期时间为三十分钟),调用Value2接口:

    失败了!!!错误提示是token过期。

    如果觉得不保险,还可以逐步调试看一下是否所有逻辑都正确执行,这里就不再进行赘述了。

    至此证明我们依照jwt原理写的权限验证成功!!!


    源码地址:https://gitee.com/jingboweilanGO/Demo_jwt_core.git

    说明:Demo-jwt-core2是本篇文章涉及到的源码,是使用asp.net core 自带的jwt方法;

         Demo-jwt-core是第三篇jwt文章的源码,在asp.net core中的使用JWT

    如果觉得不错,可以推荐收藏一下,让我也更有动力。
  • 相关阅读:
    Storm:分布式流式计算框架
    GreenPlum:基于PostgreSQL的分布式关系型数据库
    Hive和SparkSQL: 基于 Hadoop 的数据仓库工具
    Linux网络子系统
    Python标准模块--multiprocessing
    Python标准模块--built-ins函数
    Python标准模块--threading
    Python标准模块--import
    Python标准模块--os
    Python标准模块--argparse
  • 原文地址:https://www.cnblogs.com/jingboweilan/p/14590999.html
Copyright © 2011-2022 走看看