WebApi验证JWT爬坑记录

在VS2022环境下 做JWT验证,的记录。

一、建项目

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

提示:删掉自带的demo

二、JWT验权相关

2.1、nuGet 包

加载:Microsoft.AspNetCore.Authentication.JwtBearer
在这里插入图片描述在这里插入图片描述

2.2、JWT设置

改动:appsettings.json

在这里插入图片描述

 {
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
 "Jwt": {
  "SecretKey": "#&**……&%*&d@DC%TGN&UJ(OL)P:123u^Bdob@OJ&KF2RcAB%ybsoy&2#%^&*(*()))_+.-.apikey",
  "Issuer": "MyApi", //颁发者 
  "Audience": "WebAppAudience", //接收者
  "ExpireSeconds": 7200 //过期时间
 }
}

改动:Program.cs

#region JWT服务
// 注册JWT服务
builder.Services.AddSingleton(new JwtHelper(builder.Configuration));

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
{
    options.TokenValidationParameters = new TokenValidationParameters()
    {
        ValidateIssuer = true, //是否验证Issuer
        ValidIssuer = builder.Configuration["Jwt:Issuer"], //发行人Issuer
        ValidateAudience = false, //是否验证Audience      
        ValidateIssuerSigningKey = true, //是否验证SecurityKey
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:SecretKey"] ?? "")), //SecurityKey
        ValidateLifetime = true, //是否验证失效时间
        ClockSkew = TimeSpan.FromSeconds(30), //过期时间容错值,解决服务器端时间不同步问题(秒)
        RequireExpirationTime = true,
    };
}
);
#endregion

2.3、Common/JwtHelper.cs (JWT逻辑类)

在这里插入图片描述
添加:Common/JwtHelper.cs

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

namespace WebApi.Common
{
    /// <summary>
    /// 授权JWT类
    /// </summary>
    public class JwtHelper
    {
        private readonly IConfiguration _configuration;

        /// <summary>
        /// Token配置
        /// </summary>
        /// <param name="configuration"></param>
        public JwtHelper(IConfiguration configuration)
        {
            _configuration = configuration;
        }

