如何使用 JWT 令牌 ASP.NET MVC

Posted

技术标签:

【中文标题】如何使用 JWT 令牌 ASP.NET MVC【英文标题】:How to consume a JWT Token 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 我已将其添加到问题中 关注这个解决方案:***.com/questions/27912003/… 【参考方案1】:

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

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

您应该始终仔细检查 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://***.com/a/52370255/1538039

【讨论】:

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

以上是关于如何使用 JWT 令牌 ASP.NET MVC的主要内容,如果未能解决你的问题,请参考以下文章

在 asp net core MVC 应用程序中创建和验证 JWT 令牌(没有客户端服务器方法)

如何自定义 ASP .NET Web API JWT 令牌响应?

如何在 ASP.NET Core 中转储解析的 JWT 令牌?

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

使用 JWT 令牌保护 asp.net 核心 Web api 时如何从 Azure AD 获取用户

如何保护 ASP.NET Core Web API 免受被盗 JWT 令牌以进行模拟