ASP Core 3.0 API 令牌自定义令牌认证(不是 jwt!)
ASP Core 3.0 API Token Custom Token Authentication (not jwt!)
我们有一个 ASP CORE 3 API 项目,我们需要使用 API 令牌来保护它。这些 API 令牌将从数据库中配置和加载,但作为概念证明,我们将硬编码以进行测试。我们为令牌授权查看的所有内容都指的是 JWT。我们不想使用 JWT。我们只需提供 API 允许访问我们的 API 的密钥 - 然后用户可以通过在 header 中传递令牌来调用 API 方法,例如X-CUSTOM-TOKEN:abcdefg。
如何修改 startup.cs 和管道,以便在每次请求时都检查此 X-CUSTOM-TOKEN header?一个简单的指向正确方向的观点会很棒。
编辑:好的,这看起来是个不错的开始!太感谢了!
您的示例似乎表明用户 API 令牌是用户令牌。我们的要求是我们需要一个 API 密钥来使用 API,然后还需要一个用户令牌来调用某些控制器。
示例:
myapi.com/Auth/SSO(通过API Token和用户信息登录,returns用户信息+用户Token)
myapi.com/Schedule/Create(需要 API 令牌 header 和带有用户令牌的 header)
您能否建议如何修改您的代码以支持此功能?
您可以创建您的自定义中间件来检查 header 并验证令牌的值,然后您只需将它注入您的中间件管道,我认为 this is 您需要什么。
您可以为此场景创建自定义身份验证方案,因为已经有一个内置的 Authentication
中间件。此外,自定义身份验证方案允许您与 built-in authentication/authorization 子系统集成。您不必实施自己的 challenge/forbid 逻辑。
例如,创建如下处理程序和选项:
public class MyCustomTokenAuthOptions : AuthenticationSchemeOptions
{
public const string DefaultScemeName= "MyCustomTokenAuthenticationScheme";
public string TokenHeaderName{get;set;}= "X-CUSTOM-TOKEN";
}
public class MyCustomTokenAuthHandler : AuthenticationHandler<MyCustomTokenAuthOptions>
{
public MyCustomTokenAuthHandler(IOptionsMonitor<MyCustomTokenAuthOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
: base(options, logger, encoder, clock) { }
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!Request.Headers.ContainsKey(Options.TokenHeaderName))
return Task.FromResult(AuthenticateResult.Fail($"Missing Header For Token: {Options.TokenHeaderName}"));
var token = Request.Headers[Options.TokenHeaderName];
// get username from db or somewhere else accordining to this token
var username= "Username-From-Somewhere-By-Token";
var claims = new[] {
new Claim(ClaimTypes.NameIdentifier, username),
new Claim(ClaimTypes.Name, username),
// add other claims/roles as you like
};
var id = new ClaimsIdentity(claims, Scheme.Name);
var principal = new ClaimsPrincipal(id);
var ticket = new AuthenticationTicket(principal, Scheme.Name);
return Task.FromResult(AuthenticateResult.Success(ticket));
}
}
然后在您的启动中配置此身份验证方案:
services.AddAuthentication(MyCustomTokenAuthOptions.DefaultScemeName)
.AddScheme<MyCustomTokenAuthOptions,MyCustomTokenAuthHandler>(
MyCustomTokenAuthOptions.DefaultScemeName,
opts =>{
// you can change the token header name here by :
// opts.TokenHeaderName = "X-Custom-Token-Header";
}
);
另外不要忘记在Configure(IApplicationBuilder app, IWebHostEnvironment env)
方法中启用Authentication
中间件:
app.UseRouting();
app.UseAuthentication(); // add this line, the order is important
app.UseAuthorization();
app.UseEndpoints(endpoints =>{ ... });
最后,像这样保护您的端点:
[Authorize(AuthenticationSchemes=MyCustomTokenAuthOptions.DefaultScemeName)]
public IActionResult ScretApi()
{
return new JsonResult(...);
}
或直接使用 Authorize(),因为我们已将 MyCustomTokenAuth Scheme 设置为默认身份验证方案:
[Authorize()]
public IActionResult ScretApi()
{
return new JsonResult(...);
}
[编辑]:
Our requirements are that we need an API Key to use the API, and then also a User Token to call certain controllers.
好的。假设我们有一个 TokenChecker
检查 api 密钥并且令牌是正确的(由于我不知道具体的业务逻辑,我只是 return true
这里) :
public static class TokenChecker{
public static Task<bool> CheckApiKey(StringValues apiKey) {
return Task.FromResult(true);// ... return true/false according to the business
}
public static Task<bool> CheckToken(StringValues userToken) {
return Task.FromResult(true);// ... return true/false according to the business
}
}
并更改上述身份验证方案以检查 ApiKey 和 UserToken header,如下所示:
public class MyCustomTokenAuthOptions : AuthenticationSchemeOptions
{
public const string DefaultScemeName= "MyCustomTokenAuthenticationScheme";
public string ApiKeyHeaderName{get;set;}= "X-Api-Key";
public string UserTokenHeaderName{get;set;}= "X-User-Token";
}
public class MyCustomTokenAuthHandler : AuthenticationHandler<MyCustomTokenAuthOptions>
{
public MyCustomTokenAuthHandler(IOptionsMonitor<MyCustomTokenAuthOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
: base(options, logger, encoder, clock) { }
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!Request.Headers.ContainsKey(Options.ApiKeyHeaderName))
return AuthenticateResult.Fail($"Missing Header For Token: {Options.ApiKeyHeaderName}");
if (!Request.Headers.ContainsKey(Options.UserTokenHeaderName))
return AuthenticateResult.Fail($"Missing Header For Token: {Options.UserTokenHeaderName}");
var apiKey= Request.Headers[Options.ApiKeyHeaderName];
var userToken = Request.Headers[Options.UserTokenHeaderName];
var succeeded= await TokenChecker.CheckToken(userToken) && await TokenChecker.CheckApiKey(apiKey);
if(!succeeded ){ return AuthenticateResult.Fail("incorrect ApiKey or UserToken"); }
var username = "the-username-from-user-token"; //e.g. decode the userToken header
var claims = new[] {
new Claim(ClaimTypes.NameIdentifier, username),
new Claim(ClaimTypes.Name, username),
// add other claims/roles as you like
};
var id = new ClaimsIdentity(claims, Scheme.Name);
var principal = new ClaimsPrincipal(id);
var ticket = new AuthenticationTicket(principal, Scheme.Name);
return AuthenticateResult.Success(ticket);
}
}
并将您的 Auth/SSO 端点更改为 return 用户令牌:
public class AuthController: Controller
{
private readonly MyCustomTokenAuthOptions _myCustomAuthOpts;
// inject the options so that we can know the actual header name
public AuthController(IOptionsMonitor<MyCustomTokenAuthOptions> options)
{
this._myCustomAuthOpts= options.CurrentValue;
}
[HttpPost("/Auth/SSO")]
public async System.Threading.Tasks.Task<IActionResult> CreateUserTokenAsync()
{
var apiKeyHeaderName =_myCustomAuthOpts.ApiKeyHeaderName ;
if (!Request.Headers.ContainsKey(apiKeyHeaderName))
return BadRequest($"Missing Header For Token: {apiKeyHeaderName}");
// check key
var succeeded = await TokenChecker.CheckApiKey(Request.Headers[apiKeyHeaderName]);
if(!succeeded)
return BadRequest($"Incorrect Api Key");
return Json(... {userInfo, apiKey} ... );
}
}
我们有一个 ASP CORE 3 API 项目,我们需要使用 API 令牌来保护它。这些 API 令牌将从数据库中配置和加载,但作为概念证明,我们将硬编码以进行测试。我们为令牌授权查看的所有内容都指的是 JWT。我们不想使用 JWT。我们只需提供 API 允许访问我们的 API 的密钥 - 然后用户可以通过在 header 中传递令牌来调用 API 方法,例如X-CUSTOM-TOKEN:abcdefg。
如何修改 startup.cs 和管道,以便在每次请求时都检查此 X-CUSTOM-TOKEN header?一个简单的指向正确方向的观点会很棒。
编辑:好的,这看起来是个不错的开始!太感谢了!
您的示例似乎表明用户 API 令牌是用户令牌。我们的要求是我们需要一个 API 密钥来使用 API,然后还需要一个用户令牌来调用某些控制器。
示例: myapi.com/Auth/SSO(通过API Token和用户信息登录,returns用户信息+用户Token)
myapi.com/Schedule/Create(需要 API 令牌 header 和带有用户令牌的 header)
您能否建议如何修改您的代码以支持此功能?
您可以创建您的自定义中间件来检查 header 并验证令牌的值,然后您只需将它注入您的中间件管道,我认为 this is 您需要什么。
您可以为此场景创建自定义身份验证方案,因为已经有一个内置的 Authentication
中间件。此外,自定义身份验证方案允许您与 built-in authentication/authorization 子系统集成。您不必实施自己的 challenge/forbid 逻辑。
例如,创建如下处理程序和选项:
public class MyCustomTokenAuthOptions : AuthenticationSchemeOptions
{
public const string DefaultScemeName= "MyCustomTokenAuthenticationScheme";
public string TokenHeaderName{get;set;}= "X-CUSTOM-TOKEN";
}
public class MyCustomTokenAuthHandler : AuthenticationHandler<MyCustomTokenAuthOptions>
{
public MyCustomTokenAuthHandler(IOptionsMonitor<MyCustomTokenAuthOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
: base(options, logger, encoder, clock) { }
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!Request.Headers.ContainsKey(Options.TokenHeaderName))
return Task.FromResult(AuthenticateResult.Fail($"Missing Header For Token: {Options.TokenHeaderName}"));
var token = Request.Headers[Options.TokenHeaderName];
// get username from db or somewhere else accordining to this token
var username= "Username-From-Somewhere-By-Token";
var claims = new[] {
new Claim(ClaimTypes.NameIdentifier, username),
new Claim(ClaimTypes.Name, username),
// add other claims/roles as you like
};
var id = new ClaimsIdentity(claims, Scheme.Name);
var principal = new ClaimsPrincipal(id);
var ticket = new AuthenticationTicket(principal, Scheme.Name);
return Task.FromResult(AuthenticateResult.Success(ticket));
}
}
然后在您的启动中配置此身份验证方案:
services.AddAuthentication(MyCustomTokenAuthOptions.DefaultScemeName)
.AddScheme<MyCustomTokenAuthOptions,MyCustomTokenAuthHandler>(
MyCustomTokenAuthOptions.DefaultScemeName,
opts =>{
// you can change the token header name here by :
// opts.TokenHeaderName = "X-Custom-Token-Header";
}
);
另外不要忘记在Configure(IApplicationBuilder app, IWebHostEnvironment env)
方法中启用Authentication
中间件:
app.UseRouting();
app.UseAuthentication(); // add this line, the order is important
app.UseAuthorization();
app.UseEndpoints(endpoints =>{ ... });
最后,像这样保护您的端点:
[Authorize(AuthenticationSchemes=MyCustomTokenAuthOptions.DefaultScemeName)]
public IActionResult ScretApi()
{
return new JsonResult(...);
}
或直接使用 Authorize(),因为我们已将 MyCustomTokenAuth Scheme 设置为默认身份验证方案:
[Authorize()]
public IActionResult ScretApi()
{
return new JsonResult(...);
}
[编辑]:
Our requirements are that we need an API Key to use the API, and then also a User Token to call certain controllers.
好的。假设我们有一个 TokenChecker
检查 api 密钥并且令牌是正确的(由于我不知道具体的业务逻辑,我只是 return true
这里) :
public static class TokenChecker{
public static Task<bool> CheckApiKey(StringValues apiKey) {
return Task.FromResult(true);// ... return true/false according to the business
}
public static Task<bool> CheckToken(StringValues userToken) {
return Task.FromResult(true);// ... return true/false according to the business
}
}
并更改上述身份验证方案以检查 ApiKey 和 UserToken header,如下所示:
public class MyCustomTokenAuthOptions : AuthenticationSchemeOptions
{
public const string DefaultScemeName= "MyCustomTokenAuthenticationScheme";
public string ApiKeyHeaderName{get;set;}= "X-Api-Key";
public string UserTokenHeaderName{get;set;}= "X-User-Token";
}
public class MyCustomTokenAuthHandler : AuthenticationHandler<MyCustomTokenAuthOptions>
{
public MyCustomTokenAuthHandler(IOptionsMonitor<MyCustomTokenAuthOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
: base(options, logger, encoder, clock) { }
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!Request.Headers.ContainsKey(Options.ApiKeyHeaderName))
return AuthenticateResult.Fail($"Missing Header For Token: {Options.ApiKeyHeaderName}");
if (!Request.Headers.ContainsKey(Options.UserTokenHeaderName))
return AuthenticateResult.Fail($"Missing Header For Token: {Options.UserTokenHeaderName}");
var apiKey= Request.Headers[Options.ApiKeyHeaderName];
var userToken = Request.Headers[Options.UserTokenHeaderName];
var succeeded= await TokenChecker.CheckToken(userToken) && await TokenChecker.CheckApiKey(apiKey);
if(!succeeded ){ return AuthenticateResult.Fail("incorrect ApiKey or UserToken"); }
var username = "the-username-from-user-token"; //e.g. decode the userToken header
var claims = new[] {
new Claim(ClaimTypes.NameIdentifier, username),
new Claim(ClaimTypes.Name, username),
// add other claims/roles as you like
};
var id = new ClaimsIdentity(claims, Scheme.Name);
var principal = new ClaimsPrincipal(id);
var ticket = new AuthenticationTicket(principal, Scheme.Name);
return AuthenticateResult.Success(ticket);
}
}
并将您的 Auth/SSO 端点更改为 return 用户令牌:
public class AuthController: Controller
{
private readonly MyCustomTokenAuthOptions _myCustomAuthOpts;
// inject the options so that we can know the actual header name
public AuthController(IOptionsMonitor<MyCustomTokenAuthOptions> options)
{
this._myCustomAuthOpts= options.CurrentValue;
}
[HttpPost("/Auth/SSO")]
public async System.Threading.Tasks.Task<IActionResult> CreateUserTokenAsync()
{
var apiKeyHeaderName =_myCustomAuthOpts.ApiKeyHeaderName ;
if (!Request.Headers.ContainsKey(apiKeyHeaderName))
return BadRequest($"Missing Header For Token: {apiKeyHeaderName}");
// check key
var succeeded = await TokenChecker.CheckApiKey(Request.Headers[apiKeyHeaderName]);
if(!succeeded)
return BadRequest($"Incorrect Api Key");
return Json(... {userInfo, apiKey} ... );
}
}