三、JWT(JSON Web Tokens)令牌(token)

一、什么是JWT

根据维基百科定义,JWT(读作 [/dʒɒt/]),即JSON Web Tokens,是一种基于JSON的、用于在网络上声明某种主张的令牌(token)。JWT通常由三部分组成: 头信息(header), 消息体(payload)和签名(signature)。它是一种用于双方之间传递安全信息的表述性声明规范。JWT作为一个开放的标准(RFC 7519),定义了一种简洁的、自包含的方法,从而使通信双方实现以JSON对象的形式安全的传递信息。

二、在appsettings.json中配置jwt参数的值

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "AppSettings": {
    //数据库连接字符串
    "ConnectionStrings": "Server=127.0.0.1;User Id=id;Password=pwd;Database=dbname;",
    //jwt参数配置
    "JwtSetting": {
      "Issuer": "jwtIssuer", //颁发者
      "Audience": "jwtAudience", //可以给哪些客户端使用
      "SecretKey": "jwtsecretkeysixteen" //加密的Key,大于16位的字符串
    }
  }
}

 三、新建用户信息类TokenModel.cs

namespace Model
{
    /// <summary>
    /// 令牌
    /// </summary>
    public class TokenModel
    {
        /// <summary>
        /// ID
        /// </summary>
        public string? Uid { get; set; }
        /// <summary>
        /// 角色
        /// </summary>
        public string? Role { get; set; }
    }
}

四、新建JwtHelper.cs辅助类,并在Nuget添加包

IdentityModel,

Microsoft.AspNetCore.Authentication.JwtBearer,

Microsoft.AspNetCore.Authorization

using Microsoft.IdentityModel.Tokens;
using Model;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

namespace Common
{
    public class JwtHelper
    {
        /// <summary>
        /// 颁发JWT字符串
        /// </summary>
        /// <param name="tokenModel"></param>
        /// <returns></returns>
        public static string IssueJwt(TokenModel tokenModel)
        {
            //获Appsetting 
            //文章地址:https://blog.csdn.net/m0_37894611/article/details/123526554?spm=1001.2014.3001.5501            
            string iss = AppSetting.app(new string[] { "AppSettings", "JwtSetting", "Issuer" });//颁发者
            string aud = AppSetting.app(new string[] { "AppSettings", "JwtSetting", "Audience" });//可以给哪些客户端使用
            string secret = AppSetting.app(new string[] { "AppSettings", "JwtSetting", "SecretKey" });//加密的Key

            //定义需要使用的Claim
            var claims = new List<Claim>
            {
                new Claim(JwtRegisteredClaimNames.Jti, $"{tokenModel.Uid}"),
                new Claim(JwtRegisteredClaimNames.Iat, $"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}"),
                new Claim(JwtRegisteredClaimNames.Nbf,$"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}"),
                //这个就是过期时间,目前是过期180秒,可自定义,注意JWT有自己的缓冲过期时间
                new Claim (JwtRegisteredClaimNames.Exp,$"{new DateTimeOffset(DateTime.Now.AddSeconds(180)).ToUnixTimeSeconds()}"),
                new Claim(ClaimTypes.Expiration, DateTime.Now.AddSeconds(180).ToString()),
                new Claim(JwtRegisteredClaimNames.Iss,iss),
                new Claim(JwtRegisteredClaimNames.Aud,aud),
            };

            // 可以将一个用户的多个角色全部赋予;
            #pragma warning disable CS8602 // 解引用可能出现空引用。
            claims.AddRange(tokenModel.Role.Split(',').Select(s => new Claim(ClaimTypes.Role, s)));
            #pragma warning restore CS8602 // 解引用可能出现空引用。

            //秘钥 (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 TokenModel SerializeJwt(string jwtStr)
        {
            var jwtHandler = new JwtSecurityTokenHandler();
            JwtSecurityToken jwtToken = jwtHandler.ReadJwtToken(jwtStr);
            object role;
            try
            {
#pragma warning disable CS8600 // 将 null 字面量或可能为 null 的值转换为非 null 类型。
                jwtToken.Payload.TryGetValue(ClaimTypes.Role, out role);
#pragma warning restore CS8600 // 将 null 字面量或可能为 null 的值转换为非 null 类型。
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }
            var tm = new TokenModel
            {
                Uid = jwtToken.Id.ToString(),
                Role = role != null ? role.ToString() : "",
            };
            return tm;
        }
    }
}

五、UserController新建Login接口,用来获取token

using Common;
using Microsoft.AspNetCore.Mvc;
using Model;

namespace MainProject.Controllers
{
    /// <summary>
    /// 用户控制器
    /// </summary>
    [Route("[controller]/[action]")]
    [ApiController]
    public class UserController : ControllerBase
    {
        /// <summary>
        /// 登录验证
        /// </summary>
        /// <param name="role">用户角色,通常是通过账号密码验证后获取,这里测试直接当做参数</param>
        /// <returns></returns>
        [HttpGet]
        public IActionResult Login(string role)
        {
            string jwtStr = string.Empty;
            bool suc = false;

            if (role != null)
            {
                // 将用户id和角色名,作为单独的自定义变量封装进 token 字符串中。
                TokenModel tokenModel = new TokenModel { Uid = "123", Role = role };
                jwtStr = JwtHelper.IssueJwt(tokenModel);//获取到一定规则的 Token 令牌
                suc = true;
            }
            else
            {
                jwtStr = "login fail!!!";
            }

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

运行项目。使用swagger调用Login接口成功得到token,如下图

 六、得到token字符串后,我们在调用业务接口就必定要输入token令牌,平时我们只需要在Header中添加Authorization属性,但在Swagger作为接口文档时,我们需要在Program.cs添加以下代码。


builder.Services.AddSwaggerGen(options =>
{
#region 在接口中添加token
        options.OperationFilter<SecurityRequirementsOperationFilter>();
        //Token绑定到ConfigureServices
        options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
        {
            Description = "JWT授权(数据将在请求头中进行传输) 直接在下框中输入Bearer {token}(注意两者之间是一个空格)\"",
            Name = "Authorization",//jwt默认的参数名称
            In = ParameterLocation.Header,//jwt默认存放Authorization信息的位置(请求头中)
            Type = SecuritySchemeType.ApiKey
        });
}
//jwt授权验证
builder.Services.AddAuthorizationSetup();

//注意中间件的顺序,UseRouting放在最前边,UseAuthentication在UseAuthorization前边
app.UseRouting();

app.UseAuthentication();

app.UseAuthorization();

运行,就可以在 swagger/index.html 页面里看到这个Token入口了:

 七、JWT授权认证,新建AuthorizationSetup.cs,新建注册服务方法

using Common;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;

namespace MainProject.Setup
{
    /// <summary>
    /// 注册JWT服务方法
    /// </summary>
    public static class AuthorizationSetup
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="services"></param>
        /// <exception cref="ArgumentNullException"></exception>
        public static void AddAuthorizationSetup(this IServiceCollection services)
        {
            if (services == null) throw new ArgumentNullException(nameof(services));

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

            //读取配置文件
            var symmetricKeyAsBase64 = AppSetting.app(new string[] { "AppSettings", "JwtSetting", "SecretKey" });
            var keyByteArray = Encoding.ASCII.GetBytes(symmetricKeyAsBase64);
            var signingKey = new SymmetricSecurityKey(keyByteArray);
            var Issuer = AppSetting.app(new string[] { "AppSettings", "JwtSetting", "Issuer" });
            var Audience = AppSetting.app(new string[] { "AppSettings", "JwtSetting", "Audience" });

            // 令牌验证参数
            var tokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = signingKey,
                ValidateIssuer = true,
                ValidIssuer = Issuer,//发行人
                ValidateAudience = true,
                ValidAudience = Audience,//订阅人
                ValidateLifetime = true,
                ClockSkew = TimeSpan.FromSeconds(30),
                RequireExpirationTime = true,
            };

            //2.1【认证】、core自带官方JWT认证
            // 开启Bearer认证
            services.AddAuthentication("Bearer")
             // 添加JwtBearer服务
             .AddJwtBearer(o =>
             {
                 o.TokenValidationParameters = tokenValidationParameters;
                 o.Events = new JwtBearerEvents
                 {
                     OnAuthenticationFailed = context =>
                     {
                         // 如果过期,则把<是否过期>添加到,返回头信息中
                         if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
                         {
                             context.Response.Headers.Add("Token-Expired", "true");
                         }
                         return Task.CompletedTask;
                     }
                 };
             });
        }
    }
}

在Program.cs添加以下代码,在第六点代码中已写好,取消注释即可,注意位置

