【问题标题】:How to consume a JWT Token ASP.NET MVC如何使用 JWT 令牌 ASP.NET MVC
【发布时间】:2021-01-31 10:08:15
【问题描述】:

抱歉,这是一条很长的信息,但要清楚(我希望) 我已经按照article 设置了一个 AuthorizationServerProvider 并让任何拥有 JWT 令牌的人在我的代码中调用某个 API。但是,当我尝试调用我的 MVC 项目的任何控制器或 API 控制器方法时,我会收到一条未经授权的消息。

This is When I Request the Token (Updated)

This is when I try to Invoke a Method with [authorize attribute]

This is The Payload

这是我的 ValidateClientAuthentication 和 GrantResourceOwnerCredentials 方法:

using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin.Security.OAuth;
using System.Security.Claims;
using System.Threading.Tasks;
using MyProject.Models;
using Microsoft.Owin.Security;

public class AuthorizationServerProvider : OAuthAuthorizationServerProvider
{
    public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
    {
            context.Validated();
        
    }
    public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {
        var allowedOrigin = "*";
        //
        context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });

        var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
        
        ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
        if (user == null)
        {
            context.SetError("invalid_grant", "The user name or password is incorrect.");
            return;
        }

        ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager, "JWT"); // maybe this is the problem
        // oAuthIdentity.AddClaim(new Claim(ClaimTypes.Role, "User"));
        // oAuthIdentity.AddClaim(new Claim(ClaimTypes.Authentication, "AudienceID"));
        var ticket = new AuthenticationTicket(oAuthIdentity, null);
        context.Validated(ticket);
    }
}

在我的 Startup.Auth.cs 中,我放置了两种方法来让具有 [Authorize] 属性的 Api 控制器可以使用 JWT 进行验证。 这里是部分类:

using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Owin.Security.OAuth;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Threading.Tasks;

public partial class Startup
{
    public void ConfigureAuth(IAppBuilder app)
    {
        app.CreatePerOwinContext(ApplicationDbContext.Create);
        app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
        app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
        
        ConfigureOAuthTokenGeneration(app);
        ConfigureOAuthTokenConsumption(app);
        
        app.UseCookieAuthentication(new CookieAuthenticationOptions
         {
             AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
             LoginPath = new PathString("/Account/Login"),
             Provider = new CookieAuthenticationProvider
             {  
                 OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
                     validateInterval: TimeSpan.FromMinutes(10),
                     regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
             }
         });    
        /*Other Types of Authentication*/        
        app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
        app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
        app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
    }

    private void ConfigureOAuthTokenGeneration(IAppBuilder app)
    {
        OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
        {
            //For Dev enviroment only (on production should be AllowInsecureHttp = false)
            AllowInsecureHttp = true,
            TokenEndpointPath = new PathString("/oauth/token"),
            AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(3),
            Provider = new AuthorizationServerProvider(),
            AccessTokenFormat = new CustomJwtFormat("https://localhost:44391")
        };
        app.UseOAuthAuthorizationServer(OAuthServerOptions);
    }

    private void ConfigureOAuthTokenConsumption(IAppBuilder app)
    {
        var issuer = "https://localhost:44391";
        string audienceId = ConfigurationManager.AppSettings["AudienceID"]; //its value is "AudienceID"
        byte[] audienceSecret = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["SecretKey"]);
        string[] AllowedAud = new[] { audienceId };
        // Api controllers with an [Authorize] attribute will be validated with JWT
        app.UseJwtBearerAuthentication(
            new JwtBearerAuthenticationOptions
            {
                AuthenticationMode = AuthenticationMode.Active,
                AllowedAudiences = AllowedAud,
                // IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[],
                IssuerSecurityKeyProviders = new IIssuerSecurityKeyProvider[]
                {
                    new SymmetricKeyIssuerSecurityKeyProvider(issuer, audienceSecret)
                }
            }) ;
    }
}

我在 ConfigureAuth(IAppBuilder app) 中调用这些方法。 所以毕竟这一切,我无法理解为什么我无法获得授权。有什么想法吗?

如果这有帮助: 我正在使用Microsoft.Owin:Security.Jwt v4.1.1

我用这个方法生成令牌:

using Microsoft.Owin.Security;
using System;
using System.Configuration;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using Microsoft.IdentityModel.Tokens;

public class CustomJwtFormat : ISecureDataFormat<AuthenticationTicket>
{
    private readonly string _issuer = string.Empty;

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

