首页 > 解决方案 > 在 asp.net 中使用 JWT 实现 [Authorize(Roles = "something")]

问题描述

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

我能够成功验证 JWT 令牌并返回一个 claimPrincipal 对象;但是,当我使用 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);
        }
}

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

任何意见,将不胜感激!

标签: c#authorize-attributeasp.net-authorizationjwt-auth

解决方案


推荐阅读