自定义身份验证过滤器问题 asp.net 核心
Issue with custom Authentication filter asp.net core
我正在尝试在 ASP.NET Core 中创建自定义身份验证过滤器。需要在控制器中使用它来验证提供给我的 JWT 并创建 Claim Principal。但是,当我将身份验证标签放在控制器上方时,什么也没有发生,控制器在没有身份验证的情况下得到处理。
以下是已完成的步骤:
- 在
下的 startup.cs 中添加了 app.UseAuthentication()
Configure(IApplicationBuilder app, IHostingEnvironment env)
{
......
......
app.UseAuthentication();
}
- 在同一项目中创建了一个新的 class 文件 ProcessAuth,其中包含 AuthenticationAsync 和 ChallengeAsync
public class ProcessAuth : Attribute, IAuthenticationFilter
{
public bool AllowMultiple { get { return false; } }
public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
HttpRequestMessage request = context.Request;
AuthenticationHeaderValue authorization = request.Headers.Authorization;
// More code to be added for validating the JWT
}
public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
{
throw new NotImplementedException(); //sample code
}
}
- 在控制器中添加了对这个新文件的引用
- 将标签 [ProcessAuth] 放在控制器的顶部
[ProcessAuth]
[Route("api/[controller]")]
[ApiController]
- 使用 Postman 发送 JSON 数据,以及包含有效 JWT 令牌作为“Bearer”的授权 Header
- 代码只是忽略了控制器中的过滤器和处理代码,returns结果
更多信息:
如果我将 [Authorize]
添加到控制器,Postman 只需 returns a 401 Unauthorized error
也检查了 this URL,但找不到问题。
更新: 我检查了类似 Stack Overflow 问题的答案并遵循了相同的答案,但问题仍然存在。
安装的 Nuget 包:
Microsoft.AspNet.WebApi.Core 还有 Microsoft.AspNet.WebApi
使用的命名空间:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http.Filters;
using System.Web.Http.Controllers;
如何让它工作?我错过了什么吗?
根据您的代码,我发现您在 asp.net 核心应用程序中使用了 asp.net 身份验证过滤器。这是行不通的。
在asp.net核心中,我们应该使用JWT bear认证中间件来实现你的要求。
您可以创建自定义 OnChallenge 来验证 jwt 令牌和 OnTokenValidated 来添加声明。
更多详情,您可以参考以下代码:
services.AddAuthentication(auth =>
{
auth.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
auth.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(token =>
{
token.RequireHttpsMetadata = false;
token.SaveToken = true;
token.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
//Same Secret key will be used while creating the token
IssuerSigningKey = new SymmetricSecurityKey(SecretKey),
ValidateIssuer = true,
//Usually, this is your application base URL
ValidIssuer = "http://localhost:45092/",
ValidateAudience = true,
//Here, we are creating and using JWT within the same application.
//In this case, base URL is fine.
//If the JWT is created using a web service, then this would be the consumer URL.
ValidAudience = "http://localhost:45092/",
RequireExpirationTime = true,
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
};
token.Events = new JwtBearerEvents {
OnChallenge = async ctx => {
},
OnTokenValidated = async ctx =>
{
//Get the calling app client id that came from the token produced by Azure AD
string clientId = ctx.Principal.FindFirstValue("appid");
//Get EF context
//var db = ctx.HttpContext.RequestServices.GetRequiredService<AuthorizationDbContext>();
//Check if this app can read confidential items
bool canReadConfidentialItems = await db.Applications.AnyAsync(a => a.ClientId == clientId && a.ReadConfidentialItems);
if (canReadConfidentialItems)
{
//Add claim if yes
var claims = new List<Claim>
{
new Claim("ConfidentialAccess", "true")
};
var appIdentity = new ClaimsIdentity(claims);
ctx.Principal.AddIdentity(appIdentity);
}
}
};
});
编辑:
您可以像下面这样创建 AuthenticationHandler 和 AuthenticationSchemeOptions class 并在 startup.cs 中注册 class。然后你可以使用 [Authorize(AuthenticationSchemes = "Test")]
来设置特殊的 AuthenticationSchemes。
更多细节,您可以参考下面的代码示例:
public class ValidateHashAuthenticationSchemeOptions : AuthenticationSchemeOptions
{
}
public class ValidateHashAuthenticationHandler
: AuthenticationHandler<ValidateHashAuthenticationSchemeOptions>
{
public ValidateHashAuthenticationHandler(
IOptionsMonitor<ValidateHashAuthenticationSchemeOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock)
: base(options, logger, encoder, clock)
{
}
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
//TokenModel model;
// validation comes in here
if (!Request.Headers.ContainsKey("X-Base-Token"))
{
return Task.FromResult(AuthenticateResult.Fail("Header Not Found."));
}
var token = Request.Headers["X-Base-Token"].ToString();
try
{
// convert the input string into byte stream
using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(token)))
{
// deserialize stream into token model object
//model = Serializer.Deserialize<TokenModel>(stream);
}
}
catch (System.Exception ex)
{
Console.WriteLine("Exception Occured while Deserializing: " + ex);
return Task.FromResult(AuthenticateResult.Fail("TokenParseException"));
}
//if (model != null)
//{
// // success case AuthenticationTicket generation
// // happens from here
// // create claims array from the model
// var claims = new[] {
// new Claim(ClaimTypes.NameIdentifier, model.UserId.ToString()),
// new Claim(ClaimTypes.Email, model.EmailAddress),
// new Claim(ClaimTypes.Name, model.Name) };
// // generate claimsIdentity on the name of the class
// var claimsIdentity = new ClaimsIdentity(claims,
// nameof(ValidateHashAuthenticationHandler));
// // generate AuthenticationTicket from the Identity
// // and current authentication scheme
// var ticket = new AuthenticationTicket(
// new ClaimsPrincipal(claimsIdentity), this.Scheme.Name);
// // pass on the ticket to the middleware
// return Task.FromResult(AuthenticateResult.Success(ticket));
//}
return Task.FromResult(AuthenticateResult.Fail("Model is Empty"));
}
}
public class TokenModel
{
public int UserId { get; set; }
public string Name { get; set; }
public string EmailAddress { get; set; }
}
Startup.cs 将以下代码添加到 ConfigureServices 方法中:
services.AddAuthentication(options =>
{
options.DefaultScheme
= "Test";
})
.AddScheme<ValidateHashAuthenticationSchemeOptions, ValidateHashAuthenticationHandler>
("Test", null);
控制器:
[Authorize(AuthenticationSchemes = "Test")]
我正在尝试在 ASP.NET Core 中创建自定义身份验证过滤器。需要在控制器中使用它来验证提供给我的 JWT 并创建 Claim Principal。但是,当我将身份验证标签放在控制器上方时,什么也没有发生,控制器在没有身份验证的情况下得到处理。
以下是已完成的步骤:
- 在 下的 startup.cs 中添加了 app.UseAuthentication()
Configure(IApplicationBuilder app, IHostingEnvironment env)
{
......
......
app.UseAuthentication();
}
- 在同一项目中创建了一个新的 class 文件 ProcessAuth,其中包含 AuthenticationAsync 和 ChallengeAsync
public class ProcessAuth : Attribute, IAuthenticationFilter
{
public bool AllowMultiple { get { return false; } }
public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
HttpRequestMessage request = context.Request;
AuthenticationHeaderValue authorization = request.Headers.Authorization;
// More code to be added for validating the JWT
}
public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
{
throw new NotImplementedException(); //sample code
}
}
- 在控制器中添加了对这个新文件的引用
- 将标签 [ProcessAuth] 放在控制器的顶部
[ProcessAuth]
[Route("api/[controller]")]
[ApiController]
- 使用 Postman 发送 JSON 数据,以及包含有效 JWT 令牌作为“Bearer”的授权 Header
- 代码只是忽略了控制器中的过滤器和处理代码,returns结果
更多信息:
如果我将 [Authorize]
添加到控制器,Postman 只需 returns a 401 Unauthorized error
也检查了 this URL,但找不到问题。
更新: 我检查了类似 Stack Overflow 问题的答案并遵循了相同的答案,但问题仍然存在。
安装的 Nuget 包: Microsoft.AspNet.WebApi.Core 还有 Microsoft.AspNet.WebApi
使用的命名空间:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http.Filters;
using System.Web.Http.Controllers;
如何让它工作?我错过了什么吗?
根据您的代码,我发现您在 asp.net 核心应用程序中使用了 asp.net 身份验证过滤器。这是行不通的。
在asp.net核心中,我们应该使用JWT bear认证中间件来实现你的要求。
您可以创建自定义 OnChallenge 来验证 jwt 令牌和 OnTokenValidated 来添加声明。
更多详情,您可以参考以下代码:
services.AddAuthentication(auth =>
{
auth.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
auth.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(token =>
{
token.RequireHttpsMetadata = false;
token.SaveToken = true;
token.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
//Same Secret key will be used while creating the token
IssuerSigningKey = new SymmetricSecurityKey(SecretKey),
ValidateIssuer = true,
//Usually, this is your application base URL
ValidIssuer = "http://localhost:45092/",
ValidateAudience = true,
//Here, we are creating and using JWT within the same application.
//In this case, base URL is fine.
//If the JWT is created using a web service, then this would be the consumer URL.
ValidAudience = "http://localhost:45092/",
RequireExpirationTime = true,
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
};
token.Events = new JwtBearerEvents {
OnChallenge = async ctx => {
},
OnTokenValidated = async ctx =>
{
//Get the calling app client id that came from the token produced by Azure AD
string clientId = ctx.Principal.FindFirstValue("appid");
//Get EF context
//var db = ctx.HttpContext.RequestServices.GetRequiredService<AuthorizationDbContext>();
//Check if this app can read confidential items
bool canReadConfidentialItems = await db.Applications.AnyAsync(a => a.ClientId == clientId && a.ReadConfidentialItems);
if (canReadConfidentialItems)
{
//Add claim if yes
var claims = new List<Claim>
{
new Claim("ConfidentialAccess", "true")
};
var appIdentity = new ClaimsIdentity(claims);
ctx.Principal.AddIdentity(appIdentity);
}
}
};
});
编辑:
您可以像下面这样创建 AuthenticationHandler 和 AuthenticationSchemeOptions class 并在 startup.cs 中注册 class。然后你可以使用 [Authorize(AuthenticationSchemes = "Test")]
来设置特殊的 AuthenticationSchemes。
更多细节,您可以参考下面的代码示例:
public class ValidateHashAuthenticationSchemeOptions : AuthenticationSchemeOptions
{
}
public class ValidateHashAuthenticationHandler
: AuthenticationHandler<ValidateHashAuthenticationSchemeOptions>
{
public ValidateHashAuthenticationHandler(
IOptionsMonitor<ValidateHashAuthenticationSchemeOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock)
: base(options, logger, encoder, clock)
{
}
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
//TokenModel model;
// validation comes in here
if (!Request.Headers.ContainsKey("X-Base-Token"))
{
return Task.FromResult(AuthenticateResult.Fail("Header Not Found."));
}
var token = Request.Headers["X-Base-Token"].ToString();
try
{
// convert the input string into byte stream
using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(token)))
{
// deserialize stream into token model object
//model = Serializer.Deserialize<TokenModel>(stream);
}
}
catch (System.Exception ex)
{
Console.WriteLine("Exception Occured while Deserializing: " + ex);
return Task.FromResult(AuthenticateResult.Fail("TokenParseException"));
}
//if (model != null)
//{
// // success case AuthenticationTicket generation
// // happens from here
// // create claims array from the model
// var claims = new[] {
// new Claim(ClaimTypes.NameIdentifier, model.UserId.ToString()),
// new Claim(ClaimTypes.Email, model.EmailAddress),
// new Claim(ClaimTypes.Name, model.Name) };
// // generate claimsIdentity on the name of the class
// var claimsIdentity = new ClaimsIdentity(claims,
// nameof(ValidateHashAuthenticationHandler));
// // generate AuthenticationTicket from the Identity
// // and current authentication scheme
// var ticket = new AuthenticationTicket(
// new ClaimsPrincipal(claimsIdentity), this.Scheme.Name);
// // pass on the ticket to the middleware
// return Task.FromResult(AuthenticateResult.Success(ticket));
//}
return Task.FromResult(AuthenticateResult.Fail("Model is Empty"));
}
}
public class TokenModel
{
public int UserId { get; set; }
public string Name { get; set; }
public string EmailAddress { get; set; }
}
Startup.cs 将以下代码添加到 ConfigureServices 方法中:
services.AddAuthentication(options =>
{
options.DefaultScheme
= "Test";
})
.AddScheme<ValidateHashAuthenticationSchemeOptions, ValidateHashAuthenticationHandler>
("Test", null);
控制器:
[Authorize(AuthenticationSchemes = "Test")]