在ASP.NET中用JWT实现[Authorize(Roles =“ something”)]

时间:2019-07-19 14:30:07

标签: c# authorize-attribute asp.net-authorization jwt-auth

我是JWT和授权的新手。在我们的.NET 4.7.2 Web应用程序中,我们有一个ApplicationPrincipal.cs,它具有一个带两个参数的构造函数:IPrincipal对象和UserAccount对象。我们将在JWT令牌验证的SetPrincipalAsync方法中使用此方法。到目前为止,我们一直在JWT有效负载中传递useId以便从其创建UserAccount对象。但是,现在有了一个api控制器,我们正在使用带有Role的Authorize属性(假设是在JWT有效负载中编码的“ randomName”),并且我们不要求在JWT有效负载中使用userId。在我授权没有userId的请求的情况下,我的ApplicationPrincipal类中可以有第二个构造函数,仅接受IPrincipal对象,但是Identity将为null。

我能够成功验证JWT令牌并返回ClaimsPrincipal对象;但是,当我使用Postman测试我的api时,它会返回401-未经授权。

public class ApplicationIdentity : IIdentity
{
    public ApplicationIdentity(UserAccount account)
    {
        Name = account.FullName;
        Account = account;
    }

    public UserAccount Account { get; }
    public string AuthenticationType => "JWT";
    public bool IsAuthenticated => true;
    public string Name { get; }
}

public class ApplicationPrincipal : IPrincipal
{
    private readonly IPrincipal _principal;
    public IIdentity Identity { get; }

    public ApplicationPrincipal(IPrincipal principal, UserAccount account)
    {
        _principal = principal;
        Identity = new ApplicationIdentity(account);
    }

    public ApplicationPrincipal(IPrincipal principal)
    {
        _principal = principal;
    }

    public bool IsInRole(string role) => _principal.IsInRole(role);
}

public class TokenValidationHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(
            HttpRequestMessage request,
            CancellationToken cancellationToken
        )
    {
        try
        {
            var (principal, jwtSecurityToken) = await ValidateJwtAsync(token).ConfigureAwait(true);
            var payload = ValidatePayload(jwtSecurityToken);

            await SetPrincipalAsync(principal, payload).ConfigureAwait(true);

            return await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
        }
        catch (SecurityTokenValidationException ex)
        {
            return request.CreateApiErrorResponse(HttpStatusCode.Unauthorized, ex);
        }
        catch (Exception ex)
        {
            return request.CreateApiErrorResponse(HttpStatusCode.InternalServerError, ex);
        }
    }

    private static async Task SetPrincipalAsync(IPrincipal principal, JWTPayload payload)
    {
        if (!Guid.TryParse(payload.UserId, out var userId) && payload.api?.version != "someName")
        {
            throw new SecurityTokenValidationException("Token does not have valid user ID.");
        }

        if (payload.api?.version == "someName")
        {
            var myPrincipal = new ApplicationPrincipal(principal);
            HttpContext.Current.User = myPrincipal;
        }
        else
        {
            var myPrincipal = new ApplicationPrincipal(principal);
            var handler = new Account(userId, comeOtherValue);

            var account = await CacheManager.Instance.GetOrAddAsync(handler).ConfigureAwait(true);

            if (account == null)
            {
                throw new SecurityTokenValidationException("Could not find user account.");
            }

            myPrincipal = new ApplicationPrincipal(principal, account);
            HttpContext.Current.User = myPrincipal;
        }
    }

  private static async Task<(IPrincipal Principal, JwtSecurityToken Token)> ValidateJwtAsync(string token, string requestingApi)
        {
            // the rest of the code

            ClaimsPrincipal claimsPrincipal;
            SecurityToken securityToken;
            var handler = new JwtSecurityTokenHandler();

            try
            {
                claimsPrincipal = handler.ValidateToken(
                    token,
                    validationParameters,
                    out securityToken
                );

                if (requestingApi.Contains("the specific api with Role"))
                {
                    var ci = new ClaimsIdentity();
                    ci.AddClaim(new Claim(ClaimTypes.Role, "roleName")); //role name applied on the api
                    claimsPrincipal.AddIdentity(ci);
                }
            }
            catch (ArgumentException ex)
            {
                // some code
            }

            var jwtToken = (JwtSecurityToken)securityToken;
            if (jwtToken == null)
            {
                //some code
            }

            return (claimsPrincipal, jwtToken);
        }
}

我的目标是基于具有特定嵌套属性的JWT有效负载将[Authorize(Roles = "randomName")]应用于控制器: {"http://clients": {"api" : {"version1" : "randomName"}}

任何建议将不胜感激!

0 个答案:

没有答案