1、JWT定义
JWT(Json Web Token)是一种用于双方之间传递安全信息的简洁的、URL安全的表述性声明规范。JWT作为一个开放的标准( RFC 7519 ),定义了一种简洁的,自包含的方法用于通信双方之间以Json
对象的形式安全的传递信息。因为数字签名的存在,这些信息是可信的,JWT可以使用HMAC算法或者是RSA的公私秘钥对进行签名。
1.1、JWT结构
JWT由三部分组成,这些部分由点(.)分隔,分别是:Header(头部包含算法信息) .Payload(负载包含实际传递信息).Signature(对前面两部分的签名)
例如:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
2、JWT使用
生成JWT
public static string CreateNewJwt()
{
var claims = new List<Claim>();
//添加负载
claims.Add(new Claim(ClaimTypes.NameIdentifier, "6"));
claims.Add(new Claim(ClaimTypes.Name, "Panda"));
claims.Add(new Claim(ClaimTypes.Role, "User"));
claims.Add(new Claim(ClaimTypes.Role, "Manager"));
claims.Add(new Claim(ClaimTypes.Role, "Admin"));
claims.Add(new Claim("SomeCode", "Panda666com"));
//密钥
string key = "dkdls()dlfj%sldfj#sdlfnbmlkepkkqaopxd@9094";
//设置过期时间
DateTime expires = DateTime.Now.AddDays(1);
byte[] secBytes = Encoding.UTF8.GetBytes(key);
var secKey = new SymmetricSecurityKey(secBytes);
var credentials = new SigningCredentials(secKey, SecurityAlgorithms.HmacSha256Signature);
var tokenDescriptor = new JwtSecurityToken(claims: claims,
expires: expires, signingCredentials: credentials);
//生成jwt字符串
string jwt = new JwtSecurityTokenHandler().WriteToken(tokenDescriptor);
return jwt;
}
解码JWT
//不使用私钥,直接解码
public static string DecodeJwt(string jwtString)
{
string jwt = jwtString;
string[] segments = jwt.Split('.');
string head = JwtDecode(segments[0]);
string payload = JwtDecode(segments[1]);
Console.WriteLine("--------head--------");
Console.WriteLine(head);
Console.WriteLine("--------payload--------");
Console.WriteLine(payload);
string JwtDecode(string s)
{
s = s.Replace('-', '+').Replace('_', '/');
switch (s.Length % 4)
{
case 2:
s += "==";
break;
case 3:
s += "=";
break;
}
var bytes = Convert.FromBase64String(s);
return Encoding.UTF8.GetString(bytes);
}
return "";
}
由此可见,JWT中payload是明文保存的,不要把不能被客户端知道的信息放到JWT中。
私钥校验JWT并解码
/// <summary>
/// 验证Jwt字符串
/// </summary>
/// <param name="jwtString"></param>
public static Dictionary<string, string> ValidJwt(string jwtString)
{
Console.WriteLine("校验JWT");
string secKey = "dkdls()dlfj%sldfj#sdlfnbmlkepkkqaopxd@9094";
JwtSecurityTokenHandler tokenHandler = new();
TokenValidationParameters valParam = new();
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secKey));
valParam.IssuerSigningKey = securityKey;
valParam.ValidateIssuer = false;
valParam.ValidateAudience = false;
//返回值
Dictionary<string, string> result = new Dictionary<string, string>();
try
{
//解析Jwt
ClaimsPrincipal claimsPrincipal = tokenHandler.ValidateToken(jwtString,
valParam, out SecurityToken secToken);
foreach (var claim in claimsPrincipal.Claims)
{
result[claim.Type] = claim.Value;
Console.WriteLine($"{claim.Type}={claim.Value}");
}
}
catch (Exception ex)
{
Console.WriteLine("failed");
}
finally
{
}
return result;
}
测试:
public static void Main(string[] args)
{
//创建新的Jwt
string jwtEncodeString = CreateNewJwt();
Console.WriteLine(jwtEncodeString);
//读取Jwt
string jwtDecodeString = DecodeJwt(jwtEncodeString);
//验证Jwt
Dictionary<string, string> result = ValidJwt(jwtEncodeString);
}
结果
eyJhbGciOiJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNobWFjLXNoYTI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6IjYiLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiUGFuZGEiLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOlsiVXNlciIsIk1hbmFnZXIiLCJBZG1pbiJdLCJTb21lQ29kZSI6IlBhbmRhNjY2Y29tIiwiZXhwIjoxNzExMDEwNzE4fQ.qpJWz8aEefmUycBYb1qlQz5GBuU5wARNJ-FHvHF6abA
--------head--------
{"alg":"http://www.w3.org/2001/04/xmldsig-more#hmac-sha256","typ":"JWT"}
--------payload--------
{"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier":"6","http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name":"Panda","http://schemas.microsoft.com/ws/2008/06/identity/claims/role":["User","Manager","Admin"],"SomeCode":"Panda666com","exp":1711010718}
校验JWT
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier=6
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name=Panda
http://schemas.microsoft.com/ws/2008/06/identity/claims/role=User
http://schemas.microsoft.com/ws/2008/06/identity/claims/role=Manager
http://schemas.microsoft.com/ws/2008/06/identity/claims/role=Admin
SomeCode=Panda666com
exp=1711010718
注意:
- 生成jwt时候的key必须在16位以上,否则会因为长度不够抛出异常。
- jwt本身是不加密的,里面包含的信息任何人都可以读取到。
- jwt的签名部分是对前两部分的签名,防止数据被篡改。
3、.Net Core JWT 授权
1、Nuget 安装 Microsoft.AspNetCore.Authentication.JwtBearer
2、在appsettings.json
中配置JWT 参数,如下:
"JwtConfig": {
"SecretKey": "d0ecd23c-dfdb-4005-a2ea-0fea210c858a", // 密钥 可以是guid 也可以是随便一个字符串
"Issuer": "zhangsan", // 颁发者
"Audience": "zhangsan", // 接收者
"Expired": 30 // 过期时间(30min)
}
JwtConfig
/// <summary>
/// jwt配置
/// </summary>
public class JwtConfig : IOptions<JwtConfig>
{
public JwtConfig Value => this;
public string SecretKey { get; set; }
public string Issuer { get; set; }
public string Audience { get; set; }
public int Expired { get; set; }
public DateTime NotBefore => DateTime.UtcNow;
public DateTime IssuedAt => DateTime.UtcNow;
public DateTime Expiration => IssuedAt.AddMinutes(Expired);
private SecurityKey SigningKey => new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecretKey));
public SigningCredentials SigningCredentials =>
new SigningCredentials(SigningKey, SecurityAlgorithms.HmacSha256);
}
3、实体类
LoginUserModel
public class LoginUserModel
{
public string userid { get; set; }
public string username { get; set; }
public string phone { get; set; }
public string roles { get; set; }
}
JwtTokenResult
public class JwtTokenResult
{
public string access_token { get; set; }
public string refresh_token { get; set; }
/// <summary>
/// 过期时间(单位秒)
/// </summary>
public int expires_in { get; set; }
public string token_type { get; set; }
public LoginUserModel user { get; set; }
}
4、创建JWT服务类
public class GenerateJwt
{
private readonly JwtConfig _jwtConfig;
public GenerateJwt(IOptions<JwtConfig> jwtConfig)
{
_jwtConfig = jwtConfig.Value;
}
/// <summary>
/// 生成token
/// </summary>
/// <param name="sub"></param>
/// <param name="customClaims">携带的用户信息</param>
/// <returns></returns>
public string GenerateEncodedToken(LoginUserModel customClaims)
{
//创建用户身份标识,可按需要添加更多信息
var claims = new List<Claim>();
claims.Add(new Claim("phone",customClaims.phone));
claims.Add(new Claim(ClaimTypes.NameIdentifier, customClaims.userid));
claims.Add(new Claim(ClaimTypes.Name, customClaims.username));
foreach (var role in customClaims.roles.Split(','))
{
claims.Add(new Claim(ClaimTypes.Role, role));
}
//创建令牌
var jwt = new JwtSecurityToken(
issuer: _jwtConfig.Issuer,
audience: _jwtConfig.Audience,
claims: claims,
notBefore: _jwtConfig.NotBefore,
expires: _jwtConfig.Expiration,
signingCredentials: _jwtConfig.SigningCredentials);
//生成JWT
string access_token = new JwtSecurityTokenHandler().WriteToken(jwt);
//return new JwtTokenResult()
//{
// access_token = access_token,
// expires_in = _jwtConfig.Expired * 60,
// token_type = JwtBearerDefaults.AuthenticationScheme,
// user = customClaims
//};
return access_token;
}
}
5、控制器代码
[Route("api/[controller]/[action]")]
[ApiController]
[Authorize()]//鉴权
public class JwtController : ControllerBase
{
private readonly GenerateJwt _generateJwt;
public JwtController(GenerateJwt generate)
{
this._generateJwt = generate;
}
[AllowAnonymous]//可匿名
[HttpPost]
public ActionResult Login([FromBody] LoginUserModel loginUser)
{
if (!ModelState.IsValid)
{
return BadRequest("Invalid Request");
}
var refreshToken = Guid.NewGuid().ToString();
/*
生成token之前要验证一下账户是否存在
*/
try
{
var jwtTokenResult = _generateJwt.GenerateEncodedToken(loginUser);
////客户端每次登陆都刷新jwt guid值,与保存的guid相比较
////若不相同,则说明是不同客户端登陆,之前登陆的客户端jwt过时,需重新登陆
//jwtTokenResult.refresh_token = refreshToken;
//这里可按需返回
return Ok(jwtTokenResult);
}
catch (Exception e) { return BadRequest("Invalid Request"); }
}
[Authorize(Roles = "admin")]//角色,可叠加
[HttpGet]
public ActionResult GetAuthorization()
{
var claims = new LoginUserModel()
{
username = User.FindFirst(ClaimTypes.Name).Value,
userid = User.FindFirst(ClaimTypes.NameIdentifier).Value,
roles = string.Join(',', User.FindAll(ClaimTypes.Role).Select(c => c.Value)),
phone = User.FindFirst("phone").Value,
};
return Ok(claims);
}
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
}
6、在Startup.cs 中启用Jwt认证
//注入jwt服务
services.AddScoped<GenerateJwt>();
//读取配置文件中的JwtConfig字段
services.Configure<JwtConfig>(Configuration.GetSection("JwtConfig"));
//将字段中的值赋值给 JwtConfig 实体类
var jwtConfig = new JwtConfig();
Configuration.Bind("JwtConfig", jwtConfig);
//var jwtConfig = Configuration.GetSection("JwtConfig").Get<JwtConfig>();
#region jwt认证
services
.AddAuthentication(option =>
{
//认证middleware配置
option.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
option.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
//是否验证SecurityKey
ValidateIssuerSigningKey = true,
//Token颁发机构
ValidIssuer = jwtConfig.Issuer,
//颁发给谁
ValidAudience = jwtConfig.Audience,
//这里的key要进行加密
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtConfig.SecretKey)),
//是否验证Token有效期,使用当前时间与Token的Claims中的NotBefore和Expires对比
ValidateLifetime = true,
////过期时间容错值,解决服务器端时间不同步问题(秒)
//ClockSkew = TimeSpan.FromSeconds(30),
//RequireExpirationTime = true,
};
});
#endregion
启用认证、授权中间件:
app.UseAuthentication();
app.UseAuthorization();
7、配置Swagger全局授权
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "WebApplication1", Version = "v1" });
#region 启用swagger验证功能
//添加一个必须的全局安全信息,和AddSecurityDefinition方法指定的方案名称一致即可。
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference {
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
new string[] { }
}
});
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = "JWT授权(数据将在请求头中进行传输) 在下方输入Bearer {token} 即可,注意两者之间有空格",
Name = "Authorization",//jwt默认的参数名称
In = ParameterLocation.Header,//jwt默认存放Authorization信息的位置(请求头中)
Type = SecuritySchemeType.ApiKey,
BearerFormat = "JWT",
Scheme = "Bearer",
});
#endregion
});
Authotrize授权,Value:Bearer {jwt字符串},注意:中间加一个空格
8、测试
Login接口:
入参:出参:
Authotrize授权后,GetAuthorization接口:
get接口未授权:
标签:string,JWT,jwt,claims,new,public From: https://www.cnblogs.com/xixi-in-summer/p/18088028