吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2977|回复: 3
收起左侧

[其他转载] [完全图解].NET Croe 使用JWT验证签名

  [复制链接]
迷恋自留地 发表于 2021-3-6 13:40

.NET Croe 使用JWT验证签名

一、为什么使用JWT

1.跨语言使用。

2.服务器端无需再保存任何东西,只需要客户端保存token就可以。

3.实现简单。

4.统一认证方式,如果是移动端也要验证的话,jwt也支持就无需修改,否则客户端 服务器一套,移动端 服务器又是一套

当然缺陷也是很明显,就是退出登录后,已发放的token无法销毁,可以继续访问。就是令牌给你了,如果别人盗取了你的令牌,我也是认的,我只认令牌不认人。也可以设置令牌有效期,假如设置过期有效时间为10分钟,就算你拿到了令牌想用也已经过期了,但是这就要求客户端每次想要做什么,先去申请令牌,然后在去操作,这就很麻烦。内部系统的话是可以用这种模式的,如果对外的不建议使用。对外的可以使用传统的签名认证方法。

1)客户端向授权服务系统发起请求,申请获取“令牌”。

2)授权服务根据用户身份,生成一张专属“令牌”,并将该“令牌”以JWT规范返回给客户端

3)客户端将获取到的“令牌”放到http请求的headers中后,向主服务系统发起请求。主服务系统收到请求后会从headers中获取“令牌”,并从“令牌”中解析出该用户的身份权限,然后做出相应的处理(同意或拒绝返回资源)

二、在.net core webApi 搭建jwt并且使用

第一步:在程序中读取appSettings配置文件信息

  1. 创建一个AppSettings.cs类,存放读取配置信息的函数 代码如下,使用Nuget  安装Microsoft.Extensions.ConfigurationMicrosoft.Extensions.Configuration.JsonMicrosoft.Extensions.Configuration.Binder

