如何解决ASP.NET Core - 将 Cookie 和 JWT 用于 WebAPI
我已经为我的 ASP.NET Core Web API 配置了 JWT 身份验证。它在使用 Postman 时有效。
我还构建了一个 MVC 管理部分,我想登录该部分。我遵循的创建管理部分的指南使用 cookie 而不是 JWT 身份验证用于登录页面。
它不起作用,我在登录后收到 401 身份验证错误。它会将我重定向到正确的页面,您可以在浏览器中看到身份 cookie,但我没有通过身份验证。
我在这里的深度不够哈哈
我还可以使用 cookie 和 JWT 身份验证吗? JWT 适用于任何想要访问 WebAPI 但需要通过 WebAPI 的管理页面登录的 Cookie 和会话的手机应用程序?
我的中间件 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。
代币偏向解决方案
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 令牌的静态方法。每种方法都有不同的方法,具体取决于图书馆。
此事件仅在默认令牌验证失败时调用。
注意 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:这些解决方案可能不是生产就绪的,甚至不是最好的方法。与一粒盐一起使用。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。