jwt身份认证的原理
jwt token的格式
jwt token 的格式为 Header.Payload.VeritySignature.
实际jwt如下
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
用Json格式表示如下:
"jwt":{
"header":{
"alg": "HS256",
"typ": "JWT"
}
"payload":{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
"VerifySignature":
{
HMACSHA256(base64UrlEncode(header) + "." +
base64UrlEncode(payload),
your-256-bit-secret).toBase64String()
}
}
生成jwt
作为使用者,自己需要掌握SecurityTokenDescriptor类的参数,通过调用JsonWebTokenHandler().CreateToken(new SecurityTokenDescriptor() ) 来生成jwt。asp.net SecurityTokenDescriptor 的构造函数如下:
public class SecurityTokenDescriptor
{
//
// 摘要:
// Gets or sets the value of the 'audience' claim.
public string Audience { get; set; }
//
// 摘要:
// Defines the compression algorithm that will be used to compress the JWT token
// payload.
public string CompressionAlgorithm { get; set; }
//
// 摘要:
// Gets or sets the Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor.EncryptingCredentials
// used to create a encrypted security token.
public EncryptingCredentials EncryptingCredentials { get; set; }
//
// 摘要:
// Gets or sets the value of the 'expiration' claim. This value should be in UTC.
public DateTime? Expires { get; set; }
//
// 摘要:
// Gets or sets the issuer of this Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor.
public string Issuer { get; set; }
//
// 摘要:
// Gets or sets the time the security token was issued. This value should be in
// UTC.
public DateTime? IssuedAt { get; set; }
//
// 摘要:
// Gets or sets the notbefore time for the security token. This value should be
// in UTC.
public DateTime? NotBefore { get; set; }
//
// 摘要:
// Gets or sets the token type. If provided, this will be added as the value for
// the 'typ' header parameter. In the case of a JWE, this will be added to both
// the inner (JWS) and the outer token (JWE) header. By default, the value used
// is 'JWT'. If Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor.AdditionalHeaderClaims
// also contains 'typ' header claim value, it will override the TokenType provided
// here. This value is used only for JWT tokens and not for SAML/SAML2 tokens
public string TokenType { get; set; }
//
// 摘要:
// Gets or sets the System.Collections.Generic.Dictionary`2 which represents the
// claims that will be used when creating a security token. If both and Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor.Subject
// are set, the claim values in Subject will be combined with the values in Claims.
// The values found in Claims take precedence over those found in Subject, so any
// duplicate values will be overridden.
public IDictionary<string, object> Claims { get; set; }
//
// 摘要:
// Gets or sets the System.Collections.Generic.Dictionary`2 which contains any custom
// header claims that need to be added to the JWT token header. The 'alg', 'kid',
// 'x5t', 'enc', and 'zip' claims are added by default based on the Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor.SigningCredentials,
// Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor.EncryptingCredentials,
// and/or Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor.CompressionAlgorithm
// provided and SHOULD NOT be included in this dictionary as this will result in
// an exception being thrown. These claims are only added to the outer header (in
// case of a JWE).
public IDictionary<string, object> AdditionalHeaderClaims { get; set; }
//
// 摘要:
// Gets or sets the System.Collections.Generic.Dictionary`2 which contains any custom
// header claims that need to be added to the inner JWT token header. The 'alg',
// 'kid', 'x5t', 'enc', and 'zip' claims are added by default based on the Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor.SigningCredentials,
// Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor.EncryptingCredentials,
// and/or Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor.CompressionAlgorithm
// provided and SHOULD NOT be included in this dictionary as this will result in
// an exception being thrown. For JsonWebTokenHandler, these claims are merged with
// Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor.AdditionalHeaderClaims
// while adding to the inner JWT header.
public IDictionary<string, object> AdditionalInnerHeaderClaims { get; set; }
//
// 摘要:
// Gets or sets the Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor.SigningCredentials
// used to create a security token.
public SigningCredentials SigningCredentials { get; set; }
//
// 摘要:
// Gets or sets the System.Security.Claims.ClaimsIdentity. If both and Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor.Subject
// are set, the claim values in Subject will be combined with the values in Claims.
// The values found in Claims take precedence over those found in Subject, so any
// duplicate values will be overridden.
public ClaimsIdentity Subject { get; set; }
}
生成Jwt的实际代码:
public string CreatJwt(IdentityUser user,int AddMinutes)
{
PasswordHasher<IdentityUser> hasher = new PasswordHasher<IdentityUser>();
RSA rSA = RSA.Create();
string s= _config.GetValue<string>("RSAPrivateKey");
byte[] b=System.Convert.FromBase64String(s);
int i;
rSA.ImportRSAPrivateKey(b, out i);
var okey = new RsaSecurityKey(rSA);
IdentityUser user0 = _Dbcontext.Users.FirstOrDefault(o => o.Id == user.Id);
if (user != null)
{
string? jwt = new JsonWebTokenHandler().CreateToken(new SecurityTokenDescriptor()
{
Issuer = "NewSoft",
Audience = "lxl",
IssuedAt = DateTime.UtcNow,
NotBefore = DateTime.UtcNow,
Expires = DateTime.UtcNow.AddMinutes(AddMinutes),
Subject = new System.Security.Claims.ClaimsIdentity(new List<Claim>
{
new Claim("email", user.Email),
new Claim("host", "192.168.3.2"),
new Claim(ClaimTypes.Name,user.UserName),
new Claim(ClaimTypes.Role,"asd"),
}, "myBearer"),
SigningCredentials = new SigningCredentials(okey, SecurityAlgorithms.RsaSha384Signature)
}); ;
return jwt;
}
else
return "";
}
使用Jwt
把Jwt 传递到服务器
1.使用变量传递到服务器
代码如下:
MessageRecieveContext ctx
if (ctx.Request.Query.ContainsKey("token"))
{
ctx.Token = ctx.Request.Query["token"];
}
2.使用Cookies
代码如下:
if (!string.IsNullOrEmpty(ctx.HttpContext.Request.Cookies["token"]))
{
ctx.Token= ctx.HttpContext.Request.Cookies["token"];
}
服务器身份认证
服务器收到HttpContext.Request后,调用Authentication服务,自动进行身份的认证与授权。
builder.Services.AddAuthentication(o => o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme).AddJwtBearer("Bearer",o=>
{
RSA rsa = RSA.Create();
string s = builder.Configuration.GetValue<string>("RSAPublicKey");
byte[] b = new byte[2048];
int i = 0;
b=System.Convert.FromBase64String(s);
rsa.ImportRSAPublicKey(b, out i);
o.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
ValidIssuer = "NewSoft",
ValidateAudience = true,
ValidAudience = "lxl",
IssuerSigningKey = new RsaSecurityKey(rsa),
ValidateIssuerSigningKey = true
};
}
上例代码,当服务器收到httpContext后,用RSA公钥,对jwt进行验证签名,若签名无误,则将身份信息写入HttpContext.User.Identity中,再根据用户名,角色等,授予request的访问权限。
Api访问权限的设置
Api的访问权限设置时,只需要在Api函数前加上[Authorize(Roles ="admin,asd")],即代表允许角色为admin 和asd的进行访问。当然,你也可以设定用户名为:“张三,李四,王麻子”的调用权限。你不特殊设定,只要通过认证的任何人和任何角色都可访问。
[Authorize(Roles ="admin,asd")]
// GET: Stations
public async Task<IActionResult> Index()
{
return View(await _context.Stationss.ToListAsync());
}