在这里插入图片描述

 /// <summary>
    /// appsettings.json操作类
    /// </summary>
    public class Appsettings
    {
        static IConfiguration Configuration { get; set; }
        static string contentPath { get; set; }

        public Appsettings(string contentPath)
        {
            string Path = "appsettings.json";

            //如果你把配置文件 是 根据环境变量来分开了,可以这样写
            //Path = $"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json";

            Configuration = new ConfigurationBuilder()
               .SetBasePath(contentPath)
               .Add(new JsonConfigurationSource { Path = Path, Optional = false, ReloadOnChange = true })//这样的话,可以直接读目录里的json文件,而不是 bin 文件夹下的,所以不用修改复制属性
               .Build();
        }

        public Appsettings(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        /// <summary>
        /// 封装要操作的字符
        /// </summary>
        /// <param name="sections">节点配置</param>
        /// <returns></returns>
        public static string app(params string[] sections)
        {
            try
            {

                if (sections.Any())
                {
                    return Configuration[string.Join(":", sections)];
                }
            }
            catch (Exception) { }

            return "";
        }

        /// <summary>
        /// 递归获取配置信息数组
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="sections"></param>
        /// <returns></returns>
        public static List<T> app<T>(params string[] sections)
        {
            List<T> list = new List<T>();
            // 引用 Microsoft.Extensions.Configuration.Binder 包
            Configuration.Bind(string.Join(":", sections), list);
            return list;
        }
    }

找到webApi项目,打开Startup类,在ConfigureService函数 注册AppSettings

在这里插入图片描述

  1. 在Api项目中 添加Nuget 包 IdentityModelMicrosoft.AspNetCore.Authentication.JwtBearerMicrosoft.AspNetCore.Authorization

    appsettings.json添加节点

    在这里插入图片描述

    "Audience": {
    "Secret": "qwertyuiopasdfghjklzxcvbnm123456", //不要太短,16位+
    "Issuer": "Core.Demo",
    "Audience": "milianziliudi"
    },

在这里插入图片描述

  1. 创建JwtHelper帮助类
   public class JwtHelper
    {

        /// <summary>
        /// 颁发JWT字符串
        /// </summary>
        /// <param name="tokenModel"></param>
        /// <returns></returns>
        public static string IssueJwt(TokenModelJwt tokenModel)
        {
            // 自己封装的 appsettign.json 操作类,看下文
            string iss = Appsettings.app(new string[] { "Audience", "Issuer" });
            string aud = Appsettings.app(new string[] { "Audience", "Audience" });
            string secret = Appsettings.app(new string[] { "Audience", "Secret" });

            var claims = new List<Claim>
              {
                 /*
                 * 特别重要:
                   1、这里将用户的部分信息,比如 uid 存到了Claim 中,如果你想知道如何在其他地方将这个 uid从 Token 中取出来,请看下边的SerializeJwt() 方法,或者在整个解决方案,搜索这个方法,看哪里使用了!
                   2、你也可以研究下 HttpContext.User.Claims ,具体的你可以看看 Policys/PermissionHandler.cs 类中是如何使用的。
                 */                

                new Claim(JwtRegisteredClaimNames.Jti, tokenModel.Uid.ToString()),
                new Claim(JwtRegisteredClaimNames.Iat, $"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}"),
                new Claim(JwtRegisteredClaimNames.Nbf,$"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}") ,
                //这个就是过期时间,目前是过期1000秒,可自定义,注意JWT有自己的缓冲过期时间
                new Claim (JwtRegisteredClaimNames.Exp,$"{new DateTimeOffset(DateTime.Now.AddSeconds(1000)).ToUnixTimeSeconds()}"),
                new Claim(JwtRegisteredClaimNames.Iss,iss),
                new Claim(JwtRegisteredClaimNames.Aud,aud),

                //new Claim(ClaimTypes.Role,tokenModel.Role),//为了解决一个用户多个角色(比如:Admin,System),用下边的方法
               };

            // 可以将一个用户的多个角色全部赋予;
            // 作者:DX 提供技术支持;
            claims.AddRange(tokenModel.Role.Split(',').Select(s => new Claim(ClaimTypes.Role, s)));

            //秘钥 (SymmetricSecurityKey 对安全性的要求,密钥的长度太短会报出异常)
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret));
            var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

            var jwt = new JwtSecurityToken(
                issuer: iss,
                claims: claims,
                signingCredentials: creds);

            var jwtHandler = new JwtSecurityTokenHandler();
            var encodedJwt = jwtHandler.WriteToken(jwt);

            return encodedJwt;
        }

        /// <summary>
        /// 解析
        /// </summary>
        /// <param name="jwtStr"></param>
        /// <returns></returns>
        public static TokenModelJwt SerializeJwt(string jwtStr)
        {
            var jwtHandler = new JwtSecurityTokenHandler();
            JwtSecurityToken jwtToken = jwtHandler.ReadJwtToken(jwtStr);
            object role;
            try
            {
                jwtToken.Payload.TryGetValue(ClaimTypes.Role, out role);
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }
            var tm = new TokenModelJwt
            {
                Uid = int.Parse(jwtToken.Id),
                Role = role != null ? role.ToString() : "",
            };
            return tm;
        }
    }

    /// <summary>
    /// 令牌
    /// </summary>
    public class TokenModelJwt
    {
        /// <summary>
        /// Id
        /// </summary>
        public long Uid { get; set; }
        /// <summary>
        /// 角色
        /// </summary>
        public string Role { get; set; }
        /// <summary>
        /// 职能
        /// </summary>
        public string Work { get; set; }

    }

新建一个控制器LoginController,在控制器中添加登录方法
在这里插入图片描述

在这里插入图片描述

       /// <summary>
        /// 登录验证并且获取token
        /// </summary>
        /// <param name="loginModel"></param>
        /// <returns></returns>
        [HttpPost]
        public IActionResult LoginValidate(LoginModel loginModel)
        {
            string jwtStr = string.Empty;
            bool suc = false;

            if (loginModel != null)
            {
                //加登录验证
                if (loginModel.UserName == "admin" && loginModel.PassWord == "123456")
                {
                   var  tokenModel = new TokenModelJwt { Uid = 1, Role = loginModel.PassWord };
                    jwtStr = JwtHelper.IssueJwt(tokenModel);
                    suc = true;
                }

            }

            return Ok(new
            {
                success = suc,
                token = jwtStr
            });
        }

在这里插入图片描述
获取Token成功
在这里插入图片描述

三. 在Swagger中开启 JWT服务

我们要在Swagger中开启 JWT服务,先安装包
Swashbuckle.AspNetCore.Filters

然后找到SwaggerSetUp.cs的AddSwaggerSetUp方法中增加以下代码
在这里插入图片描述
关于1和2的报错,可能是因为没有设置生成xml文件,具体操作看下图
然后启动项目就可以看到生成的注释(在方法前面加的)和 头部小锁
在这里插入图片描述
可以看到 token入口了,按要求的格式在 value中输入 token 以后的请求head 就会一直加入token了

