持票人错误=" invalid_token"来自.net core 2.0

时间:2017-12-21 21:48:12

标签: php .net-core jwt

我在PHP中设置了一个身份验证系统,我试图将JSON Web令牌发送到客户端应用程序,以便应用程序访问.net core 2.0 api。因此,身份验证服务器获取用户凭据,如果凭据通过,它将使用我使用openssl创建的公钥/私钥对生成令牌。我正在使用以下过程生成令牌:http://www.phpbuilder.com/articles/application-architecture/security/using-a-json-web-token-in-php.html

我可以用jwt.io解码生成的令牌就好了。解码后的版本如下所示:

{
  "typ": "JWT",
  "alg": "HS256"
}

{
  "iss": "https://crm.advtis.com",
  "exp": "2017-12-21 18:14:42",
  "aud": "https://localhost:44354",
  "data": {
    "username": "pwalter@advtis.com",
    "role": 1
  }
}

我可以从我的私钥文件输入字符串,该文件用于编码令牌,jwt.io表示签名有效。

所以现在我想将令牌和请求一起发送到我的API以获取对该资源的访问权限,而该资源在localhost上找到。这是与之相关的.NET 2.0启动代码:

public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<SmartRxDBContext>();

            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddJwtBearer(options =>
            {
                options.Audience = "https://localhost:44354/";
                options.TokenValidationParameters = new TokenValidationParameters {
                    ValidateIssuerSigningKey = true,
                    ValidateIssuer = true,
                    ValidIssuer = "https://crm.advtis.com/",
                    IssuerSigningKey = new X509SecurityKey(new X509Certificate2("C:\\Path\\To\\webOrders-cert-AspNetPubkey.cert"))
                };
            });
            services.AddMvc();
        }

引用的路径是我使用openssl从用于编码jwt的私钥生成的pem格式化公钥。我试图按照.NET Core的这个例子 - JWT Validation and Authorization in ASP.NET Core - 但当然整个auth中间件配置已经在2.0中移动到ConfigureServices方法,并且不再有AutomaticAuthorization选项。

一切似乎都很好,但我得到的是401 - 未经授权的持票人错误=&#34; invalid_token&#34;使用标题作为Authorization: Bearer TOKEN发出请求时出错。无论如何,我希望有人可以就我可能出错的地方和/或我如何进一步排除故障给出一些建议。

1 个答案:

答案 0 :(得分:0)

所以,对于想要运行同一类型场景的人来说,我想到了这一点。

我的第一个问题是我使用HMAC在PHP端生成令牌。 HMAC不是公共/私有对算法。我使用composer安装了firebase/php-jwt库,并使用它来生成我的令牌:

use \Firebase\JWT\JWT;
$token = array(      
"iss" => "https://crm.advtis.com",
"exp" => time()+10000,
"aud" => "https://localhost:44354",
"data" => array(
    "username" => "pwalter@advtis.com",
    "role" => 1
)
);

$jwt = JWT::encode($token, file_get_contents("../path/to/private.key"), 'RS256');

密钥文件是在OpenSSL的命令行上生成的。我使用相同的实用程序来生成带有私钥的公钥。这两个文件都是PEM格式。

我接下来要做的就是将公钥变为.NET。我发现最简单的方法是使用RSACryptoServiceProvider,类似这样:

private static RSACryptoServiceProvider myRSA = new RSACryptoServiceProvider();
public void ConfigureServices(IServiceCollection services)
    {
        JwtHelper.FromXmlString(myRSA, "C:\\Users\\Path\\To\\xmlPubKey.xml");
        services.AddDbContext<SmartRxDBContext>();

        services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(options =>
        {
            options.TokenValidationParameters = new TokenValidationParameters {
                ValidateIssuerSigningKey = true,
                ValidateIssuer = true,
                ValidIssuer = "https://crm.advtis.com",
                ValidateAudience = true,
                ValidAudience = "https://localhost:44354",
                IssuerSigningKey = new RsaSecurityKey(myRSA.ExportParameters(false))
            };
        });
        services.AddMvc();

    }

JwtHelper.FromXml()方法改编自here,因为.NET 2.0无法在RSA Provider中从XML导入或设置参数,因此该方法解析XML并设置参数像这样:

public static void FromXml(this RSA rsa, string filepath)
    {
        RSAParameters parameters = new RSAParameters();

        XmlDocument xmlDoc = new XmlDocument();
        xmlDoc.Load(filepath);

        if (xmlDoc.DocumentElement.Name.Equals("RSAKeyValue"))
        {
            foreach (XmlNode node in xmlDoc.DocumentElement.ChildNodes)
            {
                switch (node.Name)
                {
                    case "Modulus": parameters.Modulus = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                    case "Exponent": parameters.Exponent = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                    case "P": parameters.P = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                    case "Q": parameters.Q = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                    case "DP": parameters.DP = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                    case "DQ": parameters.DQ = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                    case "InverseQ": parameters.InverseQ = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                    case "D": parameters.D = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                }
            }
        }
        else
        {
            throw new Exception("Invalid XML RSA key.");
        }

        rsa.ImportParameters(parameters);
    }

这就是它的要点。现在我可以从我的PHP服务器向客户端应用程序发出JWT。然后我可以将JWT发送到我的.NET Core 2.0 API,以访问受[Authorize]属性保护的端点。