基于 JWT API + Piranha CMS 劫持 [Authorize] 路由
JWT Based API + Piranha CMS HiJacks the [Authorize] Routes
最近在 Piranha 项目中为 api 设置了 JWT。我可以在没有 Piranha 劫持请求的情况下访问登录端点(匿名)。
当我使用 [Authorize] 属性点击 API 端点(在成功验证并接收 JWT 之后)时,它总是被 Piranha 拾取。它试图将我重定向到 CMS 登录。
因为这是 API 重定向到网页是不可接受的行为。无论如何要纠正这种行为?
var appSettingsSection = config.GetSection("AppSettings");
services.Configure<AppSettings> (appSettingsSection);
// configure jwt authentication
var appSettings = appSettingsSection.Get<AppSettings> ();
var key = Encoding.UTF8.GetBytes (appSettings.Secret); // todo - UTF8 vs ASCII?!
services.AddAuthentication (x => {
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer (x => {
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters {
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey (key),
ValidateIssuer = false,
ValidateAudience = false
};
});
services.AddPiranhaApplication ();
services.AddPiranhaFileStorage ();
services.AddPiranhaImageSharp ();
services.AddPiranhaEF (options =>
options.UseSqlite ("Filename=./piranha.db"));
services.AddPiranhaIdentityWithSeed<IdentitySQLiteDb> (options =>
options.UseSqlite ("Filename=./piranha.db"));
}
services.AddPiranhaManager ();
services.AddPiranhaMemCache ();
services.AddMvc (config => {
config.ModelBinderProviders.Insert (0,
new Piranha.Manager.Binders.AbstractModelBinderProvider ());
}).SetCompatibilityVersion (CompatibilityVersion.Version_2_1);
--------更新--------
在@hakan 的帮助下,以下属性有效:
[ApiController]
[Route ("api/v1/")]
[Produces("application/json")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class ApiController : ControllerBase {
这里的问题实际上是 ASP.NET Identity 如何与 JWT 交互。在您的初创公司中,您的电话:
services.AddPiranhaIdentityWithSeed<IdentitySQLiteDb> (options =>
options.UseSqlite ("Filename=./piranha.db"));
这意味着设置使用 Piranha 设置的默认选项,其中一些选项实际上更适合开发(例如密码强度)。您可以在方法中提供自己的 options
和 cookie options
,如下所示:
services.AddPiranhaIdentityWithSeed<IdentitySQLiteDb> (options =>
options.UseSqlite ("Filename=./piranha.db"), identityOptions, cookieOptions);
使用的默认身份选项是:
// Password settings
options.Password.RequireDigit = false;
options.Password.RequiredLength = 6;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = false;
options.Password.RequireLowercase = false;
options.Password.RequiredUniqueChars = 1;
// Lockout settings
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
options.Lockout.MaxFailedAccessAttempts = 10;
options.Lockout.AllowedForNewUsers = true;
// User settings
options.User.RequireUniqueEmail = true;
这些是默认的 cookie 选项:
options.Cookie.HttpOnly = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
options.LoginPath = "/manager/login";
options.AccessDeniedPath = "/manager/login";
options.SlidingExpiration = true;
此致
哈坎
如果您仍想将 Cookie Auth 与您的 API 端点一起使用,下面的内容可能会起作用,但有一些注意事项。要进行设置,请执行以下操作:
- 提供每个 Håkan's post 的默认选项,根据需要更新
- 为
cookieOptions.Events.OnRedirectToLogin
添加自定义处理程序。
注意事项:
- .NET Core 仍然会 re-direct 如果响应没有 body.
- 如果您使用的是 COR,则 headers 不再自动发送,因此您需要弄清楚如何执行此操作。
- 我在我的前端项目中使用 Axios 客户端。默认情况下,即使
withCredentials
参数设置为 true
. ,axios 也不会发送带有 PUT/DELETE 请求的 cookie
identityOptions =>
{
// Password settings
identityOptions.Password.RequireDigit = false;
identityOptions.Password.RequiredLength = 6;
identityOptions.Password.RequireNonAlphanumeric = false;
identityOptions.Password.RequireUppercase = false;
identityOptions.Password.RequireLowercase = false;
identityOptions.Password.RequiredUniqueChars = 1;
// Lockout settings
identityOptions.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
identityOptions.Lockout.MaxFailedAccessAttempts = 10;
identityOptions.Lockout.AllowedForNewUsers = true;
// User settings
identityOptions.User.RequireUniqueEmail = true;
},
cookieOptions =>
{
cookieOptions.Cookie.HttpOnly = true;
cookieOptions.ExpireTimeSpan = TimeSpan.FromMinutes(30);
cookieOptions.LoginPath = "/manager/login";
cookieOptions.AccessDeniedPath = "/manager/login";
cookieOptions.SlidingExpiration = true;
var defaultAction = cookieOptions.Events.OnRedirectToLogin;
cookieOptions.Events.OnRedirectToLogin = (context) =>
{
if (context.Request.Path.Value.StartsWith("/api"))
{
context.Response.StatusCode = 401;
context.Response.BodyWriter.WriteAsync(new ReadOnlyMemory<byte>(Encoding.ASCII.GetBytes("unauthorized.")));
return Task.CompletedTask;
}
else
{
return defaultAction(context);
}
};
});
最近在 Piranha 项目中为 api 设置了 JWT。我可以在没有 Piranha 劫持请求的情况下访问登录端点(匿名)。
当我使用 [Authorize] 属性点击 API 端点(在成功验证并接收 JWT 之后)时,它总是被 Piranha 拾取。它试图将我重定向到 CMS 登录。
因为这是 API 重定向到网页是不可接受的行为。无论如何要纠正这种行为?
var appSettingsSection = config.GetSection("AppSettings");
services.Configure<AppSettings> (appSettingsSection);
// configure jwt authentication
var appSettings = appSettingsSection.Get<AppSettings> ();
var key = Encoding.UTF8.GetBytes (appSettings.Secret); // todo - UTF8 vs ASCII?!
services.AddAuthentication (x => {
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer (x => {
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters {
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey (key),
ValidateIssuer = false,
ValidateAudience = false
};
});
services.AddPiranhaApplication ();
services.AddPiranhaFileStorage ();
services.AddPiranhaImageSharp ();
services.AddPiranhaEF (options =>
options.UseSqlite ("Filename=./piranha.db"));
services.AddPiranhaIdentityWithSeed<IdentitySQLiteDb> (options =>
options.UseSqlite ("Filename=./piranha.db"));
}
services.AddPiranhaManager ();
services.AddPiranhaMemCache ();
services.AddMvc (config => {
config.ModelBinderProviders.Insert (0,
new Piranha.Manager.Binders.AbstractModelBinderProvider ());
}).SetCompatibilityVersion (CompatibilityVersion.Version_2_1);
--------更新-------- 在@hakan 的帮助下,以下属性有效:
[ApiController]
[Route ("api/v1/")]
[Produces("application/json")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class ApiController : ControllerBase {
这里的问题实际上是 ASP.NET Identity 如何与 JWT 交互。在您的初创公司中,您的电话:
services.AddPiranhaIdentityWithSeed<IdentitySQLiteDb> (options =>
options.UseSqlite ("Filename=./piranha.db"));
这意味着设置使用 Piranha 设置的默认选项,其中一些选项实际上更适合开发(例如密码强度)。您可以在方法中提供自己的 options
和 cookie options
,如下所示:
services.AddPiranhaIdentityWithSeed<IdentitySQLiteDb> (options =>
options.UseSqlite ("Filename=./piranha.db"), identityOptions, cookieOptions);
使用的默认身份选项是:
// Password settings
options.Password.RequireDigit = false;
options.Password.RequiredLength = 6;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = false;
options.Password.RequireLowercase = false;
options.Password.RequiredUniqueChars = 1;
// Lockout settings
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
options.Lockout.MaxFailedAccessAttempts = 10;
options.Lockout.AllowedForNewUsers = true;
// User settings
options.User.RequireUniqueEmail = true;
这些是默认的 cookie 选项:
options.Cookie.HttpOnly = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
options.LoginPath = "/manager/login";
options.AccessDeniedPath = "/manager/login";
options.SlidingExpiration = true;
此致
哈坎
如果您仍想将 Cookie Auth 与您的 API 端点一起使用,下面的内容可能会起作用,但有一些注意事项。要进行设置,请执行以下操作:
- 提供每个 Håkan's post 的默认选项,根据需要更新
- 为
cookieOptions.Events.OnRedirectToLogin
添加自定义处理程序。
注意事项:
- .NET Core 仍然会 re-direct 如果响应没有 body.
- 如果您使用的是 COR,则 headers 不再自动发送,因此您需要弄清楚如何执行此操作。
- 我在我的前端项目中使用 Axios 客户端。默认情况下,即使
withCredentials
参数设置为true
. ,axios 也不会发送带有 PUT/DELETE 请求的 cookie
identityOptions =>
{
// Password settings
identityOptions.Password.RequireDigit = false;
identityOptions.Password.RequiredLength = 6;
identityOptions.Password.RequireNonAlphanumeric = false;
identityOptions.Password.RequireUppercase = false;
identityOptions.Password.RequireLowercase = false;
identityOptions.Password.RequiredUniqueChars = 1;
// Lockout settings
identityOptions.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
identityOptions.Lockout.MaxFailedAccessAttempts = 10;
identityOptions.Lockout.AllowedForNewUsers = true;
// User settings
identityOptions.User.RequireUniqueEmail = true;
},
cookieOptions =>
{
cookieOptions.Cookie.HttpOnly = true;
cookieOptions.ExpireTimeSpan = TimeSpan.FromMinutes(30);
cookieOptions.LoginPath = "/manager/login";
cookieOptions.AccessDeniedPath = "/manager/login";
cookieOptions.SlidingExpiration = true;
var defaultAction = cookieOptions.Events.OnRedirectToLogin;
cookieOptions.Events.OnRedirectToLogin = (context) =>
{
if (context.Request.Path.Value.StartsWith("/api"))
{
context.Response.StatusCode = 401;
context.Response.BodyWriter.WriteAsync(new ReadOnlyMemory<byte>(Encoding.ASCII.GetBytes("unauthorized.")));
return Task.CompletedTask;
}
else
{
return defaultAction(context);
}
};
});