前言
JWT (JSON Web Token) 是一种安全传输信息的开放标准,由Header、Payload和Signature三部分组成。它主要用于身份验证、信息交换和授权。JWT可验证用户身份,确保访问权限,实现单点登录,并在客户端和服务器之间安全地交换信息。因其简单、安全和便捷,JWT在现代Web应用中广泛使用。
用JWT有多个原因:
-
无状态的身份验证:JWT允许服务器无需保存用户的会话信息,因为所有必要的信息都存储在令牌本身中。这降低了服务器的存储需求,并提高了系统的可扩展性。
-
跨域身份验证:由于JWT是自我包含的,并且可以被轻松传递,因此它非常适合跨域身份验证。用户可以在一个服务上验证身份,然后使用相同的令牌访问另一个服务,从而实现单点登录(SSO)。
-
安全性:JWT可以通过使用强大的加密算法(如HS256, RS256等)进行签名,以确保其完整性和真实性。服务器可以使用公钥验证令牌的签名,从而确保它没有被篡改,并且确实是由受信任的颁发者签发的。
-
减少数据库查询:由于用户的身份信息都存储在JWT中,服务器无需每次都去数据库中查询用户的身份信息,从而减少了数据库的压力和查询时间。
-
可定制性:JWT的Payload部分可以包含自定义的信息,如用户角色、权限等,这使得JWT非常灵活,并可以根据具体需求进行定制。
-
易于分发和共享:JWT可以轻松地通过网络传输,并且可以在多个服务和客户端之间共享,这使得它在微服务架构和分布式系统中非常有用。
-
标准化和互操作性:JWT是一个开放标准(RFC 7519),这意味着不同的系统和语言都可以使用相同的方式生成和验证JWT,从而提高了系统的互操作性。
环境 Win10 VS2022 .NET8
✨ 建立项目jwttest
1.创建TestJwtController
2.下载JWT
3.建实体类
/// <summary>
/// 用户信息类
/// </summary>
public class LoginRs
{
/// <summary>
/// 用户ID
/// </summary>
public string UserId { get; set; }
/// <summary>
/// 用户密码
/// </summary>
public string PasswordMD5 { get; set; }
}
/// <summary>
/// 用户登录信息类
/// </summary>
public class LoginInfo
{
/// <summary>
/// 用户信息
/// </summary>
public string UserId { get; set; }
/// <summary>
/// 检验时间
/// </summary>
public DateTime Expires { get; set; }
}
/// <summary>
/// rsmodel
/// </summary>
public class RsModel
{
/// <summary>
/// 是否成功
/// </summary>
public bool isOk { get; set; }
/// <summary>
/// 返回值
/// </summary>
public int code { get; set; }
/// <summary>
/// 返回消息
/// </summary>
public string msg { get; set; }
/// <summary>
/// 返回数据
/// </summary>
public object rsData { get; set; }
}
4.添加post login
// POST api/<ValuesController>
[HttpPost]
public string Login([FromBody] LoginRs loginRequest)
{
if (loginRequest == null) return JsonConvert.SerializeObject(new RsModel() { code = 0, isOk = false, msg = "登录信息为空!" });
#region 判断userid pwd
if (loginRequest.UserId != "admin" || loginRequest.PasswordMD5 != "admin")
{
return JsonConvert.SerializeObject(new RsModel() { code = 0, isOk = false, msg = "用户名和密码不正确!" });
}
#endregion
LoginInfo Info = new LoginInfo()
{
UserId = loginRequest.UserId,
Expires = DateTime.Now.AddDays(1)
};
const string secretKey = "myseckey";//口令加密秘钥
byte[] key = Encoding.UTF8.GetBytes(secretKey);
IJwtAlgorithm algorithm = new HMACSHA256Algorithm();//加密方式
IJsonSerializer serializer = new JsonNetSerializer();//序列化Json
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();//base64加解密
IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder);//JWT编码
var token = encoder.Encode(Info, key);//生成令牌
return JsonConvert.SerializeObject(new RsModel() { code = 1, isOk = true, rsData = token, msg = "登录成功!" });
}
5.登录验证
这里使用swagger方便检验 👉 .NET MVC API Swagger入坑
6.测试没问题,写个JwtHelper
public static class JwtHelper
{
private static readonly string JwtKey = "mysecret";
/// <summary>
/// 获取加密解密
/// </summary>
/// <returns></returns>
private static IJwtEncoder GetEncoder()
{
IJwtAlgorithm algorithm = new HMACSHA256Algorithm();//加密方式
IJsonSerializer serializer = new JsonNetSerializer();//序列化Json
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();//base64加解密
IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder);//JWT编码
return encoder;
}
/// <summary>
/// 获取解密密钥
/// </summary>
/// <returns></returns>
private static IJwtDecoder GetDecoder()
{
IJsonSerializer serializer = new JsonNetSerializer();
IDateTimeProvider provider = new UtcDateTimeProvider();
IJwtValidator validator = new JwtValidator(serializer, provider);
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
IJwtAlgorithm algorithm = new HMACSHA256Algorithm();
IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder, algorithm);
return decoder;
}
/// <summary>
/// 加密
/// </summary>
public static string Encode(object payload)
{
var encoder = GetEncoder();
var token = encoder.Encode(payload, JwtKey);
return token;
}
/// <summary>
/// 解密
/// </summary>
public static T Decode<T>(string token)
{
var decoder = GetDecoder();
var data = decoder.Decode(token, JwtKey);
var res = JsonConvert.DeserializeObject<T>(data);
return res;
}
/// <summary>
/// 解密,只返回Json文本
/// </summary>
/// <param name="token"></param>
/// <returns></returns>
public static string Decode(string token)
{
var decoder = GetDecoder();
var data = decoder.Decode(token, JwtKey);
return data;
}
}
把中间的加密算法替换成helper的Encode
var token = JwtHelper.Encode(Info);
7.添加token加密类
调用Decode方法 解密token
8.测试JWT
{"isOk":true,"code":1,"msg":"登录成功!","rsData":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJVc2VySWQiOiJhZG1pbiIsIkV4cGlyZXMiOiIyMDI0LTA0LTEwVDAxOjUxOjUwLjk5NDgxNzQrMDA6MDAifQ.eYJovquJFezVhfdLp-Hro2vnMoQsUwgXLkYcZSYEy7U"}
测试解密
解密成功
⭐️JwtBearer
9、添加NuGet包Microsoft.AspNetCore.Authentication.JwtBearer
10、在appsettings.json
中添加JWT配置节点
"JWT": {
"SecKey": "im6666666!#@$%@%^^&*(~Czmjklneafguvioszb%yuv&*6WVDf5dw#5dfw6f5w6faW%FW^f5wa65f^AWf56", //密钥
"Issuer": "im666", //发行者
"ExpireSeconds": 7200 //过期时间 2h
},
11.添加jwt类
using Microsoft.IdentityModel.Tokens;
using System.Diagnostics;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
namespace webapijwttest.Models
{
/// <summary>
/// 授权JWT类
/// </summary>
public class JwtHelper2
{
private readonly IConfiguration _configuration;
/// <summary>
/// Token配置
/// </summary>
/// <param name="configuration"></param>
public JwtHelper2(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:SecKey"]));
// 3. 选择加密算法
var algorithm = SecurityAlgorithms.HmacSha256;
// 4. 生成Credentials
var signingCredentials = new SigningCredentials(secretKey, algorithm);
// 5. 根据以上,生成token
var jwtSecurityToken = new JwtSecurityToken(
_configuration["Jwt:Issuer"], //Issuer
_configuration["Jwt:ExpireSeconds"], //ExpireSeconds
claims, //Claims,
DateTime.Now, //notBefore
DateTime.Now.AddSeconds(30), //expires
signingCredentials //Credentials
);
// 6. 将token变为string
var token = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);
return token;
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// 获取信息
/// </summary>
/// <param name="jwt"></param>
/// <returns></returns>
public static string ReaderToken(string jwt)
{
var str = string.Empty;
try
{
//获取Token的三种方式
//第一种直接用JwtSecurityTokenHandler提供的read方法
var jwtHander = new JwtSecurityTokenHandler();
JwtSecurityToken jwtSecurityToken = jwtHander.ReadJwtToken(jwt);
str = jwtSecurityToken.ToString();
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
return str;
}
/// <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:SecKey"]));
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();
}
}
}
12.Program.cs注册JWT服务
#region JWT服务
// 注册JWT服务
builder.Services.AddSingleton(new JwtHelper2(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:SecKey"])), //SecurityKey
ValidateLifetime = true, //是否验证失效时间
ClockSkew = TimeSpan.FromSeconds(30), //过期时间容错值,解决服务器端时间不同步问题(秒)
RequireExpirationTime = true,
};
}
);
#endregion
添加swagger authorization
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);//需要 生成 目录生成XML
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);
});
var app = builder.Build();
//启用验证中间件
app.UseAuthentication();
app.UseAuthorization();
13.添加jwt测试api
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity.Data;
using Microsoft.AspNetCore.Mvc;
using webapijwttest.Models;
// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
namespace webapijwttest.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class Jwt2Controller : ControllerBase
{
private readonly JwtHelper2 _jwt;
/// <summary>
/// 初始化
/// </summary>
/// <param name="jwtHelper"></param>
public Jwt2Controller(JwtHelper2 jwtHelper)
{
_jwt = jwtHelper;
}
/// <summary>
/// 获取Token
/// </summary>
/// <returns></returns>
[HttpPost]
public IActionResult GetToken(LoginRs user)
{
//参数验证等等....
if (string.IsNullOrEmpty(user.UserId))
{
return Ok("参数异常!");
}
//这里可以连接mysql数据库做账号密码验证
//这里可以做Redis缓存验证等等
//这里获取Token,当然,这里也可以选择传结构体过去
var token = _jwt.CreateToken(user.UserId, user.PasswordMD5);
//解密后的Token
var PWToken = _jwt.JwtDecrypt(token);
return Ok(token + "解密后:" + PWToken);
}
/// <summary>
/// 获取自己的详细信息,其中 [Authorize] 就表示要带Token才行
/// </summary>
/// <returns></returns>
[HttpPost]
[Authorize]
public IActionResult GetSelfInfo()
{
//执行到这里,就表示已经验证授权通过了
/*
* 这里返回个人信息有两种方式
* 第一种:从Header中的Token信息反向解析出用户账号,再从数据库中查找返回
* 第二种:从Header中的Token信息反向解析出用户账号信息直接返回,当然,在前面创建 Token时,要保存进使用到的Claims中。
*/
return Ok("授权通过了!");
}
}
}
调用
检测控制器
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using webapijwttest.Models;
// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
namespace webapijwttest.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class AuthController : ControllerBase
{
private ILogger<AuthController> _logger = null;
private JwtHelper2 _iJWTService = null;
private readonly IConfiguration _configuration;
public AuthController(ILogger<AuthController> logger, JwtHelper2 jWTService, IConfiguration configuration)
{
this._logger = logger;
_iJWTService = jWTService;
_configuration = configuration;
}
[Route("Get")]
[HttpGet]
public IEnumerable<int> Get()
{//未加授权认证
return new List<int>() { 1, 3, 5, 7, 9 };
}
[Route("GetData")]
[HttpGet]
[Authorize]
public List<object> GetData()
{//添加了授权认证,需要使用token
return new List<object>() { new { userName = "123", remark = "1234" } };
}
[Route("Login")]
[HttpGet]
public string Login(string name, string password)
{
if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(password))
{
string token = this._iJWTService.CreateToken(name,password);
return JsonConvert.SerializeObject(new { result = true, token });
}
else
{
return JsonConvert.SerializeObject(new { result = false, token = "" });
}
}
}
}
调用
把token放进
测试GetData
当超过时间调用则GetData失败
END🐟🐟🐟