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

    如果觉得不错,可以推荐收藏一下,让我也更有动力。
  • 相关阅读:
    Qt计算器开发(三):执行效果及项目总结
    [HNOI2019]校园旅行
    How to fix nuget Unrecognized license type MIT when pack
    How to fix nuget Unrecognized license type MIT when pack
    git 通过 SublimeMerge 处理冲突
    git 通过 SublimeMerge 处理冲突
    git 上传当前分支
    git 上传当前分支
    gif 格式
    gif 格式
  • 原文地址:https://www.cnblogs.com/jingboweilan/p/14590999.html
Copyright © 2011-2022 走看看