 //jwt授权验证
services.AddAuthorizationSetup();
app.UseRouting();
app.UseAuthentication();

八、测试,接口

        /// <summary>
        /// 需要Admin权限
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        [Authorize(Roles = "Admin")]
        public IActionResult Admin()
        {
            return Ok("hello admin");
        }


        /// <summary>
        /// 需要System权限
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        [Authorize(Roles = "System")]
        public IActionResult System()
        {
            return Ok("hello System");
        }

F5运行项目,获取一个Admin权限的token,并放到swagger 的权限验证按钮里面

 测试可知可以成功调取Admin,而System则会失败。

加入多角色怎么处理?我们把第七点注释代码取消注释,详情看代码,自行理解。即多角色访问,的代码如下图

上一章:二、读取appsettings.json配置_XiaoGuaiSs的博客-CSDN博客_读取appsettings.json一、什么是JWT根据维基百科定义,JWT(读作 [/dʒɒt/]),即JSON Web Tokens,是一种基于JSON的、用于在网络上声明某种主张的令牌(token)。一、创建项目,本章主要讲的是接口文档在线生成工具Swagger,需要创建WebAPI项目,点击下一步二、输入项目名称和选择项目存储位置,点击下一步三、取消勾选“配置HTTPS“项,勾选"启用OpenAPI支持"项,点击创建...一、使用Swagger(接口文档工具)_XiaoGuaiSs的博客-CSDN博客。六、运行测试结果如下。https://blog.csdn.net/m0_37894611/article/details/123526554?spm=1001.2014.3001.5501 下一章:四、仓储模式_XiaoGuaiSs的博客-CSDN博客_仓储模式一、什么是仓储(Respository)仓储(Respository)是存在于工作单元和数据库之间单独分离出来的一层,是对数据访问的封装。其优点:    1)业务层不需要知道它的具体实现,达到了分离关注点。    2)提高了对数据库访问的维护,对于仓储的改变并不会改变业务的逻辑,数据库可以用Sql Server(该系列博客使用)、MySql等。二、目录结构三、目录解析1、Common:公共层:用于封装一些常用公用方法2、Models数据层:用于...https://blog.csdn.net/m0_37894611/article/details/123636501

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值