Azure 函数在不使用 Active Directory 的情况下创建和读取 JWT

Azure Function create and read JWT without using Active Directory

我正在尝试使用 C# 在 Azure 函数中创建和读取(验证)JSON Web 令牌 (JWT)。我遇到了这个 post:

https://www.codeproject.com/Tips/1208535/Create-And-Consume-JWT-Tokens-in-csharp

它很好地概述了这个过程。作为 Azure Functions 的新手,我将 "System.IdentityModel.Tokens.Jwt" 的引用放在我的 project.json 文件中,如下所示:

{
  "frameworks": {
    "net46":{
      "dependencies": {
        "System.IdentityModel.Tokens.Jwt" : "5.0"
      }
    }
   }
}

我使用的版本来自这个 post: ,它在 2016 年讨论了版本控制问题。

不幸的是,这没有用。对 SecurityAlgorithms、JwtHeader、JwtPayload、JwtSecurityToken 和 JwtSecurityTokenHandler 的引用均报告,“[run.csx] 找不到类型或命名空间名称 'class name'(是否缺少 using 指令或程序集引用?) ".

进一步研究,我发现了这个页面:https://www.nuget.org/packages/System.IdentityModel.Tokens.Jwt/,其中显示了 System.IdentityModel.Tokens.Jwt 的 Nuget 版本信息。在尝试了多个版本后(通过更改我的 project.json 文件中的版本),我仍然无法让 Function App 识别我需要的 类。

我认为这是一个版本控制问题。如果是这样,我可以去哪里确定哪个版本的 "System.IdentityModel.Tokens.Jwt" 与 "net46" 兼容?我已经很多年没有编写 C# 代码了(我是一名 Java 开发人员),所以我对版本控制的假设可能是错误的。

顺便说一句,这是我函数中的代码,它看起来与 https://www.codeproject.com/Tips/1208535/Create-And-Consume-JWT-Tokens-in-csharp 中的代码示例完全一样。唯一的区别是我将它包装在一个函数应用程序中。

using System.Net;
using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using System.IdentityModel;  
using System.Security; 
using System.Text;
using System.IdentityModel.Tokens;

public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
    // Define const Key this should be private secret key  stored in some safe place
           string key = "401b09eab3c013d4ca54922bb802bec8fd5318192b0a75f201d8b3727429090fb337591abd3e44453b954555b7a0812e1081c39b740293f765eae731f5a65ed1";

           // Create Security key  using private key above:
           // not that latest version of JWT using Microsoft namespace instead of System
           var securityKey = new Microsoft
               .IdentityModel.Tokens.SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));

           // Also note that securityKey length should be >256b
           // so you have to make sure that your private key has a proper length
           //
           var credentials = new Microsoft.IdentityModel.Tokens.SigningCredentials
                             (securityKey, SecurityAlgorithms.HmacSha256Signature);

           //  Finally create a Token
           var header = new JwtHeader(credentials);

           //Some PayLoad that contain information about the  customer
           var payload = new JwtPayload
           {
               { "some ", "hello "},
               { "scope", "http://dummy.com/"},
           };


           //
           var secToken = new JwtSecurityToken(header, payload);
           var handler = new JwtSecurityTokenHandler();

           // Token to String so you can use it in your client
           var tokenString = handler.WriteToken(secToken);

           // And finally when  you received token from client
           // you can  either validate it or try to  read
           var token = handler.ReadJwtToken(tokenString);

    return req.CreateResponse(HttpStatusCode.Created, "test");
}

所以,我的问题是:

  1. 我的项目文件中 "net46" 应该使用哪个 System.IdentityModel.Tokens 版本?
  2. 下次发生这种情况时,我如何确定哪些版本可以一起工作?

我刚试过这个,看到了同样的事情。您缺少对 System.IdentityModelusing System.IdentityModel.Tokens.Jwt;

的引用

改成这个得到东西建设:

#r "System.IdentityModel"

using System.Net;
using System.IdentityModel;
using System.Security;
using System.Text;
using System.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;

我还建议您将 JWT 包参考升级到 5.2.4,这是该包的最新版本。

我明白了。从各种站点和数百种版本组合,它都有效。 我希望我能解释原因,但我将 post 此处的工作代码列出适当的库。如果其他人遇到这个问题,我希望它有所帮助。感谢您查看此 brettsam!

功能应用程序如下所示:

using System;
using System.Net; 
using System.Text;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using Microsoft.IdentityModel.Tokens;
using System.Configuration;


public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
    string token = JwtManager.GenerateToken("rbivens@mydomain.com", 60);
    ClaimsPrincipal simplePrinciple = JwtManager.GetPrincipal(token);

    var identity = simplePrinciple.Identity as ClaimsIdentity;  
    log.Info(identity.IsAuthenticated.ToString());

    var usernameClaim = identity.FindFirst(ClaimTypes.Name);  
    var username = usernameClaim ? .Value;
    log.Info(username);

    return req.CreateResponse(HttpStatusCode.Created, token);
}


public static class JwtManager
{
    private static string secret = ConfigurationManager.AppSettings["FunctionsJwtSecret"];

    public static string GenerateToken(string username, int expireMinutes = 60)
    {
        var symmetricKey = Convert.FromBase64String(secret);
        var tokenHandler = new JwtSecurityTokenHandler();
        var now = DateTime.UtcNow;

        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(new[]
            {
                new Claim(ClaimTypes.Name, username)
            }),

            Expires = now.AddMinutes(Convert.ToInt32(expireMinutes)),
            SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(symmetricKey), SecurityAlgorithms.HmacSha256Signature)
        };

        var stoken = tokenHandler.CreateToken(tokenDescriptor);
        var token = tokenHandler.WriteToken(stoken);
        return token;
    }

    public static ClaimsPrincipal GetPrincipal(string token)
    {
        try
        {
            var tokenHandler = new JwtSecurityTokenHandler();
            var jwtToken = tokenHandler.ReadToken(token) as JwtSecurityToken;

            if (jwtToken == null)
                return null;

            var symmetricKey = Convert.FromBase64String(secret);

            var validationParameters = new TokenValidationParameters()
            {
                RequireExpirationTime = true,
                ValidateIssuer = false,
                ValidateAudience = false,
                IssuerSigningKey = new SymmetricSecurityKey(symmetricKey)
            };

            SecurityToken securityToken;
            var principal = tokenHandler.ValidateToken(token, validationParameters, out securityToken);
            // log.Info(securityToken.ToString());

            return principal;
        }

        catch (Exception)
        {
            return null;
        }
    }
}

project.json 看起来像这样:

{
  "frameworks": {
    "net46":{
      "dependencies": {
        "Microsoft.IdentityModel.Logging" : "1.0.0.127",
        "Microsoft.IdentityModel.Tokens" : "5.0.0.127",
        "Newtonsoft.Json" : "9.0.0.0",
        "System.IdentityModel.Tokens.Jwt" : "5.0.0.127"
      }
    }
   }
}

同样,我不知道为什么这种版本组合可以一起工作,但我希望这可以为其他人节省 20 个小时的繁琐试验和错误。