如何手动验证JWT Asp.Net核心?

时间:2017-06-28 17:11:52

标签: c# asp.net-core jwt

那里有数以百万计的指南,而且似乎没有一个指导我的需要。我正在创建一个身份验证服务器,只需要发出,并验证/重新发出令牌。所以我无法创建一个中间件类来“验证”cookie或标题。我只是收到字符串的POST,我需要以这种方式验证令牌,而不是.net核心提供的Authorize中间件。

我的启动包括唯一的令牌发行者示例我可以开始工作。

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();

            app.UseExceptionHandler("/Home/Error");

            app.UseStaticFiles();
            var secretKey = "mysupersecret_secretkey!123";
            var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secretKey));

            var options = new TokenProviderOptions
            {

                // The signing key must match!
                Audience = "AllApplications",
                SigningCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256),

                Issuer = "Authentication"
            };
            app.UseMiddleware<TokenProviderMiddleware>(Microsoft.Extensions.Options.Options.Create(options));

我可以在创建时使用中间件,因为我只需要截取正文以获取用户名和密码。中间件从前面的Startup.cs代码中获取选项,检查请求路径并从下面的上下文生成令牌。

private async Task GenerateToken(HttpContext context)
{
    CredentialUser usr = new CredentialUser();

    using (var bodyReader = new StreamReader(context.Request.Body))
    {
        string body = await bodyReader.ReadToEndAsync();
        usr = JsonConvert.DeserializeObject<CredentialUser>(body);
    }

    ///get user from Credentials put it in user variable. If null send bad request

    var now = DateTime.UtcNow;

    // Specifically add the jti (random nonce), iat (issued timestamp), and sub (subject/user) claims.
    // You can add other claims here, if you want:
    var claims = new Claim[]
    {
        new Claim(JwtRegisteredClaimNames.Sub, JsonConvert.SerializeObject(user)),
        new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
        new Claim(JwtRegisteredClaimNames.Iat, now.ToString(), ClaimValueTypes.Integer64)
    };

    // Create the JWT and write it to a string
    var jwt = new JwtSecurityToken(
        issuer: _options.Issuer,
        audience: _options.Audience,
        claims: claims,
        notBefore: now,
        expires: now.Add(_options.Expiration),
        signingCredentials: _options.SigningCredentials);
    var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);

    ///fill response with jwt
}

上面的这一大块代码将Deserialize CredentialUser json,然后执行一个返回User Object的存储过程。然后我会添加三个声明,并将其发回。

我能够成功生成一个jwt,并使用像jwt.io这样的在线工具,我把秘密密钥,工具说它是有效的,带有我可以使用的对象

    {
         "sub": " {User_Object_Here} ",
         "jti": "96914b3b-74e2-4a68-a248-989f7d126bb1",
         "iat": "6/28/2017 4:48:15 PM",
         "nbf": 1498668495,
         "exp": 1498668795,
         "iss": "Authentication",
         "aud": "AllApplications"
    }

我遇到的问题是了解如何根据签名手动检查声明。因为这是一个发布和验证令牌的服务器。像大多数指南一样,设置Authorize中间件不是一种选择。下面我试图验证令牌。

[Route("api/[controller]")]
public class ValidateController : Controller
{

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Validate(string token)
    {
        var validationParameters = new TokenProviderOptions()
        {
            Audience = "AllKSDEApplications",
            SigningCredentials = new 
            SigningCredentials("mysupersecret_secretkey!123", 
            SecurityAlgorithms.HmacSha256),

            Issuer = "Authentication"
        };
        var decodedJwt = new JwtSecurityTokenHandler().ReadJwtToken(token);
        var valid = new JwtSecurityTokenHandler().ValidateToken(token, //The problem is here
        /// I need to be able to pass in the .net TokenValidParameters, even though
        /// I have a unique jwt that is TokenProviderOptions. I also don't know how to get my user object out of my claims
    }
}

1 个答案:

答案 0 :(得分:1)

偷了主要从ASP.Net Core源代码中借用了这段代码:https://github.com/aspnet/Security/blob/dev/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerHandler.cs#L45

从那段代码我创建了这个函数:

private string Authenticate(string token) {
    var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
    var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
    List<Exception> validationFailures = null;
    SecurityToken validatedToken;
    var validator = new JwtSecurityTokenHandler();

    TokenValidationParameters validationParameters = new TokenValidationParameters();
    validationParameters.ValidIssuer = "http://localhost:5000";
    validationParameters.ValidAudience = "http://localhost:5000";
    validationParameters.IssuerSigningKey = key;
    validationParameters.ValidateIssuerSigningKey = true;
    validationParameters.ValidateAudience = true;

    if (validator.CanReadToken(token))
    {
        ClaimsPrincipal principal;
        try
        {
            principal = validator.ValidateToken(token, validationParameters, out validatedToken);
            if (principal.HasClaim(c => c.Type == ClaimTypes.Email))
            {
                return principal.Claims.Where(c => c.Type == ClaimTypes.Email).First().Value;
            }
        }
        catch (Exception e)
        {
            _logger.LogError(null, e);
        }
    }

    return String.Empty;
}

validationParameters需要与GenerateToken函数中的#include <iostream> #include <string> std::string intToString(int my_array[], int size) { // just declare a variable of type std::string instead of char string[size+1] // there's no need to define a size as this std::string will grow dynamically // there's also no need to add a delimiter as std::string takes // care of this itself std::string string; for (int i = 0; i < size; i++) { // this appends an integer representing an (ASCII) codepoint // to an initially empty std::string string += my_array[i]; // this won't work as string is initially empty // so you can't access the element at index 0 // string[i] = my_array[i]; } return string; } int main() { const int size = 5; int my_array[] = { 72, 101, 108, 108, 111 }; std::cout << intToString(my_array, size) << std::endl; return 0; } 匹配,然后它才能正常验证。