        /// <summary>
        /// 创建Token 这里面可以保存自己想要的信息
        /// </summary>
        /// <param name="username"></param>
        /// <param name="mobile"></param>
        /// <returns></returns>
        public string CreateToken(string username, string mobile)
        {
            try
            {
                // 1. 定义需要使用到的Claims
                var claims = new[]
                {
                    new Claim("username", username),
                    new Claim("mobile", mobile),
                    /* 可以保存自己想要信息,传参进来即可
                    new Claim("sex", "sex"),
                    new Claim("limit", "limit"),
                    new Claim("head_url", "xxxxx")
                    */
                };

                // 2. 从 appsettings.json 中读取SecretKey
                var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:SecretKey"]));

                // 3. 选择加密算法
                var algorithm = SecurityAlgorithms.HmacSha256;

                // 4. 生成Credentials
                var signingCredentials = new SigningCredentials(secretKey, algorithm);
                int ExpireSeconds = int.Parse(_configuration["Jwt:ExpireSeconds"]);//生效时间
                // 5. 根据以上,生成token
                var jwtSecurityToken = new JwtSecurityToken(
                    _configuration["Jwt:Issuer"],     //颁发者 
                    _configuration["Jwt:ExpireSeconds"],  //生效时间
                    claims,                          //票据,
                    DateTime.Now,                    //当前时间
                    DateTime.Now.AddSeconds(ExpireSeconds),     //过期时间
                    signingCredentials               //Credentials
                );

                // 6. 将token变为string
                var token = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);

                return token;
            }
            catch (Exception)
            {
                throw;
            }
        }
        /// <summary>
        /// 解密jwt
        /// </summary>
        /// <param name="jwt"></param>
        /// <returns></returns>
        public string JwtDecrypt(string jwt)
        {

            StringBuilder sb = new StringBuilder();
            try
            {
                JwtSecurityTokenHandler tokenHandler = new();
                TokenValidationParameters valParam = new();
                var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:SecretKey"]));
                valParam.IssuerSigningKey = securityKey;
                valParam.ValidateIssuer = false;
                valParam.ValidateAudience = false;
                //解密
                ClaimsPrincipal claimsPrincipal = tokenHandler.ValidateToken(jwt,
                        valParam, out SecurityToken secToken);
                foreach (var claim in claimsPrincipal.Claims)
                {
                    sb.Append($"{claim.Type}={claim.Value};");
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
            return sb.ToString();
        }
    }

}


2.4、实体类Model/UserInfo.cs

在这里插入图片描述
改动:Model/UserInfo.cs

using System.ComponentModel.DataAnnotations;

namespace WebApi.Models
{
    public class UserInfo
    {
        /// <summary>
        /// 其中 [Required] 表示非空判断
        /// </summary>
        [Required]
        public string ? UserName { get; set; }
        [Required]
        public string ? Password { get; set; }
        [Required]
        public string ? PhoneNumber { get; set; }

    }
}

2.5、控制器Controllers/UserInfoController.cs

在这里插入图片描述
添加:Controllers/UserInfoController.cs

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using WebApi.Common;
using WebApi.Models;

namespace WebApi.Controllers
{
    [Route("[controller]/[action]")]
    [ApiController]
    public class UserInfoController : ControllerBase
    {
        private readonly JwtHelper _jwt;
        /// <summary>
        /// 初始化
        /// </summary>
        /// <param name="jwtHelper"></param>
        /// <param name="redis"></param>
        public UserInfoController(JwtHelper jwtHelper)
        {
            _jwt = jwtHelper;
        }
        /// <summary>
        /// 获取Token
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        public IActionResult GetToken(UserInfo user)
        {
            //参数验证等等....
            if (string.IsNullOrEmpty(user.UserName))
            {
                return Ok("参数异常!");
            }
            //获取Token
            var token = _jwt.CreateToken(user.UserName, user.PhoneNumber ?? "");
            //解密后的Token
           
            return Ok(token);
        }
        /// <summary>
        /// 获取自己的详细信息,其中 [Authorize] 就表示要带Token才行
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        [Authorize]
        public IActionResult GetSelfInfo()
        {
            //解析token
            string ? token = this.Request.Headers["Authorization"];
            string _token = token.Replace("Bearer ", "");
            string PWToken = _jwt.JwtDecrypt(_token);
            //解析出用户名,redis里获取用户信息,并返回
            return Ok("授权通过了!");
        }
    }
}

2.6、运行 F5

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

三、Redis使用

3.1、nuGet 支持包,安装小皮(phpstudy)

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

3.2、Redis引入设置

修改:appsettings.json

"Redis": {
  "Default": {
    "Connection": "127.0.0.1:6379,password=d0d493hGlqePd2Tn", //redis连接地址,端口号,密码
    "InstanceName": "local", //实例名
    "DefaultDB": "8" //Db8数据库
  }
}

修改:Program.cs

#region 配置Redis 
// 配置Redis   注入类似于配置数据库连接字符串
var section = builder.Configuration.GetSection("Redis:Default");
// 连接字符串
string? _connectionString = section.GetSection("Connection").Value;
// 实例名称
string? _instanceName = section.GetSection("InstanceName").Value;
// 默认数据库 
int _defaultDB = int.Parse(section.GetSection("DefaultDB").Value ?? "0");
builder.Services.AddSingleton(new RedisHelper(_connectionString ?? "", _instanceName ?? "", _defaultDB));
#endregion

添加:Common/RedisHelper.cs

using Microsoft.AspNetCore.Hosting.Server;
using StackExchange.Redis;
using System.Collections.Concurrent;

namespace WebApi.Common
{
    /// <summary>
    /// redis类
    /// </summary>
    public class RedisHelper : IDisposable
    {
        //连接字符串
        private string _connectionString;
        //实例名称
        private string _instanceName;
        //默认数据库
        private int _defaultDB;

        private ConcurrentDictionary<string, ConnectionMultiplexer> _connections;
        /// <summary>
        /// 初始化
        /// </summary>
        /// <param name="connectionString"></param>
        /// <param name="instanceName"></param>
        /// <param name="defaultDB"></param>
        public RedisHelper(string connectionString, string instanceName, int defaultDB = 0)
        {
            _connectionString = connectionString;
            _instanceName = instanceName;
            _defaultDB = defaultDB;
            _connections = new ConcurrentDictionary<string, ConnectionMultiplexer>();
        }

        /// <summary>
        /// 获取ConnectionMultiplexer
        /// </summary>
        /// <returns></returns>
        private ConnectionMultiplexer GetConnect()
        {
            return _connections.GetOrAdd(_instanceName, p => ConnectionMultiplexer.Connect(_connectionString));
        }

