如何在UseJwtBearerAuthentication中指定多个IssuerSigningKey?

时间:2017-05-25 05:36:12

标签: oauth

我使用OAuth承载令牌身份验证的REST api。令牌由非对称密钥签名,REST api使用公钥验证令牌。我得到的代码如下所示。但是,当密钥需要更新时,我需要处理一个案例。我想要传入一个辅助公钥,让框架首先使用主键和辅助密钥验证令牌。这样,当我需要更新密钥时,我可以轻松添加二级密钥,交换和退出。问题是看下面的代码只需要一个签名密钥。有没有办法指定多个?

    public void ConfigureAuth(IAppBuilder app)
    {
        app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);

        // codes to get signningKey ignored here

        app.UseJwtBearerAuthentication(
            new JwtBearerAuthenticationOptions
            {
                TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateAudience = false,
                    ValidateIssuer = false,
                    IssuerSigningKey = new RsaSecurityKey(signingKey)
                },
            });
    }

谢谢,

2 个答案:

答案 0 :(得分:1)

好的,我想我已经明白了。有两种方法。一种简单直接的方法是使用IssuerSigningKeys属性(我怎么能不在第一时间发现它)。代码如下所示:

        app.UseJwtBearerAuthentication(
            new JwtBearerAuthenticationOptions
            {
                TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateAudience = false,
                    ValidateIssuer = false,
                    IssuerSigningKeys = new List<RsaSecurityKey>
                    {
                        Utils.GetSigningKey(isPrimary: true),
                        Utils.GetSigningKey(isPrimary: false)
                    },
                },
            });

第二种方法是自定义IOAuthBearerAuthenticationProvider。代码如下所示:首先,

        app.UseJwtBearerAuthentication(
            new JwtBearerAuthenticationOptions
            {
                AllowedAudiences = new string[] { "*" },
                IssuerSecurityTokenProviders = new List<IIssuerSecurityTokenProvider>()
                {
                    // Dummy object which won't be used anywhere. It is used to work around parameter validation error about no token provider specified.
                    new SymmetricKeyIssuerSecurityTokenProvider("dummy", "dummy")
                },

                // This is where validation work happens.
                Provider = new BearerAuthenticationProvider(app)
            });

然后,BearerAuthenticationProvider类:

/// <summary>
/// Bearer authentication provider.
/// </summary>
public class BearerAuthenticationProvider : IOAuthBearerAuthenticationProvider
{
    /// <summary>
    /// App config.
    /// </summary>
    private readonly IAppBuilder appConfig;

    /// <summary>
    /// Handles applying the authentication challenge to the response message.
    /// </summary>
    public Func<OAuthChallengeContext, Task> OnApplyChallenge { get; set; }

    /// <summary>
    /// Handles processing OAuth bearer token.
    /// </summary>
    public Func<OAuthRequestTokenContext, Task> OnRequestToken { get; set; }

    /// <summary>
    /// Handles validating the identity produced from an OAuth bearer token.
    /// </summary>
    public Func<OAuthValidateIdentityContext, Task> OnValidateIdentity { get; set; }

    /// <summary>
    /// Initializes a new instance of the <see cref="T:Microsoft.Owin.Security.OAuth.OAuthBearerAuthenticationProvider" /> class
    /// </summary>
    public BearerAuthenticationProvider(IAppBuilder appConfig)
    {
        this.appConfig = appConfig;
        this.OnRequestToken = (OAuthRequestTokenContext context) =>
        {
            var idContext = new OAuthValidateIdentityContext(context.OwinContext, null, null);

            this.ValidateIdentity(idContext);
            return Task.FromResult<int>(0);
        };
        this.OnValidateIdentity = (OAuthValidateIdentityContext context) => Task.FromResult<object>(null);
        this.OnApplyChallenge = (OAuthChallengeContext context) => Task.FromResult<object>(null);
    }

    /// <summary>
    /// Handles applying the authentication challenge to the response message.
    /// </summary>
    /// <param name="context"></param>
    /// <returns></returns>
    public Task ApplyChallenge(OAuthChallengeContext context)
    {
        return this.OnApplyChallenge(context);
    }

    /// <summary>
    /// Handles processing OAuth bearer token.
    /// </summary>
    /// <param name="context"></param>
    /// <returns></returns>
    public virtual Task RequestToken(OAuthRequestTokenContext context)
    {
        return this.OnRequestToken(context);
    }

    /// <summary>
    /// Handles validating the identity produced from an OAuth bearer token.
    /// </summary>
    /// <param name="context"></param>
    /// <returns></returns>
    public virtual Task ValidateIdentity(OAuthValidateIdentityContext context)
    {
        const string AuthHeaderName = "Authorization";
        if (context.Request.Headers.ContainsKey(AuthHeaderName))
        {
            var jwt = context.Request.Headers[AuthHeaderName].Replace("Bearer ", string.Empty);
            var token = new JwtSecurityToken(jwt);
            var claimIdentity = new ClaimsIdentity(token.Claims, "ExternalBearer");

            var param = new TokenValidationParameters()
            {
                ValidateAudience = false,
                ValidateIssuer = false,
                IssuerSigningKeys = new List<RsaSecurityKey>
                {
                    Utils.GetSigningKey(isPrimary: true),
                    Utils.GetSigningKey(isPrimary: false)
                },
            };

            SecurityToken securityToken = null;
            var handler = new JwtSecurityTokenHandler();
            var identity = handler.ValidateToken(token.RawData, param, out securityToken);

            var claimPrincipal = new ClaimsPrincipal(claimIdentity);
            context.Response.Context.Authentication.User = claimPrincipal;
            context.Validated(claimIdentity);
        }
        else
        {
            throw new Exception("Invalid authorization header.");
        }

        return this.OnValidateIdentity(context);
    }
}

第一种方法在应用启动时初始化两个签名密钥,只有在进程重启时才能进行更改。第二种方法在运行时检索密钥,因此密钥翻转不需要重新启动服务。

答案 1 :(得分:0)

如果您希望拥有多个安全密钥,则可以使用IssuerSigningKeys属性的好处,在其中可以添加要用于身份验证的所有密钥。