前言
http協議本身是一種無狀態的協議。所以客戶端的每次請求,服務端是不清楚其身份的,需要客戶端每次都要將身份信息傳入,服務進行驗證,才能達到安全驗證的目的。
傳統的Web用戶驗證:
1、客戶端傳入用戶名和密碼---2、后端驗證成功后保存session信息,並將session信息返回給客戶端--3、客戶端保存session信息到cookie中---
4、客戶端再請求需要的資源時,將cookie中的session信息傳給服務端--5、服務端驗證信息是否正確,正確則進行相應的操作,不正確則返回登錄頁面或者提示錯誤。
當然傳統的session-cookie方式是可以達到用戶身份驗證的目的,但是在分布式的場景下就不好使了。因為session是存在當時對應的服務器中的,當我們部署多台服務器,並采用了負載均衡時,session不共享時,只能在其驗證的服務器上才能通過,而無法達到多台服務器協同負載工作。(當然也可以通過將session信息存於數據庫或者是分布式緩存中,每次需要驗證時從數據庫或者緩存中獲取做驗證,但是這樣就多了一重數據庫操作,或者是緩存出問題時無法驗證)。
Token驗證模式:
1、客戶端使用用戶名密碼來請求服務器--2、服務器驗證用戶信息,成功則返回客戶端一個token信息--3、客戶端留存token信息,在每次請求時將token一並傳給服務端--4、服務端驗證token,正確則返回數據。
Jwt(Json web token), 是為了在網絡應用環境間傳遞聲明而執行的一種基於JSON的開放標准((RFC 7519).該token被設計為緊湊且安全的。它主要由3部分數據組成:頭部(header),載荷(payload),簽名(signature)。其實它就是token,服務端驗證成功后按照算法組成jwt,返回給客戶端;客戶端請求服務端附帶jwt,服務端驗證jwt正確性,正確則返回對應的數據
.netcore實現jwt身份驗證(同時集成swagger)
1、在appsettings.json文件中配置(注意這些配置信息的安全性,特別是secretkey。secretkey長度一般要求16位或以上)
"JwtSettings": { "Issuer": "john_yong", "Audience": "user", "SecretKey": "123456789123456789" },
2、在startup.cs的ConfigureServices(IServiceCollection services)方法中配置
services.Configure<JwtSetting>(Configuration.GetSection("JwtSettings")); JwtSetting setting = new JwtSetting(); Configuration.Bind("JwtSettings", setting); #region swagger services.AddSwaggerGen(p => { p.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo { Version = "v1", Title = "SimpleTest API 接口文檔", //Contact = new OpenApiContact //{ // Name = "john_yong", // Email = "gxunet@163.com", // Url = new Uri("http://xxxx.com"), //}, }); //p.OperationFilter<HttpHeaderOperation>(); // 添加httpHeader參數 #region 啟用swagger驗證功能 p.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme() { Description = "在下框中輸入請求頭中需要添加Jwt授權Token:Bearer Token", Name = "Authorization", In = ParameterLocation.Header, Type = SecuritySchemeType.ApiKey, BearerFormat = "JWT", Scheme = "Bearer" }); p.AddSecurityRequirement(new OpenApiSecurityRequirement { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" } }, new string[] { } } }); #endregion // 為 Swagger JSON and UI設置xml文檔注釋路徑 var xmlPath = Path.Combine(AppContext.BaseDirectory, "SimpleTestApplication.xml"); p.IncludeXmlComments(xmlPath ); }); #endregion #region 添加驗證服務 services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(p => { p.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ClockSkew = TimeSpan.FromSeconds(30), ValidateIssuerSigningKey = true, ValidAudience = setting.Audience, ValidIssuer = setting.Issuer, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(setting.SecretKey)) }; }); #endregion
在 Configure(IApplicationBuilder app, IWebHostEnvironment env)方法中配置
//啟用授權 app.UseAuthentication(); app.UseAuthorization();
JwtSetting.cs
/// <summary> /// /// </summary> public class JwtSetting { /// <summary> /// 證書頒發者 /// </summary> public string Issuer { get; set; } /// <summary> /// 角色 /// </summary> public string Audience { get; set; } /// <summary> /// 加密字符串 /// </summary> public string SecretKey { get; set; } }
3、創建AuthController提供api驗證獲取jwt
[ApiController] [Route("[controller]")] public class AuthController : Controller { private JwtSetting _jwtSettings; public AuthController(IOptions<JwtSetting> jwtSetting) { _jwtSettings = jwtSetting.Value; } /// <summary> /// 獲取token /// </summary> /// <param name="userName"></param> /// <param name="pwd"></param> /// <returns></returns> [AllowAnonymous] [HttpGet("getToken")] public ResponseFormatDto<TokenInfo> GetToken(string userName, string pwd) { double expiredMinute = 30; //過期時間30分鍾 DateTime expiredTime = DateTime.Now.AddMinutes(expiredMinute); //TODO驗證用戶 if (userName == "admin" && pwd == "1234560") { var claims = new[] { new Claim(JwtRegisteredClaimNames.Nbf,$"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}") , new Claim (JwtRegisteredClaimNames.Exp,$"{new DateTimeOffset(expiredTime).ToUnixTimeSeconds()}"), new Claim(ClaimTypes.Name, userName) }; var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSettings.SecretKey)); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var jwtSecurityToken = new JwtSecurityToken( issuer: _jwtSettings.Issuer, audience: _jwtSettings.Audience, claims: claims, expires: expiredTime, signingCredentials: creds); string token = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken); TokenInfo tokenInfo = new TokenInfo() { Token = token, Expired = expiredTime }; return HelperTool.GetResponse(tokenInfo); } else { return HelperTool.GetResponse<TokenInfo>(null, Status.Fail, "username or password is incorrect."); } } }
TokenInfo.cs
/// <summary> /// token information /// </summary> public class TokenInfo { /// <summary> /// token /// </summary> public string Token { get; set; } /// <summary> /// 過期時間 /// </summary> public DateTime Expired { get; set; } }
HelperTool.cs是簡單通用的構造統一的返回實體ResponseFormatDto(此處略)
結果示例
-------------------------------------
以上就是jwt的簡單介紹以及應用。但是在實際項目中(特別是微服務流行的當下),用戶驗證、token管理一般不會在業務項目中實現的,基本上都是建立用戶中心管理用戶,鑒權用戶信息,生成token等。
一般用Identity 4實現的,實際的業務項目則是提供token參數傳入,在用戶中心中驗證。