JWT的簡(jiǎn)單使用
介紹
當(dāng)今Web開發(fā)中,API的使用越來越廣泛,而API的安全性也變得越來越重要。其中,JWT(JSON Web Token)鑒權(quán)和授權(quán)是一種常見的解決方案。
本篇文章將會(huì)介紹JWT鑒權(quán)和授權(quán)的原理、實(shí)現(xiàn)方式以及注意事項(xiàng)。
什么是JWT?
JWT是一種基于JSON格式的開放標(biāo)準(zhǔn)(RFC7519),用于在網(wǎng)絡(luò)上傳遞聲明信息的一種簡(jiǎn)潔、自包含的安全方式。JWT通常被用來在各個(gè)系統(tǒng)之間傳遞身份認(rèn)證信息和用戶授權(quán)信息。
安裝相關(guān) NuGet 包
在開始使用 JWT 進(jìn)行授權(quán)鑒權(quán)之前,需要先安裝 Microsoft.AspNetCore.Authentication.JwtBearer NuGet 包??梢允褂?Visual Studio 的 NuGet 管理器或者命令行工具進(jìn)行安裝。
Install-Package Microsoft.AspNetCore.Authentication.JwtBearer
JWT的組成部分
JWT由三個(gè)部分組成:Header(頭部)、Payload(負(fù)載)和Signature(簽名)。
頭部(Header)
頭部通常由兩部分組成:令牌類型(即JWT)和指定該令牌所使用的簽名算法。例如:
{
"alg": "HS256",
"typ": "JWT"
}
負(fù)載(Payload)
負(fù)載通常包含了需要傳遞的聲明信息,聲明信息由鍵值對(duì)組成。例如:
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
其中,“sub”表示主題(subject),可以是用戶ID或其他標(biāo)識(shí)符;“name”表示用戶名;“iat”表示令牌發(fā)行時(shí)間。
簽名(Signature)
簽名是對(duì)Header和Payload的內(nèi)容進(jìn)行數(shù)字簽名后得到的一串字符串。簽名用于驗(yàn)證JWT是否被篡改或偽造。例如:
HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)
其中,“secret”為使用該令牌的服務(wù)器端保存的密鑰。
JWT的優(yōu)點(diǎn)
- 簡(jiǎn)潔:由于JWT采用了JSON格式,而JSON是一種輕量級(jí)的數(shù)據(jù)格式,因此JWT非常適合在多個(gè)服務(wù)之間傳遞信息。
- 自包含:JWT包含了所有必要的信息,因此不需要像Session那樣在服務(wù)器端存儲(chǔ)用戶狀態(tài)。
- 安全:JWT使用數(shù)字簽名來保證消息完整性和真實(shí)性,并可以對(duì)負(fù)載進(jìn)行加密處理。
JWT鑒權(quán)
JWT鑒權(quán)是指通過JWT來驗(yàn)證用戶身份和權(quán)限。在使用JWT鑒權(quán)時(shí),客戶端將用戶憑證(例如用戶名和密碼)發(fā)送給服務(wù)器,在服務(wù)器驗(yàn)證用戶憑證有效后,生成一個(gè)JWT并將其返回給客戶端??蛻舳嗽谝院蟮恼?qǐng)求中攜帶這個(gè)JWT,服務(wù)器通過驗(yàn)證JWT的簽名和有效期等信息來驗(yàn)證用戶身份和授權(quán)信息。
JWT鑒權(quán)流程
- 用戶登錄,向服務(wù)器提交身份憑證(例如用戶名、密碼)。
- 服務(wù)器驗(yàn)證身份憑證的有效性。
- 如果身份憑證有效,服務(wù)器生成一個(gè)JWT并將其返回給客戶端。
- 客戶端在以后的請(qǐng)求中攜帶JWT。
- 服務(wù)器從JWT中解析出用戶ID等信息,并根據(jù)信息來驗(yàn)證用戶身份和授權(quán)信息。
JWT鑒權(quán)實(shí)現(xiàn)
配置appsettings.json
我們需要在appsettings.json文件中配置JWT的相關(guān)信息。在您的ASP.NET Core項(xiàng)目中,找到appsettings.json文件,并添加以下配置:
"Authentication": {
"SecretKey": "yourIssuer",
"Issuer": "yourAudience",
"Audience": "yourSecretKey"
},
添加 JWT 鑒權(quán)服務(wù)
在ASP.NET Core中,可以使用JwtBearer認(rèn)證方案來驗(yàn)證JWT。首先,在Startup.cs文件中添加以下代碼:
public void ConfigureServices(IServiceCollection services)
{
// 添加JWT身份驗(yàn)證服務(wù)
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
var secretByte = Encoding.UTF8.GetBytes(Configuration["Authentication:SecretKey"]); // 從appsettings.json讀取Jwt配置
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
ValidIssuer = Configuration["Authentication:Issuer"],
ValidateAudience = true,
ValidAudience = Configuration["Authentication:Audience"],
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(secretByte)
};
});
// 其他服務(wù)配置
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// 其他中間件配置...
app.UseAuthentication();
app.UseAuthorization();
}
上面代碼中,我們向依賴注入容器中注冊(cè)了一個(gè)身份驗(yàn)證方案,名稱為 JwtBearerDefaults.AuthenticationScheme,表示使用 JWT 進(jìn)行身份驗(yàn)證。然后,我們使用 AddJwtBearer 擴(kuò)展方法,將 JWT 鑒權(quán)服務(wù)添加到應(yīng)用程序中。
在 AddJwtBearer 方法中,我們需要配置 TokenValidationParameters 來驗(yàn)證 JWT。其中,ValidateIssuer、ValidIssuer、ValidateAudience、ValidAudience 和 ValidateLifetime 屬性用于驗(yàn)證 JWT 中的發(fā)行人、接收方、有效期等信息。IssuerSigningKey 屬性表示密鑰,用于對(duì) JWT 進(jìn)行數(shù)字簽名。最后,ValidateIssuerSigningKey 屬性用于驗(yàn)證 JWT 的簽名是否正確。
生成JWT
在ASP.NET Core中,可以使用JwtSecurityToken類來創(chuàng)建JWT。例如:
var signingAlgorithm = SecurityAlgorithms.HmacSha256;
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub,"user_id"),
new Claim(ClaimTypes.Role,"Admin"),
new Claim("UserId","12"),
};
var secretByte = Encoding.UTF8.GetBytes(_configuration["Authentication:SecretKey"]);
var signingKey = new SymmetricSecurityKey(secretByte);
var signingCredentials = new SigningCredentials(signingKey, signingAlgorithm);
var token = new JwtSecurityToken(
issuer: _configuration["Authentication:Issuer"],
audience: _configuration["Authentication:Audience"],
claims,
notBefore: DateTime.UtcNow,
expires: DateTime.UtcNow.AddDays(1),
signingCredentials
);
var tokenStr = new JwtSecurityTokenHandler().WriteToken(token);
在這里,我們首先創(chuàng)建了一個(gè)聲明(Claims)列表,其中包含用戶ID、角色信息。然后,我們指定了JWT的過期時(shí)間和簽名算法,并使用SymmetricSecurityKey類來指定密鑰。最后,我們使用JwtSecurityTokenHandler類將token轉(zhuǎn)換為字符串形式的jwt。
驗(yàn)證JWT
public bool ValidateAccessToken(string token)
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.UTF8.GetBytes(_jwtConfig.SecretKey);
try
{
tokenHandler.ValidateToken(token, new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = _jwtConfig.Issuer,
ValidAudience = _jwtConfig.Audience,
IssuerSigningKey = new SymmetricSecurityKey(key)
}, out var validatedToken);
}
catch (Exception)
{
return false;
}
return true;
}
}
上面代碼中,我們使用 JwtSecurityTokenHandler 類來驗(yàn)證 JWT 的真實(shí)性和完整性。其中,我們使用 TokenValidationParameters 來配置驗(yàn)證參數(shù),包括是否驗(yàn)證 JWT 發(fā)行人、接收方、有效期等信息,以及使用哪個(gè)密鑰對(duì)其進(jìn)行數(shù)字簽名。如果驗(yàn)證通過,則返回 true,否則返回 false。
JWT授權(quán)
JWT授權(quán)是指根據(jù)JWT中包含的聲明信息來驗(yàn)證用戶是否具有訪問特定資源的權(quán)限。在使用JWT授權(quán)時(shí),我們?cè)贘WT中添加了一些聲明信息,例如用戶所屬角色、權(quán)限等,服務(wù)器可以通過這些信息來驗(yàn)證用戶是否有權(quán)訪問特定資源。
JWT授權(quán)流程
- 用戶登錄,向服務(wù)器提交身份憑證(例如用戶名、密碼)。
- 服務(wù)器驗(yàn)證身份憑證的有效性。
- 如果身份憑證有效,服務(wù)器生成一個(gè)JWT并將其返回給客戶端。
- 客戶端在以后的請(qǐng)求中攜帶JWT。
- 服務(wù)器從JWT中解析出用戶信息和聲明信息,并根據(jù)信息來驗(yàn)證用戶是否有權(quán)訪問特定資源。
JWT授權(quán)實(shí)現(xiàn)
用戶登錄
創(chuàng)建傳入DTO:LoginDto 、返回DTO:LoginOutDto類
public class LoginDto
{
public string UserName { get; set; }
public string Pwd { get; set; }
}
public class LoginOutDto
{
public int Code { get; set; }
public string Msg { get; set; }
public string access_token { get; set; }
public string refresh_token { get; set; }
}
在用戶登錄時(shí),我們需要對(duì)用戶提供的用戶名和密碼進(jìn)行驗(yàn)證,并生成訪問令牌和刷新令牌。下面是一個(gè)簡(jiǎn)單的示例,演示如何在ASP.NET Core中實(shí)現(xiàn)用戶登錄驗(yàn)證,并生成JWT令牌。
[HttpPost("login")]
public LoginOutDto Login([FromBody] LoginDto input)
{
var dto = new LoginOutDto();
try
{
if (input.UserName != "admin" || input.Pwd != "bb123456")
{
dto.Code = 500;
dto.Msg = "用戶名或密碼不正確";
dto.access_token = string.Empty;
return dto;
}
// 生成訪問令牌
var accessToken = _jwtService.GenerateAccessToken();
// 生成刷新令牌
var refreshToken = _jwtService.GenerateRefreshToken();
dto.Code = 200;
dto.Msg = "登錄成功";
dto.access_token = accessToken;
dto.refresh_token = refreshToken;
}
catch (Exception ex)
{
dto.Code = 500;
dto.Msg = "登錄失敗:" + ex.Message;
}
return dto;
}
在上面的示例中,我們通過調(diào)用_jwtService.GenerateAccessToken和_jwtService.GenerateRefreshToken方法來生成訪問令牌和刷新令牌,并將刷新令牌保存到數(shù)據(jù)庫或其他持久化存儲(chǔ)中,以便后續(xù)使用。
刷新令牌
在用戶登錄后,訪問令牌會(huì)在一定時(shí)間后過期,此時(shí)用戶需要使用刷新令牌來獲取新的訪問令牌,而無需重新登錄。下面是一個(gè)簡(jiǎn)單的示例,演示如何在ASP.NET Core中實(shí)現(xiàn)刷新令牌功能。
[HttpPost("refresh")]
public IActionResult RefreshToken(UserModel model)
{
// 驗(yàn)證刷新令牌是否有效
var isValidRefreshToken = ValidateAccessToken(model.RefreshToken);
if (!isValidRefreshToken)
{
return BadRequest(new { message = "Invalid refresh token" });
}
// 生成新的訪問令牌
var accessToken = _jwtService.GenerateAccessToken(model);
// 返回新的訪問令牌給客戶端
return Ok(new
{
access_token = accessToken
});
}
在上面的示例中,我們通過調(diào)用_jwtService.GenerateAccessToken方法來生成新的訪問令牌,并將其返回給客戶端。在生成新的訪問令牌時(shí),我們可以使用之前保存的用戶信息,例如用戶名等
完整代碼
下面是一個(gè)包含生成 JWT,解析 JWT,鑒權(quán),授權(quán)和策略的完整示例。請(qǐng)注意,此示例僅供參考,請(qǐng)根據(jù)實(shí)際需求進(jìn)行修改。
Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
var secretByte = Encoding.UTF8.GetBytes(Configuration["Authentication:SecretKey"]);
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
ValidIssuer = Configuration["Authentication:Issuer"],
ValidateAudience = true,
ValidAudience = Configuration["Authentication:Audience"],
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(secretByte)
};
});
// 注入IJwtService服務(wù)
services.AddSingleton<IJwtService, JwtService>();
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "JWT.Demo", Version = "v1" });
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "JWT.Demo v1"));
}
app.UseHttpsRedirection();
app.UseRouting();
// 身份驗(yàn)證
app.UseAuthentication();
// 授權(quán)
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
IJwtService&JwtService
public interface IJwtService
{
/// <summary>
/// 生成JWT
/// </summary>
/// <returns></returns>
string GenerateAccessToken();
/// <summary>
/// 刷新Token
/// </summary>
/// <returns></returns>
string GenerateRefreshToken();
/// <summary>
/// 驗(yàn)證JWT
/// </summary>
/// <param name="token"></param>
/// <returns></returns>
bool ValidateAccessToken(string token);
}
public class JwtService : IJwtService
{
private readonly IConfiguration _configuration;
public JwtService(IConfiguration configuration)
{
_configuration = configuration;
}
/// <summary>
/// 生成JWT
/// </summary>
/// <returns></returns>
public string GenerateAccessToken()
{
var signingAlgorithm = SecurityAlgorithms.HmacSha256;
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub,"user_id"),
new Claim(ClaimTypes.Role,"Admin"),
new Claim("UserId","12"),
};
var secretByte = Encoding.UTF8.GetBytes(_configuration["Authentication:SecretKey"]);
var signingKey = new SymmetricSecurityKey(secretByte);
var signingCredentials = new SigningCredentials(signingKey, signingAlgorithm);
var token = new JwtSecurityToken(
issuer: _configuration["Authentication:Issuer"],
audience: _configuration["Authentication:Audience"],
claims,
notBefore: DateTime.UtcNow,
expires: DateTime.UtcNow.AddDays(1),
signingCredentials
);
var tokenStr = new JwtSecurityTokenHandler().WriteToken(token);
return tokenStr;
}
/// <summary>
/// 刷新Token
/// </summary>
/// <returns></returns>
public string GenerateRefreshToken()
{
var randomNumber = new byte[32];
using (var rng = new RNGCryptoServiceProvider())
{
rng.GetBytes(randomNumber);
return Convert.ToBase64String(randomNumber);
}
}
/// <summary>
/// 驗(yàn)證JWT
/// </summary>
/// <param name="token"></param>
/// <returns></returns>
public bool ValidateAccessToken(string token)
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.UTF8.GetBytes(_configuration["Authentication:SecretKey"]);
try
{
tokenHandler.ValidateToken(token, new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = _configuration["Authentication:Issuer"],
ValidAudience = _configuration["Authentication:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(key)
}, out var validatedToken);
}
catch (Exception)
{
return false;
}
return true;
}
}
AuthenticateController.cs
[ApiController]
[Route("auth")]
public class AuthenticateController : ControllerBase
{
private readonly IConfiguration _configuration;
private readonly IJwtService _jwtService;
public AuthenticateController(IConfiguration configuration, IJwtService jwtService)
{
_configuration = configuration;
_jwtService = jwtService;
}
[HttpPost("login")]
public LoginOutDto Login([FromBody] LoginDto input)
{
var dto = new LoginOutDto();
try
{
if (input.UserName != "admin" || input.Pwd != "bb123456")
{
dto.Code = 500;
dto.Msg = "用戶名或密碼不正確";
dto.access_token = string.Empty;
return dto;
}
// 生成訪問令牌
var accessToken = _jwtService.GenerateAccessToken();
// 生成刷新令牌
var refreshToken = _jwtService.GenerateRefreshToken();
dto.Code = 200;
dto.Msg = "登錄成功";
dto.access_token = accessToken;
dto.refresh_token = refreshToken;
}
catch (Exception ex)
{
dto.Code = 500;
dto.Msg = "登錄失敗:" + ex.Message;
}
return dto;
}
[HttpPost("refresh")]
public IActionResult RefreshToken(string token)
{
// 驗(yàn)證刷新令牌是否有效
var isValidRefreshToken = _jwtService.ValidateAccessToken(token);
if (!isValidRefreshToken)
{
return BadRequest(new { message = "Invalid refresh token" });
}
// 生成新的訪問令牌
var accessToken = _jwtService.GenerateAccessToken();
// 返回新的訪問令牌給客戶端
return Ok(new
{
access_token = accessToken
});
}
}
注意事項(xiàng)
-
密鑰管理:在使用JWT時(shí),密鑰是非常重要的,泄露密鑰會(huì)導(dǎo)致安全問題。因此,密鑰的生成、存儲(chǔ)和更新都必須謹(jǐn)慎處理。
-
過期時(shí)間:在生成JWT時(shí),要指定合適的過期時(shí)間,避免JWT過期后仍然可以使用。
-
簽名算法:簽名算法的選擇很重要,不同的簽名算法具有不同的安全性和效率。建議采用HMAC+
-
SHA256或RSA算法。文章來源:http://www.zghlxwxcb.cn/news/detail-420318.html
- 不要存儲(chǔ)敏感信息:JWT雖然安全,但仍然存在被盜用的可能性。因此,在生成JWT時(shí),應(yīng)避免將敏感信息(例如密碼、信用卡號(hào)等)存儲(chǔ)在負(fù)載中。
- 使用HTTPS:在使用JWT時(shí),建議采用HTTPS協(xié)議來保證通訊的安全性。
- 謹(jǐn)慎處理“記住我”功能:在實(shí)現(xiàn)“記住我”功能時(shí),需要謹(jǐn)慎處理,避免密鑰泄露或用戶憑證被盜用。
結(jié)論
在.NET 5 中使用 JWT 進(jìn)行授權(quán)鑒權(quán)是一種安全、可靠的身份驗(yàn)證方式。通過添加 JWT 鑒權(quán)服務(wù)、使用 Authorize 屬性啟用 JWT 授權(quán)、生成和驗(yàn)證 JWT、使用 UseAuthentication 和 UseAuthorization 中間件來啟用身份驗(yàn)證和授權(quán),并為不同的 API 設(shè)置不同的授權(quán)策略,可以輕松地實(shí)現(xiàn) JWT 的授權(quán)鑒權(quán)功能。文章來源地址http://www.zghlxwxcb.cn/news/detail-420318.html
到了這里,關(guān)于ASP.NET CORE WEBAPI 登錄 JWT 鑒權(quán) ,接口權(quán)限驗(yàn)證的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!