ASP.Net Core实战——身份认证(JWT鉴权)

关于鉴权认证


什么是鉴权认证

鉴权(authentication)是指验证用户是否拥有访问系统的权利。传统的鉴权是通过密码来验证的。这种方式的前提是,每个获得密码的用户都已经被授权。在建立用户时,就为此用户分配一个密码,用户的密码可以由管理员指定,也可以由用户自行申请。这种方式的弱点十分明显:一旦密码被偷或用户遗失密码,情况就会十分麻烦,需要管理员对用户密码进行重新修改,而修改密码之前还要人工验证用户的合法身份。
为了克服这种鉴权方式的缺点,需要一个更加可靠的鉴权方式。目前的主流鉴权方式是利用认证授权来验证数字签名的正确与否。

常见鉴权方式

Session机制 Session 是一种HTTP存储机制,Session对象存储特定用户会话所需的属性及配置信息,目的是为无状态的HTTP提供的持久机制,把 User 信息存储到 Session 里。可以理解为一张短期通行证,给请求用户颁发短期证件,每次门岗检查短期卡号在备案名册中,允许通行,因为 SID 的不可预测性,暂且认为是安全的。

JWT机制
JWT即JSON Web Tokens,是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519),他可以用来安全的传递信息,因为传递的信息是经过加密算法加密过得。相当于一张长期通行证,给请求用户颁发长期证件,每次检查证件人信息,正确允许通行。

OAuth2机制 OAuth的英文全称是Open Authorization,它是一种开放授权协议,由认证机构分别为服务与用户颁发凭证,用户授权服务使用凭证,接着服务使用此凭证去认证合法性,最终得到目标数据,如同调兵遣将时使用的两片虎符,由皇庭下发授权。

关于JWT认证


JWT认证流程

在这里插入图片描述

通过上图,我们可以看到它的授权流程

  1. 客户端通过请求服务端登录认证接口
  2. 服务端用秘密创建JWT
  3. 服务端将JWT返回浏览器
  4. 客户端在授权报头上发送JWT
  5. 服务端检查JWT签名从JWT获取用户信息
  6. 服务端向客户端发送响应

JWT组成

官网 https://jwt.io/
JSON Web Tokens(JWT)有三部分构成:Header、Payload、VERIFY SIGNATURE,用英文句点分割(.) ,一般看起来例如:aaaaaaa.bbbbbbb.ccccccc。

在这里插入图片描述

Header 头信息,通常包含两部分:

  • type: 代表token的类型,这里使用的是JWT类型。
  • alg: 代表使用的算法,例如HMAC SHA256或RSA.

Payload 荷载信息,它包含一些声明Claim(实体的描述,例:用户信息和其他的一些元数据),声明分三类:

  • Reserved Claims,这是一套预定义的声明,并不是必须的,这是一套易于使用、操作性强的声明。包括:iss(issuer)、exp(expiration time)、sub(subject)、aud(audience)等
  • Plubic Claims,
  • Private Claims,交换信息的双方自定义的声明

VERIFY SIGNATURE 由头信息+荷载信息+密钥 组合之后进行加密得到,如使用的是HMAC SHA256算法,大致流程类似于: HMACSHA256( base64UrlEncode(header) + “.” + base64UrlEncode(payload), secret)。VERIFY SIGNATURE字段被用来确认JWT信息的发送者是谁,并保证信息没有被修改。

JWT实践


想要实现JWT认证,需要做好两方面,Token令牌的签发与真实性验证。 首先准备一个用户信息对象(存放从数据库中查询到的相关信息)。
    public class Tokens
    {
    
    
        /// <summary>
        /// ID
        /// </summary>
        public string ID{
    
     get; set; }
        /// <summary>
        /// 名称
        /// </summary>
        public string Login_name {
    
     get; set; }
        /// <summary>
        /// 手机
        /// </summary>
        public string Phone {
    
     get; set; }
        /// <summary>
        /// 邮箱
        /// </summary>
        public string Email {
    
     get; set; }
        /// <summary>
        /// 身份
        /// </summary>
        public string Sub {
    
     get; set; }
    }