    /*This Generate JWT*/
    public string Protect(AuthenticationTicket data)
    {
        if (data == null)
        {
            throw new ArgumentNullException("data");
        }
        string username = data.Identity.Name;
        string audienceId = ConfigurationManager.AppSettings["AudienceID"]; // "AudienceID"

        string symmetricKeyAsBase64 = ConfigurationManager.AppSettings["SecretKey"];
        
        var keyByteArray = Convert.FromBase64String(symmetricKeyAsBase64);
        
        var issued = data.Properties.IssuedUtc;
        
        var expires = data.Properties.ExpiresUtc;
        
        var securityKey = new SymmetricSecurityKey(keyByteArray);
        
        var signingCredentials = new SigningCredentials(securityKey, algorithm: SecurityAlgorithms.HmacSha256Signature);

        var token = new JwtSecurityToken(issuer:_issuer, audience: audienceId, data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime, signingCredentials) ;
        
        var handler = new JwtSecurityTokenHandler();
        
        var jwt = handler.WriteToken(token);

        return jwt;
    }

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

这就是初创公司

using Microsoft.Owin;
using Owin;
using System.Web.Http;

[assembly: OwinStartupAttribute(typeof(MyProject.Startup))]
namespace MyProject
{
    public partial class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            ConfigureAuth(app);
            var config = new HttpConfiguration();
            WebApiConfig.Register(config);
            app.UseWebApi(config);
        }
    }
}

【问题讨论】:

  • 您可以将您的令牌显示为文本吗?
  • 我用新的 JWT 令牌示例编辑了帖子 =)。 jwt.io 它告诉我这是一个无效的签名!
  • 你能显示Startup.cs代码吗?
  • @Julián 我已将其添加到问题中
  • 关注这个解决方案:stackoverflow.com/questions/27912003/…

标签: c# asp.net asp.net-mvc asp.net-web-api jwt


【解决方案1】:

除非另有特别配置,否则作为令牌身份验证的一部分会发生一些强制性检查。

  1. 令牌是由预期的发行者发行的
  2. 令牌适用于预期受众
  3. 令牌由预期的安全密钥签名

您应该始终仔细检查 1) 和 2) 的配置,使用像 https://jwt.ms/ 这样的令牌查看器来了解有效负载。

通过查看,我可以看到您的令牌中的受众是 AudienceId

{
  "alg": "http://www.w3.org/2001/04/xmldsig-more#hmac-sha256",
  "typ": "JWT"
}.{
  "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier": "199c43cc-c189-4d72-a21e-0c4828f37c8f",
  "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": "UsernameTest@aaa.com",
  "http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider": "ASP.NET Identity",
  "AspNet.Identity.SecurityStamp": "5cb5b86f-9580-44eb-9a8f-a188262d849d",
  "nbf": 1603013411,
  "exp": 1603013591,
  "iss": "https://localhost:44391",
  "aud": "AudienceID"
}.[Signature]

在您的令牌验证参数中,您是在说

string audienceId = ConfigurationManager.AppSettings["ClientID"];

您提到此值为 AccountTest,但您的令牌具有值 AudienceID。为此,您的应用设置 ClientID 的值必须AudienceID。确保这 2 个值匹配 - 您的令牌中的 aud 声明值,以及您在配置中为 AllowedAudiences 设置的值。

您的验证设置必须与令牌负载中的内容完全相同,否则请求将被拒绝。您可以在令牌验证中添加一些日志记录,以准确了解被拒绝的内容,查看此链接

https://stackoverflow.com/a/52370255/1538039

【讨论】:

  • 我已经从代码中删除了 ConfigurationManager.AppSettings["ClientID"] 和 ConfigurationManager.AppSettings["ClientSecret"],它们无用且令人困惑。我已经检查了您所说的三点,因此 aud 声明值和 allowedAudiences 的设置(在配置中)是相同的,但我总是被拒绝。
  • 更新了答案以包含指向帖子的链接,该链接显示您如何记录令牌验证错误。
  • 我已经更新了
  • 查看我的链接stackoverflow.com/a/52370255/1538039。添加一些额外的日志记录/遵循那里的模式,这样你就可以看到 ValidateToken 发生了什么。
  • 终于有一些东西可以帮助我看到令牌的流动了。谢谢,我会告诉你的
猜你喜欢
  • 2020-09-04
  • 2021-04-20
  • 2021-10-09
  • 2022-07-08
  • 2019-10-20
  • 1970-01-01
  • 1970-01-01
  • 2015-04-16
  • 2020-02-02
相关资源
最近更新 更多