r s*_*ton 9 c# oauth-2.0 jwt asp.net-core
我需要为同事创建一个框架,允许多种身份验证方案和关联授权策略(因为我们的 IDP 有多种允许的方法),但这些方案需要依赖项注入,因为 IDP 信息是从基于云的配置源提供的。在我到目前为止所处理的情况下,身份验证是使用 JWT Bearer 令牌完成的。
我想坚持最新的做法,尽可能与时俱进。所以看来我应该在我AddAuthentication的.AddJwtBearerAddAuthorizationIServiceCollection
我期望控制器或端点可以用 来装饰,AuthorizeAttribute并且它将采用指定的默认策略,该策略将使用指定的默认 authN 方案。如果该属性被赋予构造函数参数Policy = <Some Non Default Policy>或AuthenticationSchemes = <Some other scheme>,它将切换到这些参数。
首先,我确定我需要使用依赖注入,所以我使用IConfigureNamedOptions<JwtBearerOptions>.
所以我为授权代码创建了这样的选项类
public class AuthCodeJwtBearerOptions : IConfigureNamedOptions<JwtBearerOptions>
{
private readonly IClaimsTransformation _claimsTransformation;
private readonly ConfigurationManager<OpenIdConnectConfiguration> _configurationManager;
public AuthCodeJwtBearerOptions(IOptions<TidV4OAuthSettings> tidV4OAuthOptions,
IClaimsTransformation claimsTransformation)
{
_claimsTransformation = claimsTransformation;
_configurationManager =
new ConfigurationManager<OpenIdConnectConfiguration>(tidV4OAuthOptions.Value.WellknownUrl,
new OpenIdConnectConfigurationRetriever());
}
public void Configure(JwtBearerOptions options) => Configure("AuthCode", options);
public void Configure(string name, JwtBearerOptions options)
{
var task = Task.Run(async () => await GetTokenValidationParametersAsync());
options.TokenValidationParameters = task.Result;
options.Events = new JwtBearerEvents { OnTokenValidated = OnTokenValidated };
}
private async Task<TokenValidationParameters> GetTokenValidationParametersAsync()
{
var cancellationToken = new CancellationToken();
var openIdConnectConfiguration = await _configurationManager.GetConfigurationAsync(cancellationToken);
return new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKeys = openIdConnectConfiguration?.SigningKeys,
ValidateAudience = false,
ValidateIssuer = true,
ValidIssuer = openIdConnectConfiguration?.Issuer,
ValidateLifetime = true
};
}
private async Task OnTokenValidated(TokenValidatedContext context)
{
if (context.Principal == null)
{
return;
}
context.Principal = await _claimsTransformation.TransformAsync(context.Principal);
}
}
Run Code Online (Sandbox Code Playgroud)
然后我在一个名为 的类中重复了这个模式ClientCredentialsJwtBearerOptions,但我有这个变体
public void Configure(JwtBearerOptions options) => Configure("ClientCredentials", options);
Run Code Online (Sandbox Code Playgroud)
我通过设置默认身份验证方案、使用空委托按名称添加 JWT 承载来注册所有这些,然后调用来配置选项。
然后我分配策略。我可能是错的,但我不认为策略创建是问题所在,但我会包含代码,以防出现问题。
serviceCollection
.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = "AuthCode";
options.DefaultChallengeScheme = "AuthCode";
})
.AddJwtBearer("AuthCode", _ => { })
.AddJwtBearer("ClientCredentials", _ => { });
serviceCollection.ConfigureOptions<ClientCredentialsJwtBearerOptions>();
serviceCollection.ConfigureOptions<AuthCodeJwtBearerOptions>();
serviceCollection.AddAuthorization(options =>
{
var authCodePolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.AddAuthenticationSchemes("AuthCode")
.Build();
var clientCredentialsPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.AddAuthenticationSchemes("ClientCredentials")
.Build();
var allPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.AddAuthenticationSchemes("AuthCode", "ClientCredentials")
.Build();
options.AddPolicy("AuthCodeOnly", authCodePolicy);
options.AddPolicy("ClientCredentialsOnly", clientCredentialsPolicy);
options.AddPolicy( "AllPolicies", allPolicy);
options.DefaultPolicy = authCodePolicy;
});
Run Code Online (Sandbox Code Playgroud)
我在这里使用字符串常量,但为了便于阅读示例,我编写了文字字符串。
当我测试这个时,如果我完全像这样保留它,一切都会正常。它使用并且AuthCodeJwtBearerOptions请求通过。
但是如果我将Authorize属性更改为
[Authorize(Policy = "ClientCredentialsOnly")]
Run Code Online (Sandbox Code Playgroud)
身份验证仍然使用AuthCodeJwtBearerOptions. Configure我可以通过简单地颠倒调用顺序来切换它
serviceCollection.ConfigureOptions<AuthCodeJwtBearerOptions>();
serviceCollection.ConfigureOptions<ClientCredentialsJwtBearerOptions>();
Run Code Online (Sandbox Code Playgroud)
向我建议,它只是使用最后一个注册的配置,并且不尊重命名配置的“命名”功能。
如果我更改默认策略,则不会发生任何变化。
我觉得我已经拥有了让这个工作正常运行所需的大部分内容,但我只是误解了它的作用ConfigureOptions。
任何帮助表示赞赏。谢谢。
我的方法有两个问题,我想我现在有一个很好的解决方案。
首先,在Jeremy Lakeman的帮助下,我了解到 的方法的反向IConfigureNamedOptions<JwtBearerOptions>用法Configure。相反,您对方案执行名称检查,如果通过,则对其进行配置。
public void Configure(JwtBearerOptions options)
{
var task = Task.Run(async () => await GetTokenValidationParametersAsync());
options.TokenValidationParameters = task.Result;
options.Events = new JwtBearerEvents { OnTokenValidated = OnTokenValidated };
}
public void Configure(string name, JwtBearerOptions options)
{
if(!name.Equals("AuthCode"))
{
return
}
Configure(options);
}
Run Code Online (Sandbox Code Playgroud)
这将获得正确注册的选项。但是,我遇到的第二个问题是,如果您设置默认身份验证方案,它将始终运行,即使明确请求另一个方案或策略也是如此
但是,我发现,如果您不分配默认身份验证方案而仅定义默认身份验证策略,则可以使用[Authorize]默认策略,但[Authorize("ClientCredentialsOnly")]只会执行客户端凭据的方案和策略。
serviceCollection.AddAuthentication()
.AddJwtBearer("AuthCode", _ => { })
.AddJwtBearer("ClientCredentials", _ => { });
Run Code Online (Sandbox Code Playgroud)
serviceCollection.AddAuthorization(options =>
{
var authCodePolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.AddAuthenticationSchemes("AuthCode")
.Build();
var clientCredentialsPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.AddAuthenticationSchemes("ClientCredentials")
.Build();
var allPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.AddAuthenticationSchemes("AuthCode", "ClientCredentials")
.Build();
options.AddPolicy("AuthCodeOnly", authCodePolicy);
options.AddPolicy("ClientCredentialsOnly", clientCredentialsPolicy);
options.AddPolicy( "AllPolicies", allPolicy);
options.DefaultPolicy = options.GetPolicy("AuthCodeOnly")!;
});
Run Code Online (Sandbox Code Playgroud)
那么,我们来总结一下。
public class AuthCodeJwtBearerOptions : IConfigureNamedOptions<JwtBearerOptions>
{
private readonly IClaimsTransformation _claimsTransformation;
private readonly ConfigurationManager<OpenIdConnectConfiguration> _configurationManager;
private readonly string _name;
public AuthCodeJwtBearerOptions(IOptions<TidV4OAuthSettings> tidV4OAuthOptions,
IClaimsTransformation claimsTransformation)
{
_name = "AuthCode";
_claimsTransformation = claimsTransformation;
_configurationManager =
new ConfigurationManager<OpenIdConnectConfiguration>(tidV4OAuthOptions.Value.WellknownUrl,
new OpenIdConnectConfigurationRetriever());
}
public void Configure(JwtBearerOptions options)
{
var task = Task.Run(async () => await GetTokenValidationParametersAsync());
options.TokenValidationParameters = task.Result;
options.Events = new JwtBearerEvents { OnTokenValidated = OnTokenValidated };
}
public void Configure(string name, JwtBearerOptions options)
{
if(!name.Equals(_name))
{
return;
}
Configure(options);
}
private async Task<TokenValidationParameters> GetTokenValidationParametersAsync()
{
var cancellationToken = new CancellationToken();
var openIdConnectConfiguration = await _configurationManager.GetConfigurationAsync(cancellationToken);
return new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKeys = openIdConnectConfiguration?.SigningKeys,
ValidateAudience = false,
ValidateIssuer = true,
ValidIssuer = openIdConnectConfiguration?.Issuer,
ValidateLifetime = true
};
}
private async Task OnTokenValidated(TokenValidatedContext context)
{
if (context.Principal == null)
{
return;
}
//whatever you need to do once validated including claims transformation
context.Principal = await _claimsTransformation.TransformAsync(context.Principal);
}
}
Run Code Online (Sandbox Code Playgroud)
对您想要支持的其他方案重复上述操作,_name根据需要切换 、令牌验证参数和事件逻辑。我现在为“ClientCredentials”执行此操作。
现在将其连接到您的管道中
serviceCollection.AddAuthentication()
.AddJwtBearer("AuthCode", _ => { })
.AddJwtBearer("ClientCredentials", _ => { });
serviceCollection.ConfigureOptions<ClientCredentialsJwtBearerOptions>();
serviceCollection.ConfigureOptions<AuthCodeJwtBearerOptions>();
serviceCollection.AddAuthorization(options =>
{
var authCodePolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.AddAuthenticationSchemes("AuthCode")
.Build();
var clientCredentialsPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.AddAuthenticationSchemes("ClientCredentials")
.Build();
var allPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.AddAuthenticationSchemes("AuthCode", "ClientCredentials")
.Build();
options.AddPolicy("AuthCodeOnly", authCodePolicy);
options.AddPolicy("ClientCredentialsOnly", clientCredentialsPolicy);
options.AddPolicy( "AllPolicies", allPolicy);
options.DefaultPolicy = options.GetPolicy("AuthCodeOnly")!;
});
Run Code Online (Sandbox Code Playgroud)
再次非常感谢Jeremy对选项的指导。还要感谢Marc,纠正了我第一篇 Stack Overflow 帖子中糟糕的作业格式。
| 归档时间: |
|
| 查看次数: |
10258 次 |
| 最近记录: |