接下来,使用以上部分信息生成Token

        // 密钥,注意不能太短(这个是我在网上随便生成的)
        public static string secretKey {
    
     get; set; } = @"MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAMniN23tMkxBh6j3vCQ5c4JqsZ8p8NqNqeCjNQPDga9RtIFCt3/5n4aIyUaA0ONVMOHEt33g8EoayVZMKQGf4aPjConiAXYUYy540fs8r9l0oJyHWx+5at0nkKFfTtpgppXQRjWiWyeOq3RX9jhsdFKBPqbugZW4yjlt1QlSWaQtAgMBAAECgYEAm9vNt0w8XKrqtQQteDnyd2kvoBWdIN3lnMvjjfhOErAdjv2W9XIeOps36PpiSl/m0SYyEzipykxLzBgYQGzSoXgZwp91lmUKxVpCi6QgZU+VCR5q+Tr4BuLmi7GsJE0EwcpdC6jnOwbZXdhClLaQgRiiLPbiLw+CADbiHQqaRoECQQD0sE6VOqPW2eqPHH6mqaqy+owU0UiBCkW8xfBKqR74ufgnCveDLrfE+vxIqVBU1HN+/LqKZ1rPEkqPdjAUJk3pAkEA0zdYs12mu+as3araCPrnAppsZRRGizVKmKVB074W63NZ7SiXsEqDBj3fp1oeGo2OiKWCnjEmmtzpIvfVbzjlpQJBAOTQPhwUgwO2Mj7z0Ye+n47Q9s+8yYVJ+t7FZqgasIi9N04jVkPJGzZL0kGMez9okDyOz03/yo5bN3gieGFPVIkCQQCAMt2xsKwc7HwL50GDpdZFLDmSeGYA5I2sbNGxlXUP3+m7GqJHLFKunSt8xiPzdewHYH4RSj/mSyNuSALSCTTJAkBBN7BWA39VyvJDALVYqHDKNx0r5BwSTVk8wJvn7KKzB1YdVZrl/pIIi+uPbZwBJA80QXJu4sjCyc5XBzgQdsiv";
        
        // 生成JWT字符串
        public static string GetJWT(Tokens tokenModel)
        {
    
    
            var claims = new List<Claim>
            {
    
    
                new Claim(JwtRegisteredClaimNames.Jti,tokenModel.Login_name),
                // 令牌颁发时间
                new Claim(JwtRegisteredClaimNames.Iat, $"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}"),
                new Claim(JwtRegisteredClaimNames.Nbf,$"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}"),
                 // 过期时间 100秒
                new Claim(JwtRegisteredClaimNames.Exp,$"{new DateTimeOffset(DateTime.Now.AddSeconds(100)).ToUnixTimeSeconds()}"),
                new Claim(JwtRegisteredClaimNames.Iss,"API"), // 签发者
                new Claim(JwtRegisteredClaimNames.Aud,"User") // 接收者
            };
            // 密钥
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey));
            var creds = new SigningCredentials(key, SecurityAlgorithms.RsaSha512);

            JwtSecurityToken jwt = new JwtSecurityToken(
                claims: claims,// 声明的集合
                //expires: .AddSeconds(36), // token的有效时间
                signingCredentials: creds
                );
            var handler = new JwtSecurityTokenHandler();
            // 生成 jwt字符串
            var strJWT = handler.WriteToken(jwt);
            return strJWT;
        }

在这里会需要引用几个包:

  • Microsoft.IdentityModel.Tokens;
  • System.IdentityModel.Tokens.Jwt;
  • System.Security.Claims;

在Startup 注册Authentication服务与中间件
服务

            //添加jwt验证:
            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddJwtBearer(options =>
                {
    
    
                    options.TokenValidationParameters = new TokenValidationParameters
                    {
    
    
                        ValidateIssuer = true,//是否验证Issuer
                        ValidateAudience = true,//是否验证Audience
                        ValidateLifetime = true,//是否验证失效时间
                        ValidateIssuerSigningKey = true,//是否验证SecurityKey
                        ValidAudience = "User",//Audience
                        ValidIssuer = "API",//Issuer,这两项和后面签发jwt的设置一致
                        ClockSkew = TimeSpan.Zero, // // 默认允许 300s  的时间偏移量,设置为0
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(TokenHelper.secretKey))//拿到SecurityKey
                    };
                });

中间件

			app.UseAuthentication();

在这里需要注意签发者与接受者的对应
在这里插入图片描述
然后在Controller上加上关键代码,这样对应该路由的请求都会进行Authentication,当你的都头文件包含正确的Token就可以正常返回。
在这里插入图片描述
最后,在Swagger中试验一下
首先修改Swagger的 options 内容
在这里插入图片描述

				//开启权限小锁
                options.OperationFilter<AddResponseHeadersFilter>();
                options.OperationFilter<AppendAuthorizeToSummaryOperationFilter>();

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

运行,我们可以在SwaggerUI页面看到多出来一个小锁Authorize
在这里插入图片描述
当请求没有携带Token,返回错误信息
在这里插入图片描述
先请求Token
在这里插入图片描述
点击上面的小锁Authorize,填入刚刚获取的Token信息(依据提示,在前面加上 Bearer+space
在这里插入图片描述
然后就可以看到接口标签上的小锁是认证状态了,再次请求接口,就能正确获取数据。
在这里插入图片描述


仅仅简单的使用JWT鉴权认证,有更好的想法欢迎留言指导

猜你喜欢

转载自blog.csdn.net/u010796249/article/details/109487453