在这里插入图片描述

1.授权

ConfigureServices方法中添加

            #region 【授权】
            #region 1、基于角色的API授权 

            // 1【授权】、这个很简单,其他什么都不用做,
            // 无需配置服务,只需要在API层的controller上边,增加特性即可,注意,只能是角色的:
            // [Authorize(Roles = "Admin")]

            // 2【认证】、然后在下边的configure里,配置中间件即可:app.UseMiddleware<JwtTokenAuth>();但是这个方法,无法验证过期时间,所以如果需要验证过期时间,还是需要下边的第三种方法,官方认证

            #endregion

            #region 2、基于策略的授权(简单版)

            // 1【授权】、这个和上边的异曲同工,好处就是不用在controller中,写多个 roles 。
            // 然后这么写 [Authorize(Policy = "Admin")]
            services.AddAuthorization(options =>
            {
                options.AddPolicy("Client", policy => policy.RequireRole("Client").Build());
                options.AddPolicy("Admin", policy => policy.RequireRole("Admin").Build());
                options.AddPolicy("SystemOrAdmin", policy => policy.RequireRole("Admin", "System"));
            });

            // 2【认证】、然后在下边的configure里,配置中间件即可:app.UseMiddleware<JwtTokenAuth>();但是这个方法,无法验证过期时间,所以如果需要验证过期时间,还是需要下边的第三种方法,官方认证
            #endregion 
            #endregion

在这里插入图片描述

2认证

ConfigureServices方法中添加
在这里插入图片描述

            #region 【认证】
            //读取配置文件
            var audienceConfig = Configuration.GetSection("Audience");
            var symmetricKeyAsBase64 = audienceConfig["Secret"];
            var keyByteArray = Encoding.ASCII.GetBytes(symmetricKeyAsBase64);
            var signingKey = new SymmetricSecurityKey(keyByteArray);

            //2.1【认证】
            services.AddAuthentication(x =>
            {
                x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
             .AddJwtBearer(o =>
             {
                 o.TokenValidationParameters = new TokenValidationParameters
                 {
                     ValidateIssuerSigningKey = true,
                     IssuerSigningKey = signingKey,
                     ValidateIssuer = true,
                     ValidIssuer = audienceConfig["Issuer"],//发行人
                     ValidateAudience = true,
                     ValidAudience = audienceConfig["Audience"],//订阅人
                     ValidateLifetime = true,
                     ClockSkew = TimeSpan.Zero,
                     RequireExpirationTime = true,
                 };

             });
            #endregion

无论添加或者不添加 token ,都不会报错,虽然不报错,但是如果不添加token的话,会401异常,这是正常的,毕竟我们已经加上了[Authorize]授权特性和认证中间件了。
在这里插入图片描述

3.测试

有关授权认证就完成了,下面在login控制器里面新建两个方法进行测试

/// <summary>
        /// 测试权限(只有Admin权限)
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpPost]
        [Authorize(Policy = "Admin")]
        public async Task<object> test2(int id)
        {

            return Ok(new
            {
                success = 200,
                body = Appsettings.app(new string[] { "AllowedHosts" })
            });
        }

        /// <summary>
        /// 测试权限(Client)
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpPost]
        [Authorize(Policy = "Client")]
        public async Task<object> test3(int id)
        {

            return Ok(new
            {
                success = 200,
                body = Appsettings.app(new string[] { "AllowedHosts" })
            });
        }

首先获取token,把token放到小锁里面 在这里插入图片描述
只有admin权限的ok
在这里插入图片描述
client的没有权限就直接403
在这里插入图片描述

四. 参考

  1. 老张的哲学
  2. 他的github

关于其他个人笔记

  1. C# 使用RabbitMQ的完整图解
  2. VS中进行编码时智能提示由英文切换为中文
  3. 开源项目-一沙后台管理(core-mvc-缓存,支持多数据库)
  4. ASP.NET Core中使用NLog记录日志
  5. Visual Studio中Git的使用

在这里插入图片描述
在这里插入图片描述

免费评分

参与人数 2吾爱币 +1 热心值 +2 收起 理由
NULL2019 + 1 + 1 我很赞同!
夜泉 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

Light紫星 发表于 2021-3-6 14:34
学习了,刚好我们的项目也用到了jwt,不过我们的客户端是c#写的,服务端是Java
xiaochezi01 发表于 2021-7-30 17:05
不苦小和尚 发表于 2021-10-18 07:15
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-25 14:22

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表