当前位置:网站首页>ASP. Net core JWT certification
ASP. Net core JWT certification
2022-04-23 17:10:00 【begeneral】
ASP.NET CORE edition :.NET 5.0
About JWT Basic knowledge of , Please search the Internet , This is mainly explained from the code level .
First, create a new one ASP.NET CORE Web api Empty project for , And then create a new one LoginController Of api controller .
from nuget Installation package in :Microsoft.AspNetCore.Authentication.JwtBearer;
1、 Generate Token
Look at the code first :
[Route("api/[controller]/[action]")]
[ApiController]
public class LoginController : Controller
{
private readonly PermissionRequirement _permissionRequirement;
public LoginController(PermissionRequirement permissionRequirement)
{
_permissionRequirement = permissionRequirement;
}
[HttpGet]
public IActionResult GetJwtToken(string userName, string password)
{
var claims = new List<Claim> {
new Claim(ClaimTypes.GivenName, "mark"),
new Claim(ClaimTypes.Name, userName),
new Claim(JwtRegisteredClaimNames.Jti, "10000") };
var roles = new List<Claim>();
if (userName == "ncy")
{
roles.Add(new Claim(ClaimTypes.Role, "admin"));
}
else
{
roles.Add(new Claim(ClaimTypes.Role, "normal"));
}
claims.AddRange(roles);
var token = JwtToken.BuildJwtToken(claims.ToArray(), _permissionRequirement);
var jm = new RespEntity();
jm.code = 0;
jm.data = token;
jm.msg = "success";
return Json(jm);
}
}
I add different roles according to the entered user name here for role permission control , You don't have to pay attention to this .
PermissionRequirement It is a class composed of parameter fields required for authentication , The definition is as follows :
public class PermissionRequirement : IAuthorizationRequirement
{
/// <summary>
/// User privilege set , An order contains many details ,
/// Empathy , The certification of a website is being issued , There are also many permission details ( Here is Role and URL The relationship between )
/// </summary>
public List<PermissionItem> Permissions { get; set; }
/// <summary>
/// No authority action
/// </summary>
public string DeniedAction { get; set; }
/// <summary>
/// Authentication authorization type
/// </summary>
public string ClaimType { internal get; set; }
/// <summary>
/// Request path
/// </summary>
public string LoginPath { get; set; } = "/Api/Login";
/// <summary>
/// The issuer
/// </summary>
public string Issuer { get; set; }
/// <summary>
/// Subscribe to the people
/// </summary>
public string Audience { get; set; }
/// <summary>
/// Expiration time
/// </summary>
public TimeSpan Expiration { get; set; }
/// <summary>
/// Signature verification
/// </summary>
public SigningCredentials SigningCredentials { get; set; }
/// <summary>
/// structure
/// </summary>
/// <param name="deniedAction"> Refusing a contract request url</param>
/// <param name="permissions"> Privilege set </param>
/// <param name="claimType"> Declaration type </param>
/// <param name="issuer"> The issuer </param>
/// <param name="audience"> Subscribe to the people </param>
/// <param name="signingCredentials"> Signature verification entity </param>
/// <param name="expiration"> Expiration time </param>
public PermissionRequirement(string deniedAction, List<PermissionItem> permissions, string claimType, string issuer, string audience, SigningCredentials signingCredentials, TimeSpan expiration)
{
ClaimType = claimType;
DeniedAction = deniedAction;
Permissions = permissions;
Issuer = issuer;
Audience = audience;
Expiration = expiration;
SigningCredentials = signingCredentials;
}
}
Actually generate token Function of :
public static string BuildJwtToken(Claim[] claims, PermissionRequirement
permissionRequirement)
{
var now = DateTime.Now;
// Instantiation JwtSecurityToken
var jwt = new JwtSecurityToken(
issuer: permissionRequirement.Issuer,
audience: permissionRequirement.Audience,
claims: claims,
notBefore: now,
expires: now.Add(permissionRequirement.Expiration),
signingCredentials: permissionRequirement.SigningCredentials
);
// Generate Token
var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
return encodedJwt;
}
So let's see Claim This class . This class is actually JWT Load of , The message body . We are instantiating a Claim Is passed in 2 Parameters , One type, One value, It's actually a key value pair . We turn on jwt analysis token Website :JSON Web Tokens - jwt.io, Make us into token Copy to the text box on the left , The screenshot is as follows :
payload In front of 3 Whether we use a key value pair Claim Defined ( there type Is string )? As for the following key value pairs , Let's look at the function BuildJwtToken, These values are in PermissionRequirement Assigned in .
2、ClaimTypes.Role
new Claim(ClaimTypes.Role, "admin"). Write the role information of the current user into the load . I am here. admin It's the role of testing , The actual role should be after the user logs in , Get the role of the user from the database .
There is a benefit of writing... Into a character : When verifying role permissions , There is no need to query the role of the current user from the database , Reduce the number of database queries . But there are several disadvantages :
1、 If the user has many roles , It can lead to token Bigger , And every time the client interacts with the server, it sends token, Therefore, it will lead to the increase of network traffic .
2、 If the user's role is modified , Then the user must log in again . because JWT Token It's disposable , You must log in again to get a new token.
For this question , I think the better plan is : Store the user's role information in the cache ( such as redis), Then update the cache every time you modify the user's role information .
3、 Security
We just generated token Put in jwt Official token The content of load can be analyzed in the analysis tool , That explains it jwt There are security problems . Actually jwt Just for payload Did base64 code , As long as the load part is base64 Decoding can get the content of the load . So we can't put important information in paylaod Inside .
4、 add to JWT Certification services
The following code is written in startup Class ConfigureServices Function , Used to register JWT authentication
var keyBytes = Encoding.Default.GetBytes("MyJwtWebTokenApplication");
var signingKey = new SymmetricSecurityKey(keyBytes);
var issuer = "CoreShop";
var audience = "CoreCms";
// Token validation parameters
var tokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true, // Whether the validation SecurityKey
IssuerSigningKey = signingKey, // Get SecurityKey
ValidateIssuer = true, // Whether the validation Issuer
ValidIssuer = issuer,// The issuer //Issuer, These two and the front sign jwt The Settings are consistent
ValidateAudience = true, // Whether the validation Audience
ValidAudience = audience,// Subscribe to the people
ValidateLifetime = true,// Is the failure time verified
ClockSkew = TimeSpan.FromSeconds(0),
RequireExpirationTime = true,
};
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultForbidScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(o =>
{
o.TokenValidationParameters = tokenValidationParameters;
o.Events = new JwtBearerEvents
{
OnChallenge = context =>
{
context.Response.Headers.Add("Token-Error", context.ErrorDescription);
return Task.CompletedTask;
},
OnAuthenticationFailed = context =>
{
var token = context.Request.Headers["Authorization"].ObjectToString().Replace("Bearer ", "");
var jwtToken = (new JwtSecurityTokenHandler()).ReadJwtToken(token);
if (jwtToken.Issuer != issuer)
{
context.Response.Headers.Add("Token-Error-Iss", "issuer is wrong!");
}
if (jwtToken.Audiences.FirstOrDefault() != audience)
{
context.Response.Headers.Add("Token-Error-Aud", "Audience is wrong!");
}
// If expired , Then put < Is it overdue > Add to , Back to the header
if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
{
context.Response.Headers.Add("Token-Expired", "true");
}
return Task.CompletedTask;
}
};
});
Several constant strings here should be defined in the configuration file , For convenience , I wrote it directly here .
Note that the key length here must be greater than or equal to 16 Bytes , Otherwise, it will be generated token I will make a mistake
Token validation parameters ClockSkew Parameters ( Default clock offset field ), You are generating token When the specified expiration time is added with this deviation time, it is token The expiration time of . Its default value is 300 second , The definition is as follows :
This place feels a little pit , Everyone should be careful .
The next step is to specify the authentication scheme , The default solution is Bearer. When using this scheme , The result of certification is JWT The default return result . Mainly reflected in the returned status code , If the certification is successful , The status code is 200. Failure (Challenge) Namely 401. Authentication succeeded but no authorization (Forbid) Namely 403.
In addition to using the default scheme , We can also customize the scheme . Define a class inheritance AuthenticationHandler<AuthenticationSchemeOptions>, Then rewrite the return result you want to customize (Authenticate、Challenge、Forbid), The code is as follows :
x.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = nameof(ApiResponseForAdminHandler);
x.DefaultForbidScheme = nameof(ApiResponseForAdminHandler);
public class ApiResponseForAdminHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
public ApiResponseForAdminHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
{
}
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
throw new NotImplementedException();
}
protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
{
Response.ContentType = "application/json";
var jm = new AdminUiCallBack();
jm.code = 401;
jm.data = 401;
jm.msg = " I'm sorry , You do not have access to this interface , Please make sure you are logged in !";
await Response.WriteAsync(JsonConvert.SerializeObject(jm));
}
protected override async Task HandleForbiddenAsync(AuthenticationProperties properties)
{
Response.ContentType = "application/json";
var jm = new AdminUiCallBack();
jm.code = 403;
jm.msg = " I'm sorry , Your access level is not enough , Contact administrator !!";
await Response.WriteAsync(JsonConvert.SerializeObject(jm));
}
}
there HandleAuthenticateAsync We directly throw an exception that is not implemented , Because in AddAuthentication when DefaultAuthenticateScheme=JwtBearerDefaults.AuthenticationScheme; So this function will not be called . But the following challenge and forbidden Is to use this class as its scheme .
In this case , Even if the certification is not successful , The returned status code is also 200, We need to go from AdminUiCallBack Get the result of authentication in class .
However, when using custom schemes , You need to add a line of code , Specifies the class that provides the scheme
.AddScheme<AuthenticationSchemeOptions, ApiResponseForAdminHandler>(nameof(ApiResponseForAdminHandler), o => { })
This is AddJwtBearer Call after the function . Here's one thing to pay attention to , A custom scheme is used ,JwtBearerEvents The event delegate defined in it will become invalid , So here's one of two . If the solution is too complex , You can use custom schemes , conversely , You can use event delegates to handle . If you use an event delegate , You don't need to call AddScheme Function .
Last in Configure Call in function app.UseAuthentication(); Enable the authentication middleware .
5、C/S Client side authentication
webapi not only b/s The front end can call ,c# winform You can also call
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _token);
var response = await client.GetAsync("http://localhost:5000/api/test/GetMyAddress");
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine($"api Return the data :{content}");
}
else
{
Console.WriteLine($" request api Interface failed , Status code :{((int)response.StatusCode)}");
}
Please take a look at the second line of code , stay new One AuthenticationHeaderValue Object time , One parameter is the authentication scheme , One is token Value , This certification scheme must bring .
Parsing on the server side token You should also put Bearer Remove this string to parse token
版权声明
本文为[begeneral]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/04/202204230554081782.html
边栏推荐
- Decimal format decimal / datetime conversion processing
- 杂文 谈谈古典的《拆掉思维里的墙》
- Devexpress GridView add select all columns
- Detailed explanation of Niuke - Gloves
- Regular filtering of Intranet addresses and segments
- Signalr can actively send data from the server to the client
- Lock lock
- Baidu Map Case - Zoom component, map scale component
- Promise (IV)
- ◰ GL shader handler encapsulation
猜你喜欢
[PROJECT] small hat takeout (8)
Nodejs installation and environment configuration
【WPF绑定3】 ListView基础绑定和数据模板绑定
Bytevcharts visual chart library, I have everything you want
TypeError: set_ figure_ params() got an unexpected keyword argument ‘figsize‘
Sub database and sub table & shardingsphere
2.Electron之HelloWorld
网络安全之渗透靶场实战详解
Node access to Alipay open platform sandbox to achieve payment function
Milvus 2.0 质量保障系统详解
随机推荐
VLAN高级技术,VLAN聚合,超级Super VLAN ,Sub VLAN
拷贝构造函数 浅拷贝与深拷贝
◰ GL shader handler encapsulation
1-2 characteristics of nodejs
Decimal format decimal / datetime conversion processing
Generation of barcode and QR code
Zhimeng dedecms security setup Guide
Use between nodejs modules
Signalr can actively send data from the server to the client
Multithreaded @ async thread pool
SQL: How to parse Microsoft Transact-SQL Statements in C# and to match the column aliases of a view
EF core in ASP Generate core priority database based on net entity model
websocket
Deeply understand the relevant knowledge of 3D model (modeling, material mapping, UV, normal), and the difference between displacement mapping, bump mapping and normal mapping
Use of shell cut command
自定义my_strcpy与库strcpy【模拟实现字符串相关函数】
Further study of data visualization
基于51单片机红外无线通讯仿真
Expression "func" tSource, object "to expression" func "tSource, object" []
Quick install mongodb