……

.NET CORE 6 集成认证机制JWT

一:什么是JWT?

JWT (全称:Json Web Token)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为 JSON 对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。

 

二:JWT数据结构

头部

JWT 第一部分是头部分,它是一个描述 JWT 元数据的 Json 对象

    {
    "alg": "HS256",
    "typ": "JWT"
    }

alg 属性表示签名使用的算法,默认为 HMAC SHA256(写为HS256),typ 属性表示令牌的类型,JWT 令牌统一写为JWT。

最后,使用 Base64 URL 算法将上述 JSON 对象转换为字符串保存。

 

载荷

JWT 第二部分是 Payload,也是一个 Json 对象,除了包含需要传递的数据,还有七个默认的字段供选择。

  • iss (issuer):签发人/发行人

  • sub (subject):主题

  • aud (audience):用户

  • exp (expiration time):过期时间

  • nbf (Not Before):生效时间,在此之前是无效的

  • iat (Issued At):签发时间

  • jti (JWT ID):用于标识该 JWT

也可自定义字段

{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}

签名

JWT第三部分是签名(Signature)

签名部分是对前两部分(头部,载荷)的签名,防止数据篡改。

按下列步骤生成:
1、先指定密钥(secret)
2、把头部(header)和载荷(payload)信息分别base64转换
3、使用头部(header)指定的算法加密
最终,签名(signature) = HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload),secret)

算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用 "点"( . )分隔

 

三:JWT特点

  • JWT更加简洁
  • JWT适合一次性验证
  • JWT适合无状态认证
  • 相对于数据库Session查询更加省时
  • JWT默认不加密

JWT的最大缺点是服务器不保存会话状态,所以在使用期间不可能取消令牌或更改令牌的令牌的权限,一旦JWT签发,在有效期内将会一直有效

JWT本身包含认证信息,因此一旦信息泄露,任何人都可以获得令牌的所有权限。

为了减少盗用和窃取,JWT不建议使用HTTP协议来传输代码,而是使用加密的HTTPS协议进行传输。

 

 

四:JWT的使用

 

项目引用包:Microsoft.AspNetCore.Authentication.JwtBearer

 

 

配置文件中添加

  "JWTConfig": {
    "Secret": "abcdefghijklmnop", //密钥
    "Issuer": "Sw163.API", //颁发者
    "Audience": "Jst", //使用者
    "AccessExpiration": 30, //过期时间
    "RefreshExpiration": 60 //刷新过期时间
  },

在Token中添加JwtHelper类

 JwtHelper

using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;

namespace SW163.Token.Model
{
    /// <summary>
    /// 令牌
    /// </summary>
    public class JwtUserInfo
    {
        /// <summary>
        /// Id
        /// </summary>
        public long Uid { get; set; }
        /// <summary>
        /// 角色
        /// </summary>
        public string Role { get; set; }
        /// <summary>
        /// 职能
        /// </summary>
        public string Work { get; set; }

    }


/// <summary>
/// JWT操作帮助类
/// </summary>
public class JwtHelper
    {
        /// <summary>
        /// 颁发JWT
        /// </summary>
        /// <param name="tokenModel">当前颁发对象的用户信息</param>
        /// <returns>JWT字符串</returns>
        public static string IssueJwt(JwtUserInfo jwtUserInfo)
        {
            #region 【Step1-从配置文件中获取生成JWT所需要的数据】
            string iss = Appsettings.GetVal(new string[] { "JWTConfig", "Issuer" });//颁发者
            string aud = Appsettings.GetVal(new string[] { "JWTConfig", "Audience" });//使用者
            string secret = Appsettings.GetVal(new string[] { "JWTConfig", "Secret" }); //密钥
            #endregion

            #region 【Step2-通过Claim创建JWT中的Payload(载荷)信息】

            var claimsIdentity = new List<Claim>
                {
                 new Claim(JwtRegisteredClaimNames.Jti, jwtUserInfo.Uid.ToString()), //JWT ID
                new Claim(JwtRegisteredClaimNames.Iat, $"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}"),//JWT的发布时间
                new Claim (JwtRegisteredClaimNames.Exp,$"{new DateTimeOffset(DateTime.Now.AddSeconds(600)).ToUnixTimeSeconds()}"),//JWT到期时间
                new Claim(JwtRegisteredClaimNames.Iss,iss), //颁发者
                new Claim(JwtRegisteredClaimNames.Aud,aud)//使用者
               };

            //添加用户的角色信息(非必须,可添加多个)
            var claimRoleList = jwtUserInfo.Role.Split(',').Select(role => new Claim(ClaimTypes.Role, role)).ToList();
            claimsIdentity.AddRange(claimRoleList);
            #endregion

            #region 【Step3-签名对象】

            var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret)); //创建密钥对象
            var sigCreds = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256); //创建密钥签名对象

            #endregion

            #region 【Step5-将JWT相关信息封装成对象】
            var jwt = new JwtSecurityToken(
              issuer: iss,
              claims: claimsIdentity,
              signingCredentials: sigCreds);
            #endregion

            #region 【Step6-将JWT信息对象生成字符串形式】
            var jwtHandler = new JwtSecurityTokenHandler();
            string token = jwtHandler.WriteToken(jwt);
            #endregion

            return token;
        } // END IssueJwt()

        /// <summary>
        /// 将JWT加密的字符串进行解析
        /// </summary>
        /// <param name="jwtStr">JWT加密的字符</param>
        /// <returns>JWT中的用户信息</returns>
        public static JwtUserInfo SerializeJwtStr(string jwtStr)
        {
            JwtUserInfo jwtUserInfo = new JwtUserInfo();
            var jwtHandler = new JwtSecurityTokenHandler();

            if (!string.IsNullOrEmpty(jwtStr) && jwtHandler.CanReadToken(jwtStr))
            {
                //将JWT字符读取到JWT对象
                JwtSecurityToken jwtToken = jwtHandler.ReadJwtToken(jwtStr);

                //获取JWT中的用户信息
                jwtUserInfo.Uid = Convert.ToInt64(jwtToken.Id);
                object role;
                jwtToken.Payload.TryGetValue(ClaimTypes.Role, out role); //获取角色信息
                jwtUserInfo.Role = role == null ? "" : role.ToString();
            }
            return jwtUserInfo;
        } //END SerializeJwt()

    }

}

