WebApi 2始终使用自定义JWT令牌返回401授权

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WebApi 2始终使用自定义JWT令牌返回401授权相关的知识,希望对你有一定的参考价值。

我已经按照本教程Create a RESTful API with authentication using Web API and Jwt

我设法让认证部分工作,但授权部分不起作用(朝向教程结束)。如果我在授权头中添加带有bearer一词的jwt令牌,它会让我拒绝401授权。

我想也许我需要创建一个自定义授权属性。

  1. 有没有办法使用现有的authorize属性?
  2. 现有的Authorize属性为了授权用户(在authorize属性中不包括角色或用户参数)寻找什么?

Startup.Auth.cs

public partial class Startup
{
    public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }

    public static string PublicClientId { get; private set; }

    // For more information on configuring authentication, please visit https://go.microsoft.com/fwlink/?LinkId=301864
    public void ConfigureAuth(IAppBuilder app)
    {
        var issuer = ConfigurationManager.AppSettings["Issuer"];
        var secret = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["Secret"]);

        // Configure the db context and user manager to use a single instance per request
        app.CreatePerOwinContext(ApplicationDbContext.Create);
        app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);

        app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions
        {
            AuthenticationMode = AuthenticationMode.Active,
            AllowedAudiences = new[] { "Any" },
            IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
            {
                new SymmetricKeyIssuerSecurityTokenProvider(issuer, secret)
            }
        });

        app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
        {
            AllowInsecureHttp = true,
            TokenEndpointPath = new PathString("/Token"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
            Provider = new CustomOAuthProvider(),
            AccessTokenFormat = new CustomJwtFormat(issuer)
        });
    }
}

CustomOAuthProvider

public class CustomOAuthProvider : OAuthAuthorizationServerProvider
{
    public override Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {
        context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });

        var blSecurity = new BLSecurity();
        var user = blSecurity.LogonUser(context.UserName, context.Password);

        if (!(user.ResponseType == Global.Response.ResponseTypes.Success))
        {
            context.SetError("Authentication Error", "The user name or password is incorrect");
            return Task.FromResult<object>(null);
        }

        var ticket = new AuthenticationTicket(SetClaimsIdentity(context, user.LoggedOnUser), new AuthenticationProperties());
        context.Validated(ticket);

        return Task.FromResult<object>(null);
    }

    public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
    {
        context.Validated();
        return Task.FromResult<object>(null);
    }

    private static ClaimsIdentity SetClaimsIdentity(OAuthGrantResourceOwnerCredentialsContext context, User user)
    {
        //Add User Claims
        var identity = new ClaimsIdentity("JWT");
        identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
        identity.AddClaim(new Claim("sub", context.UserName));
        identity.AddClaim(new Claim("bn", user.BranchName));
        identity.AddClaim(new Claim("fn", user.FirstName));
        identity.AddClaim(new Claim("ln", user.LastName));

        //Add User Role Claims
        var blRole = new BLRole();
        var roles = blRole.GetRolesByUserId(user.UserID);

        if (roles != null && roles.Count > 0)
        {
            foreach (var role in roles)
            {
                identity.AddClaim(new Claim(ClaimTypes.Role, role.RoleName));
            }
        }

        return identity;
    }
}

CustomJwtFormat

public class CustomJwtFormat : ISecureDataFormat<AuthenticationTicket>
{
    private static readonly byte[] _secret = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["secret"]);
    private readonly string _issuer;

    public CustomJwtFormat(string issuer)
    {
        _issuer = issuer;
    }

    public string Protect(AuthenticationTicket data)
    {
        if (data == null)
        {
            throw new ArgumentNullException(nameof(data));
        }

        var signingKey = new HmacSigningCredentials(_secret);
        var issued = data.Properties.IssuedUtc;
        var expires = data.Properties.ExpiresUtc;

        return new JwtSecurityTokenHandler().WriteToken(new JwtSecurityToken(_issuer, null, data.Identity.Claims, issued.Value.UtcDateTime.ToLocalTime(), expires.Value.UtcDateTime.ToLocalTime(), signingKey));
    }

    public AuthenticationTicket Unprotect(string protectedText)
    {
        throw new NotImplementedException();
    }
}
答案

我设法通过应用评论部分中发布的内容来解决问题。见下面的引用。

Roman Shramko 2016年6月23日上面的代码中有一个错误。 JwtBearerAuthenticationOptions配置为

AllowedAudiences = new [] {“Any”},

但事实上,令牌内容不包含任何受众,因此您的请求会被拒绝。

解决这个问题的最快方法(而不是最好的方法)是改变在CustomJwtFormat类的Protect方法中创建标记的方式。

new JwtSecurityToken(_issuer,null,data.Identity.Claims,issued.Value.UtcDateTime,expires.Value.UtcDateTime,signingKey);

对这一个

new JwtSecurityToken(_issuer,“Any”,data.Identity.Claims,issued.Value.UtcDateTime,expires.Value.UtcDateTime,signingKey);

即传递“Any”而不是null作为第二个构造函数参数。

以上是关于WebApi 2始终使用自定义JWT令牌返回401授权的主要内容,如果未能解决你的问题,请参考以下文章

如何对 ASP.NET WebApi 的每个请求应用自定义验证到 JWT 令牌?

.NET Core 中的 JWT 授权——401 错误

使用 JWT 处理 401(未授权)angularjs 身份验证

Lumen JWT Auth登录成功后总是在其他路由返回401

JWT 令牌未经过验证,因此每次都返回 401 未授权

带有 ASP.NET Core WebAPI 的 Dart 中的令牌刷新并发问题