我已成功为 ASP.NET Core Web API 配置 JWT 身份验证。它在使用 Postman 时有效。
我还构建了一个 MVC 管理部分,我想登录它。我遵循的创建管理部分的指南在登录页面中使用 cookie,而不是 JWT 身份验证。
不行,登录后出现 401 身份验证错误。它将我重定向到正确的页面,您可以在浏览器中看到身份 cookie,但我未经过身份验证。
我已经超出了我的深度,哈哈
我还可以使用 cookie 以及 JWT 身份验证吗? JWT 适用于任何想要访问 WebAPI 但需要通过 WebAPI 管理页面登录的 Cookie 和 session 的手机应用程序吗?
我的中间件Startup.cs
:
public void ConfigureServices(IServiceCollection services)
{
// Tell Entity how to connect to the SQL Server
services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
});
// Configure Identity
services.Configure<IdentityOptions>(options =>
{
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(10);
options.Lockout.MaxFailedAccessAttempts = 5;
options.Lockout.AllowedForNewUsers = true;
options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
options.SignIn.RequireConfirmedEmail = false; // Set to true for production, test it
options.User.RequireUniqueEmail = false; // Set to true for production
});
services.Configure<PasswordHasherOptions>(options =>
{
// First byte of the hashed password is 0x00 = V2 and 0x01 = V3
options.CompatibilityMode = PasswordHasherCompatibilityMode.IdentityV3; // Default IdentityV2 is used, it uses SHA1 for hashing, 1000 iterations.
options.IterationCount = 12000; // With IdentityV3 we can use SHA256 and 12000 iterations.
});
// We need to add the IdentityUser to Entity and create a token for authentication.
services.AddIdentity<User, IdentityRole>(options =>
{
options.Password.RequireDigit = true;
options.Password.RequireLowercase = true;
options.Password.RequireUppercase = true;
options.Password.RequiredLength = 6;
}).AddEntityFrameworkStores<ApplicationDbContext>().AddDefaultTokenProviders();
// JWT Authentication Tokens
services.AddAuthentication(auth =>
{
// This will stop Identity using Cookies and make it use JWT tokens by default.
auth.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
auth.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidAudience = "http://mywebsite.com",
ValidIssuer = "http://mywebsite.com",
ValidateLifetime = true,
RequireExpirationTime = true,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("rsvgy555262gthsdfrthga"))
};
options.RequireHttpsMetadata = true; // Use HTTPS to transmit the token.
});
// Admin Login Cookie
services.ConfigureApplicationCookie(options =>
{
options.LoginPath = "/Admin/Login"; // Url for users to login to the app
options.Cookie.Name = ".AspNetCore.Identity.Application";
options.ExpireTimeSpan = TimeSpan.FromMinutes(20);
options.SlidingExpiration = true;
});
services.AddControllers();
services.AddControllersWithViews();
}
我的AdminController
:
public class AdminController : Controller
{
private UserManager<User> userManager; // Manage user accounts in DB
private IPasswordHasher<User> passwordHasher; // Hash user passwords
private SignInManager<User> signInManager; // Login
// Constructor
public AdminController(UserManager<User> usrMgr, IPasswordHasher<User> passwordHash, SignInManager<User> signinMgr)
{
userManager = usrMgr;
passwordHasher = passwordHash;
signInManager = signinMgr;
}
// Admin Login Page
[AllowAnonymous]
public IActionResult Login(string returnUrl)
{
Login login = new Login();
return View(login);
}
// Admin Login Module
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(Login login)
{
if (ModelState.IsValid)
{
User loginUser = await userManager.FindByEmailAsync(login.Email);
if (loginUser != null)
{
// Sign out any user already signed in
await signInManager.SignOutAsync();
// Sign in the new user
Microsoft.AspNetCore.Identity.SignInResult result = await signInManager.PasswordSignInAsync(loginUser, login.Password, false, false);
if (result.Succeeded)
{
return Redirect("/Admin"); // Send user to localhost/Admin after login
}
}
ModelState.AddModelError(nameof(login.Email), "Login Failed: Invalid Email or password");
}
return View(login);
}
// Admin Logout
public async Task<IActionResult> Logout()
{
await signInManager.SignOutAsync();
return RedirectToAction("Index");
}
// Admin Index Page
[Authorize]
public IActionResult Index()
{
return View(userManager.Users);
}
}
谢谢,如果您能帮助让 cookie 正常工作,我们将不胜感激。
最佳答案
我发现了两种实现此目的的方法:Token Biased 和 Cookie Biased(首选)。
我使用的是 ASP.NET Core 5.0,顺便说一句,这可能不适用于 3.1。
token 偏向解决方案
services.AddAuthentication(x => {
// Set Jwt Bearer as default auth scheme.
// Token found in Authorization header by default (Authorization: Bearer <JWT_TOKEN>)
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(x => {
x.SaveToken = true;
// Tap into the event lifecycle
x.Events = new JwtBearerEvents {
// Called when default authentication fails
// This is where we validate add cookie authentication
OnAuthenticationFailed = ctx => {
string token = ctx.HttpContext.Request.Cookies["auth"];
if (string.IsNullOrEmpty(token)) {
// Tells ASP.NET that authentication failed
ctx.Fail("Invalid token");
} else {
// Validate token
if (JwtManager.ValidateToken(token, config)) {
// Set the principal
// Will throw error if not set
ctx.Principal = JwtManager.GetPrincipal(token);
// Tells ASP.NET that the authentication was successful
ctx.Success();
// Add the principal to the HttpContext for easy access in any the controllers (only routes that are authenticated)
ctx.HttpContext.Items.Add("claims", principal);
} else {
ctx.Fail("Invalid Token");
}
}
return Task.CompletedTask;
}
};
...
});
JwtManager
只是一个处理 JWT 操作的类。本质上,它拥有用于生成、验证和解码 JWT token 的静态方法。根据库的不同,每种方法都有不同的方法。
仅当默认 token 验证失败时才会调用此事件。
注 1: 使用此解决方案,您仍然需要 Authoritization: Bearer <SOMETHING>
在标题中。
偏向 Cookie 的解决方案
本质上是同一件事,但是在 OnMessageRecieved
中事件
services.AddAuthentication(x => {
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(x => {
x.SaveToken = true;
x.Events = new JwtBearerEvents {
// Same as before, but called before the default JWT bearer authentication
OnMessageReceived = ctx => {
string token = ctx.HttpContext.Request.Cookies["auth"];
if (!string.IsNullOrEmpty(token)) {
if (JwtManager.ValidateToken(token, config)) {
var principal = JwtManager.GetClaims(token);
ctx.Principal = principal;
ctx.Success();
ctx.HttpContext.Items.Add("claims", principal);
}
}
return Task.CompletedTask;
},
};
});
这会在每个请求上调用,因此可能会产生一些开销,但它仍然有效。
注 2: 这些解决方案可能尚未准备好用于生产,甚至不是实现生产的最佳方法。与一粒盐一起使用。
关于c# - ASP.NET Core - 对 WebAPI 使用 Cookie 和 JWT,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67757020/