多个JWT承载用于授权和认证

时间:2018-12-22 06:08:37

标签: asp.net-core claims-based-identity asp.net-core-2.1

我有一个用于SSO的.NET Core IdentityServer(IS),我想将其用于对.NET Core(后端)-Angular(客户端)应用程序进行身份验证。我想在后端使用EF ApplicationUser,并通过自定义的后端生成的JWT令牌在后端使用基于Claim的授权,该令牌也可以在客户端上进行授权。

在后端,我创建了一个中间件来检查所有请求上的“授权”标头。如果标头包含由IS生成的令牌,我想将其交换为包含必要声明的自定义(后端)生成的令牌。然后,客户端将此标头用于后续的后端请求。

启动配置:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
   app.UseMiddleware<AuthorizationHeaderMiddleware>();
   app.UseAuthentication();
   app.UseStaticFiles();
   app.UseMvc();
}

public void ConfigureServices(IServiceCollection services)
{
   services.AddAuthentication(DEFAULT_AUTH_SCHEME)
        .AddJwtBearer(DEFAULT_AUTH_SCHEME, cfg =>
          {
                cfg.Audience = Configuration["Authorization:JwtIssuer"];
                cfg.RequireHttpsMetadata = false;
                cfg.TokenValidationParameters = new TokenValidationParameters
                    {
                    RequireSignedTokens = false,
                    ValidateIssuer = false,
                    ValidateLifetime = false,
                    ValidateIssuerSigningKey = false,
                    ValidIssuer = Configuration["Authorization:JwtIssuer"],
                    ValidAudience = Configuration["Authorization:JwtIssuer"],
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Authorization:JwtKey"])),
                    RequireExpirationTime = false,
                    ClockSkew = TimeSpan.Zero // remove delay of token when expire
                };
          })
       .AddIdentityServerAuthentication(JwtBearerDefaults.AuthenticationScheme, options =>
             {
                 options.Authority = Configuration["IdentityServer:Url"];
                 options.RequireHttpsMetadata = false;
                 options.ApiName = Configuration["IdentityServer:ApiName"];
                 options.SupportedTokens = SupportedTokens.Both;
                 options.SaveToken = false;
                 options.EnableCaching = false; 
                 options.CacheDuration = TimeSpan.FromMinutes(10);
             });

    services.AddAuthorization(options =>
        options.AddPolicy("protectedScope", policy =>
        {
            policy.AuthenticationSchemes = new List<string> { DEFAULT_AUTH_SCHEME };
            policy.RequireAuthenticatedUser();
            policy.RequireClaim("someclaim");
        }));
}

AuthorizationHeaderMiddleware.cs:

    public class AuthorizationHeaderMiddleware
    {
        private RequestDelegate _next;
        private readonly IConfiguration _configuration;   

        public AuthorizationHeaderMiddleware(RequestDelegate next, IConfiguration configuration)
        {
            _configuration = configuration;
            _next = next;
        }

        public async Task Invoke(HttpContext context)
        {
            // here I intend to get user from the (backend) DB based on "sub" claim from IdentityServer's token and set users claims from DB. Is this correct attitude?
            var claims = new List<Claim> { new Claim("someclaim", "aaaa") };
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Authorization:JwtKey"]));
            var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
            var token = new JwtSecurityToken(
                _configuration["Authorization:JwtIssuer"],
                _configuration["Authorization:JwtIssuer"],
                claims,
                signingCredentials: creds
            );

            var tokenGenerated = new JwtSecurityTokenHandler().WriteToken(token);
            context.Request.Headers["Authorization"] = $"{DEFAULT_AUTH_SCHEME} {tokenGenerated}";

            await _next.Invoke(context);
        }

TestController.cs

    [Authorize(Policy = "protectedScope", AuthenticationSchemes = DEFAULT_AUTH_SCHEME)]
    public class TestController
    {
        [HttpGet]
        public IActionResult TestAction()
        {
            return Ok();
        }
    }

如果我在TestController中请求测试操作,则会获得401未经授权。

我在这里做什么错了?

这不是Use multiple JWT Bearer Authentication 问题的重复,因为我已经尝试了该答案,但没有解决。另外,情况也不一样,因为我想使用IdentityServer进行身份验证,而使用后端JWT进行授权。

1 个答案:

答案 0 :(得分:3)

您只能使用IdentityServerAuthentication,并且在成功通过身份验证后,将声明从应用程序用户添加到当前用户。您可以使用OnTokenValidated

services.AddAuthentication()
        .AddIdentityServerAuthentication(DEFAULT_AUTH_SCHEME, options =>
        {
            options.Authority = Configuration["IdentityServer:Url"];
            options.RequireHttpsMetadata = false;
            options.ApiName = Configuration["IdentityServer:ApiName"];
            options.SupportedTokens = SupportedTokens.Both;
            options.SaveToken = false;
            options.EnableCaching = false;
            options.CacheDuration = TimeSpan.FromMinutes(10);
            options.JwtBearerEvents.OnTokenValidated = async context =>
            {
                // get subject from authenticated principal
                var subject = context.Principal.FindFirst("sub");

                // get claims from your database for the subject
                var claims = new List<Claim> { new Claim("someclaim", "aaaa") };

                // change the principal
                context.Principal = new System.Security.Claims.ClaimsPrincipal(claims);
            };
        });