ASP.NET 核心 - 将 Cookie 和 JWT 用于 WebAPI
ASP.NET Core - Using both Cookies and JWT for WebAPI
我已经为我的 ASP.NET Core Web API 配置了 JWT 身份验证。使用 Postman 时有效。
我还构建了一个 MVC 管理部分,我想登录它。我遵循的创建管理部分的指南使用 cookie 而不是 JWT 身份验证用于登录页面。
不行,登录后提示401认证错误。它会将我重定向到正确的页面,您可以在浏览器中看到身份 cookie,但我未通过身份验证。
我这里有点力不从心哈哈
我可以同时使用 cookie 和 JWT 身份验证吗? JWT 适用于任何想要访问 WebAPI 的移动 phone 应用程序,但用于通过 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,我们将不胜感激。
我找到了 2 种方法来完成这个: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 操作的 class。本质上,它包含用于生成、验证和解码 JWT 令牌的静态方法。每种方法都有不同的方法,具体取决于图书馆。
仅当默认令牌验证失败时才会调用此事件。
注意 1: 使用此解决方案,您仍然需要在 headers 中包含 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: 这些解决方案可能未准备好生产,甚至不是最好的方法。与一粒盐一起使用。
我已经为我的 ASP.NET Core Web API 配置了 JWT 身份验证。使用 Postman 时有效。
我还构建了一个 MVC 管理部分,我想登录它。我遵循的创建管理部分的指南使用 cookie 而不是 JWT 身份验证用于登录页面。
不行,登录后提示401认证错误。它会将我重定向到正确的页面,您可以在浏览器中看到身份 cookie,但我未通过身份验证。
我这里有点力不从心哈哈
我可以同时使用 cookie 和 JWT 身份验证吗? JWT 适用于任何想要访问 WebAPI 的移动 phone 应用程序,但用于通过 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,我们将不胜感激。
我找到了 2 种方法来完成这个: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 操作的 class。本质上,它包含用于生成、验证和解码 JWT 令牌的静态方法。每种方法都有不同的方法,具体取决于图书馆。
仅当默认令牌验证失败时才会调用此事件。
注意 1: 使用此解决方案,您仍然需要在 headers 中包含 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: 这些解决方案可能未准备好生产,甚至不是最好的方法。与一粒盐一起使用。