一、JWT結構
JWT介紹就太多了,這裡主要關注下Jwt的結構。
Jwt中包含三個部分:Header(頭部).Payload(負載).Signature(簽名)
-
Header:描述 JWT 的後設資料的JSON物件,如:
{"alg":"HS256","typ":"JWT"}
-
Payload:一個用來存放實際需要傳遞的資料的JSON 物件。如:
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": "admin", "exp": 1610877510, "iss": "cba", "aud": "cba" }
- Signature:對前兩部分(Header、Payload)的簽名,防止資料篡改。
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
JWT示例:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiYWRtaW4iLCJleHAiOjE2MTA4Nzc1MTAsImlzcyI6ImNiYSIsImF1ZCI6ImNiYSJ9.O9lbZwfqRuA6vKcRCfYieA1zLkTPppdSvTc8UzwCkNw
二、ASP.NET Core 使用JTW認證
1、新增Nuget包引用:
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
2、定義一個JwtSettingDto結構,用於讀取JWT配置資訊
public class JwtSetting { /// <summary> /// 發行者 /// </summary> public string Issuer { get; set; } /// <summary> /// 受眾 /// </summary> public string Audience { get; set; } /// <summary> /// 祕鑰 /// </summary> public string SecretKey { get; set; } /// <summary> /// 過期時間 /// </summary> public int AccessExpiration { get; set; } /// <summary> /// 重新整理時間 /// </summary> public int RefreshExpiration { get; set; } }
3、在appsetting.json配置檔案中,新增jwt相關配置資訊
"JWTSetting": { "Issuer": "cba", "Audience": "cba", "SecretKey": "123456789abcdefghi", "AccessExpiration": 60, "RefreshExpiration": 80 }
4、在Startup.cs 中啟用Jwt認證
public void ConfigureServices(IServiceCollection services) { services.Configure<JwtSetting>(Configuration.GetSection("JWTSetting")); var token = Configuration.GetSection("JWTSetting").Get<JwtSetting>(); //JWT認證 services.AddAuthentication(x => { x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }).AddJwtBearer(x => { x.RequireHttpsMetadata = false; x.SaveToken = true; x.TokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(token.SecretKey)), ValidIssuer = token.Issuer, ValidAudience = token.Audience, ValidateIssuer = false, ValidateAudience = false }; }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.UseAuthentication(); app.UseAuthorization(); }
5、實現認證控制器:
a) 新增認證Dto
public class LoginDto { [Required] public string Username { get; set; } [Required] public string Password { get; set; } }
b) 實現使用者校驗服務:預設實現:當使用者密碼都等於admin通過校驗
public interface IUserService { bool IsValid(LoginDto request); } public class UserService : IUserService { //本次,固定校驗admin public bool IsValid(LoginDto request) { return request.Password == request.Username && request.Username == "admin"; } }
c) 實現簽發token的認證服務:
//認證服務介面
public interface IAuthenticateService { bool IsAuthenticated(LoginDto request, out string token); } //認證服務 public class TokenAuthenticationService : IAuthenticateService { private readonly IUserService _userService; private readonly JwtSetting _jwtSetting; public TokenAuthenticationService(IUserService userService, IOptions<JwtSetting> jwtSetting) { _userService = userService; _jwtSetting = jwtSetting.Value; } public bool IsAuthenticated(LoginDto request, out string token) { token = string.Empty; if (!_userService.IsValid(request)) return false; var claims = new[] { new Claim(ClaimTypes.Name, request.Username) }; var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSetting.SecretKey)); var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var jwtToken = new JwtSecurityToken(_jwtSetting.Issuer, _jwtSetting.Audience, claims, expires: DateTime.Now.AddMinutes(_jwtSetting.AccessExpiration), signingCredentials: credentials); token = new JwtSecurityTokenHandler().WriteToken(jwtToken); return true; } }
d) 新增認證控制器
[Route("api/[controller]")] [ApiController] public class AuthenticationController : ControllerBase { private IAuthenticateService _authService; public AuthenticationController(IAuthenticateService authService) { _authService = authService; } [AllowAnonymous] [HttpPost, Route("RequestToken")] public ActionResult RequestToken([FromBody] LoginDto request) { if (!ModelState.IsValid) { return BadRequest("Invalid Request"); } string token; if (_authService.IsAuthenticated(request, out token)) { return Ok(token); } return BadRequest("Invalid Request"); } }
6、注入服務:TokenAuthenticationService 、UserService
services.AddScoped<IUserService, UserService>();
services.AddScoped<IAuthenticateService, TokenAuthenticationService>();
7、新增測試控制器:
[Authorize] [Route("api/[controller]")] [ApiController] public class AuditLogController : ControllerBase { // GET: api/<AuditLogController> [HttpGet] public IEnumerable<string> Get() { return new string[] { "value1", "value2" }; } }
到此Jwt認證在.net core中已經實現,接下來驗證下執行效果
三、驗證結果
1、請求AuditLog介面:api/AuditLog未傳入認證資訊時:
2、獲取Token:
3、新增token呼叫介面:
四、Swagger UI新增認證
在專案中通常都新增了Swagger UI來展示介面及基礎測試,那麼如果新增了認證後,如何在呼叫介面前新增認證資訊呢?
在Startup中:ConfigureServices中新增Swagger設定時,新增認證設定
//註冊Swagger生成器,定義一個和多個Swagger 文件 services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "AuditLogDemo API", 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 });
執行效果: 新增token->呼叫需認證介面
其他:
原始碼地址:https://github.com/cwsheng/AuditLogDemo.git