ASP.NET Core
.NET Framework 中一套用于生成 Web 应用程序和 XML Web 服务的技术。
31 个问题
您好,
我正在学习用户身份的验证和授权。
我正在开发一个简单的Web API程序来学习。
我了解到MapIdentityApi这个方法可以很方便的提供很多默认的endpoint。
我想了解如何扩展其中的login和refresh端点。
login端点在登录后会返回Access Token和Refresh Token。
但Refresh Token没有被设置为HttpOnly Cookie(是否有必要设置成HttpOnly Cookie?)。
同样的,refresh端点接收前端Post请求里携带的Refresh Token,而不是从Cookie获取。
谢谢。
Hi @Yishen Yang,
对于您的问题可以有两种方式来实现,一个是添加自定义的http请求接口,另一个是扩展IdentityApiExtensions
。
我这里简单的对IdentityApiExtensions
进行了扩展,仅供您学习与参考,如果要应用正式环境的站点的话,建议进行安全评估与测试。
using Microsoft.AspNetCore.Identity;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
namespace IdentityApiDemo
{
public static class IdentityApiExtensions
{
public static IEndpointRouteBuilder MapCustomIdentityApi(this IEndpointRouteBuilder endpoints)
{
endpoints.MapPost("/login", async context =>
{
var userManager = context.RequestServices.GetRequiredService<UserManager<IdentityUser>>();
var signInManager = context.RequestServices.GetRequiredService<SignInManager<IdentityUser>>();
var jwtKey = "YourJwtSecretKey"; // 请使用安全的密钥
var jwtIssuer = "YourIssuer"; // 可选
// 从请求中获取用户名和密码
var form = await context.Request.ReadFormAsync();
var username = form["username"];
var password = form["password"];
var user = await userManager.FindByNameAsync(username);
if (user != null && await userManager.CheckPasswordAsync(user, password))
{
var accessToken = GenerateAccessToken(user, jwtKey, jwtIssuer);
var refreshToken = GenerateRefreshToken();
context.Response.Cookies.Append("RefreshToken", refreshToken, new CookieOptions
{
HttpOnly = true,
Secure = true,
SameSite = SameSiteMode.Strict,
Expires = System.DateTimeOffset.UtcNow.AddDays(7)
});
await context.Response.WriteAsJsonAsync(new { AccessToken = accessToken });
return;
}
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
});
// 添加自定义的刷新端点
endpoints.MapPost("/refresh", async context =>
{
var userManager = context.RequestServices.GetRequiredService<UserManager<IdentityUser>>();
if (context.Request.Cookies.TryGetValue("RefreshToken", out var refreshToken))
{
var userId = GetUserIdFromRefreshToken(refreshToken);
if (userId != null)
{
var user = await userManager.FindByIdAsync(userId);
if (user != null)
{
var jwtKey = "YourJwtSecretKey"; // 请使用安全的密钥
var jwtIssuer = "YourIssuer"; // 可选
var newAccessToken = GenerateAccessToken(user, jwtKey, jwtIssuer);
var newRefreshToken = GenerateRefreshToken();
context.Response.Cookies.Append("RefreshToken", newRefreshToken, new CookieOptions
{
HttpOnly = true,
Secure = true,
SameSite = SameSiteMode.Strict,
Expires = System.DateTimeOffset.UtcNow.AddDays(7)
});
await context.Response.WriteAsJsonAsync(new { AccessToken = newAccessToken });
return;
}
}
}
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
});
return endpoints;
}
private static string GenerateAccessToken(IdentityUser user, string jwtKey, string jwtIssuer)
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.UTF8.GetBytes(jwtKey);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.NameIdentifier, user.Id),
new Claim(ClaimTypes.Name, user.UserName)
}),
Expires = System.DateTime.UtcNow.AddMinutes(30),
Issuer = jwtIssuer,
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
private static string GenerateRefreshToken()
{
return System.Guid.NewGuid().ToString();
}
private static string GetUserIdFromRefreshToken(string refreshToken)
{
return "testuser-id"; // 请替换为实际的用户 ID
}
}
}
注册(中间件顺序很重要):
app.UseAuthorization();
app.MapCustomIdentityApi();
app.MapControllers();
...
关于扩展方法里面细节,需要您自己去实现,扩展完成后,可以在写代码的时候,直接调用自定义的扩展方法。
Best Regards
Jason