Appsettings.cs

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SW163.Token
{
    /// <summary>
    /// 用于帮助读取appsettings.json中的系统配置参数
    /// </summary>
    public class Appsettings
    {
        static IConfiguration Configuration { get; set; }

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

        /// <summary>
        /// 获取用appsettings.json某个字段下的值
        /// </summary>
        /// <param name="sections">获取值所在的字段(基于JSON层次结构,某个值会存在于多个层级的字段中)</param>
        /// <returns>JSON字段的值</returns>
        public static string GetVal(params string[] sections)
        {
            try
            {
                if (sections.Any())
                {
                    string key = string.Join(":", sections);
                    return Configuration[key];
                }
            }
            catch (Exception) { }

            return string.Empty;
        } // END GetVal()

        /// <summary>
        /// 获取用appsettings.json某个字段下值(值是一个组数)
        /// </summary>
        /// <param name="sections">获取值所在的字段(基于JSON层次结构,某个值会存在于多个层级的字段中)</param>
        /// <returns>JSON字段的多个值(集合)</returns>
        public static List<T> GetValues<T>(params string[] sections)
        {
            List<T> list = new List<T>();
            Configuration.Bind(string.Join(":", sections), list);
            return list;
        } // END GetValues()
    }
}

Startup添加JWT认证的相关配置

ConfigureServices中添加

  services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "JST API", Version = "v1" });
                #region 
                var basePath = PlatformServices.Default.Application.ApplicationBasePath;
                var xmlPath = Path.Combine(basePath, "WebApi.xml");
                c.IncludeXmlComments(xmlPath, true);
                #endregion

                #region Token绑定到ConfigureServices
                // 开启小锁
                c.OperationFilter<AddResponseHeadersFilter>();
                c.OperationFilter<AppendAuthorizeToSummaryOperationFilter>();
                // 在header中添加token,传递到后台
                c.OperationFilter<SecurityRequirementsOperationFilter>();

                c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
                {
                    Description = "JWT授权(数据将在请求头中进行传输) 直接在下框中输入Bearer {token}(注意两者之间是一个空格)\"",
                    Name = "Authorization",//jwt默认的参数名称
                    In = ParameterLocation.Header,//jwt默认存放Authorization信息的位置(请求头中)
                    Type = SecuritySchemeType.ApiKey
                });
                #endregion
            });

            services.AddSingleton(new Appsettings(Configuration)); //将Appsettings对象以单例模式进行注入

Configure中添加

            //先开启认证
            app.UseAuthentication();

            //在开启授权
            app.UseAuthorization();

 

创建授权控制器验证

 

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using SW163.Domain;
using SW163.Domain.IRepository;
using SW163.Token.Model;
using SW163.WebApi.Controllers;
using SW163.Infrastructure;
using System.Threading.Tasks;
using System.Security.Claims;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using System.IdentityModel.Tokens.Jwt;
using System;
using System.ComponentModel;

namespace SW163.WebApi.Api.System
{
    /// <summary>
    /// 获取jwt令牌
    /// </summary>
    [Route("api/[controller]")]
    [Description("账户信息")]
    [ApiController]
    public class AccountController : ControllerBase
    {

        public AccountController(IAdministratorRepository administratorRepository)
        {

            AdministratorRepository = administratorRepository;
        }

        public IAdministratorRepository AdministratorRepository { get; }


        [HttpGet,Route("GetJwtStr")]
        public async Task<object> GetJwtStr(string UserName, string PassWord)
        {
            //验证登录有效性。。。。
            var pwd =DESEncrypt.Encrypt(PassWord);
            var admin = await AdministratorRepository.GetEntityAsync(x => x.UserName == UserName && x.PassWord == pwd);
            if (admin!=null)
            {
                //登陆成功后,基于当前用户生成JWT令牌字符串
                JwtUserInfo jwtUserInfo = new JwtUserInfo { Uid = 1, Role = "Admin,Leader" };
                string jwtStr = JwtHelper.IssueJwt(jwtUserInfo);
                return Ok(new { success = true, token = jwtStr, Id = admin.Id, RoleId = admin.RoleId });
            }
            return NotFound(new { success = false, token = "此账号不存在", Id = 0, RoleId = 0 });
        }

    }
}

 

运行结果

 

 

"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIxIiwiaWF0IjoiMTY4NDc0NDQzNCIsImV4cCI6IjE2ODQ3NDUwMzQiLCJpc3MiOiJTdzE2My5BUEkiLCJhdWQiOiJKc3QiLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOlsiQWRtaW4iLCJMZWFkZXIiXX0.eaQKe4kOdx5p2iSQV_Td_bqmX-vOdh6gb0y7mxPmkXM

 

posted @ 2023-05-22 16:36  蟾宝  阅读(1034)  评论(1编辑  收藏  举报