        /// <summary>
        /// 获取redis数据库
        /// </summary>
        /// <returns></returns>
        public IDatabase GetDatabase()
        {
            return GetConnect().GetDatabase(_defaultDB);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="configName"></param>
        /// <param name="endPointsIndex"></param>
        /// <returns></returns>
        public StackExchange.Redis.IServer GetServer(string? configName = null, int endPointsIndex = 0)
        {
            var confOption = ConfigurationOptions.Parse(_connectionString);
            return GetConnect().GetServer(confOption.EndPoints[endPointsIndex]);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="configName"></param>
        /// <returns></returns>
        public ISubscriber GetSubscriber(string? configName = null)
        {
            return GetConnect().GetSubscriber();
        }
        /// <summary>
        /// 
        /// </summary>
        public void Dispose()
        {
            if (_connections != null && _connections.Count > 0)
            {
                foreach (var item in _connections.Values)
                {
                    item.Close();
                }
            }
        }
    }
}

3.3、Redis应用

Controllers/UserInfoControl.cs

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using WebApi.Common;
using WebApi.Models;

namespace WebApi.Controllers
{
    [Route("[controller]/[action]")]
    [ApiController]
    public class UserInfoController : ControllerBase
    {
        private readonly JwtHelper _jwt;

        private readonly RedisHelper _redis;
        /// <summary>
        /// 初始化
        /// </summary>
        /// <param name="jwtHelper"></param>
        /// <param name="redis"></param>
        public UserInfoController(JwtHelper jwtHelper, RedisHelper redis)
        {
            _jwt = jwtHelper;
            _redis = redis;
        }
        /// <summary>
        /// 获取Token
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        public IActionResult GetToken(UserInfo user)
        {
            //参数验证等等....
            if (string.IsNullOrEmpty(user.UserName))
            {
                return Ok("参数异常!");
            }

            #region Redis缓存验证
            bool ishave = _redis.GetDatabase().KeyExists(user.UserName);//查存
            if (ishave)//验证
            {
                string? Password = _redis.GetDatabase().StringGet(user.UserName);// 从Redis里面取数据
                if (user.Password != Password) {
                    return Ok("密码错误!");
                }
            }
            else//查询后写入
            {
                _redis.GetDatabase().StringSet(user.UserName, user.Password, TimeSpan.FromSeconds(7200));// 往Redis里面存入数据
            }
            #endregion Redis缓存验证 end

            //获取Token
            var token = _jwt.CreateToken(user.UserName, user.PhoneNumber ?? "");
            //解密后的Token
           
            return Ok(token);
        }
        /// <summary>
        /// 获取自己的详细信息,其中 [Authorize] 就表示要带Token才行
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        [Authorize]
        public IActionResult GetSelfInfo()
        {
            //解析token
            string ? token = this.Request.Headers["Authorization"];
            string _token = token.Replace("Bearer ", "");
            string PWToken = _jwt.JwtDecrypt(_token);
            //解析出用户名,redis里获取用户信息,并返回
            return Ok("授权通过了!");
        }
    }
}

Redis缓存了用户信息,不需要重复访问数据库

四、Swagger添加JWT验证

4.1、Swagger 设置 JWT验证

Program.cs

# region swagger里添加JWT授权
builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "Web API", Version = "v1" });
    //开启注释
    var xmlFile = $"{Assembly.GetEntryAssembly().GetName().Name}.xml";
    var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
    c.IncludeXmlComments(xmlPath, true);
    // 配置 JWT Bearer 授权
    c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
    {
        Description = "JWT Authorization header using the Bearer scheme",
        Name = "Authorization",
        In = ParameterLocation.Header,
        Type = SecuritySchemeType.Http,
        Scheme = "bearer"
    });

    var securityScheme = new OpenApiSecurityScheme
    {
        Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" }
    };
    var securityRequirement = new OpenApiSecurityRequirement { { securityScheme, new string[] { } } };
    c.AddSecurityRequirement(securityRequirement);
});
#endregion

4.2、异常:注释未开处理

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

提示:右键项目 -> 属性

在这里插入图片描述

4.3、测试

在这里插入图片描述

4.31、贴入生成的token

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

4.32、测试成功

在这里插入图片描述

五、总结

[Authorize] 封装了验证逻辑,没必要深究
数据接口验权,无外乎就是登录的时候生成 Token , 客户端用 Cookie 或 localStorage 缓存
使用时写入headers , 如:

axios.get(url, {
  headers: {
    'Authorization': 'Bearer ' + token
  }
})

//index.js
Vue.http.interceptors.push((request, next) => {
    //请求发送前的处理逻辑
    request.headers.set('Authorization', 'Bearer ' + token)
    next((response) => {
        //请求发送后的处理逻辑
        //根据请求的状态,response参数返回给successCallback或errorCallback
        return response
    })
})

参考:

C# ASP.NET Core Web API 身份授权(JWT)验证(一)
Swagger添加JWT验证(ASP.NET)

项目代码

Demo下载:https://download.csdn.net/download/yqsy123123/89571832

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值