JWT and Web API (JwtAuthForWebAPI?) - Looking For An Example

前端 未结 2 1592
感情败类
感情败类 2021-01-30 14:56

I\'ve got a Web API project fronted by Angular, and I want to secure it using a JWT token. I\'ve already got user/pass validation happening, so I think i just need to implement

2条回答
  •  醉梦人生
    2021-01-30 15:41

    I ended-up having to take a information from several different places to create a solution that works for me (in reality, the beginnings of a production viable solution - but it works!)

    I got rid of JwtAuthForWebAPI (though I did borrow one piece from it to allow requests with no Authorization header to flow through to WebAPI Controller methods not guarded by [Authorize]).

    Instead I'm using Microsoft's JWT Library (JSON Web Token Handler for the Microsoft .NET Framework - from NuGet).

    In my authentication method, after doing the actual authentication, I create the string version of the token and pass it back along with the authenticated name (the same username passed into me, in this case) and a role which, in reality, would likely be derived during authentication.

    Here's the method:

    [HttpPost]
    public LoginResult PostSignIn([FromBody] Credentials credentials)
    {
        var auth = new LoginResult() { Authenticated = false };
    
        if (TryLogon(credentials.UserName, credentials.Password))
        {
            var tokenDescriptor = new SecurityTokenDescriptor
            {
                Subject = new ClaimsIdentity(new[]
                {
                    new Claim(ClaimTypes.Name, credentials.UserName), 
                    new Claim(ClaimTypes.Role, "Admin")
                }),
    
                AppliesToAddress = ConfigurationManager.AppSettings["JwtAllowedAudience"],
                TokenIssuerName = ConfigurationManager.AppSettings["JwtValidIssuer"],
                SigningCredentials = new SigningCredentials(new 
                    InMemorySymmetricSecurityKey(JwtTokenValidationHandler.SymmetricKey),
                    "http://www.w3.org/2001/04/xmldsig-more#hmac-sha256",
                    "http://www.w3.org/2001/04/xmlenc#sha256")
                };
    
                var tokenHandler = new JwtSecurityTokenHandler();
                var token = tokenHandler.CreateToken(tokenDescriptor);
                var tokenString = tokenHandler.WriteToken(token);
    
                auth.Token = tokenString;
                auth.Authenticated = true;
            }
    
        return auth;
    }
    

    UPDATE

    There was a question about handling the token on subsequent requests. What I did was create a DelegatingHandler to try and read/decode the token, then create a Principal and set it into Thread.CurrentPrincipal and HttpContext.Current.User (you need to set it into both). Finally, I decorate the controller methods with the appropriate access restrictions.

    Here's the meat of the DelegatingHandler:

    private static bool TryRetrieveToken(HttpRequestMessage request, out string token)
    {
        token = null;
        IEnumerable authzHeaders;
        if (!request.Headers.TryGetValues("Authorization", out authzHeaders) || authzHeaders.Count() > 1)
        {
            return false;
        }
        var bearerToken = authzHeaders.ElementAt(0);
        token = bearerToken.StartsWith("Bearer ") ? bearerToken.Substring(7) : bearerToken;
        return true;
    }
    
    
    protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        HttpStatusCode statusCode;
        string token;
    
        var authHeader = request.Headers.Authorization;
        if (authHeader == null)
        {
            // missing authorization header
            return base.SendAsync(request, cancellationToken);
        }
    
        if (!TryRetrieveToken(request, out token))
        {
            statusCode = HttpStatusCode.Unauthorized;
            return Task.Factory.StartNew(() => new HttpResponseMessage(statusCode));
        }
    
        try
        {
            JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
            TokenValidationParameters validationParameters =
                new TokenValidationParameters()
                {
                    AllowedAudience = ConfigurationManager.AppSettings["JwtAllowedAudience"],
                    ValidIssuer = ConfigurationManager.AppSettings["JwtValidIssuer"],
                    SigningToken = new BinarySecretSecurityToken(SymmetricKey)
                };
    
            IPrincipal principal = tokenHandler.ValidateToken(token, validationParameters);
            Thread.CurrentPrincipal = principal;
            HttpContext.Current.User = principal;
    
            return base.SendAsync(request, cancellationToken);
        }
        catch (SecurityTokenValidationException e)
        {
            statusCode = HttpStatusCode.Unauthorized;
        }
        catch (Exception)
        {
            statusCode = HttpStatusCode.InternalServerError;
        }
    
        return Task.Factory.StartNew(() => new HttpResponseMessage(statusCode));
    }
    

    Don't forget to add it into the MessageHandlers pipeline:

    public static void Start()
    {
        GlobalConfiguration.Configuration.MessageHandlers.Add(new JwtTokenValidationHandler());
    }
    

    Finally, decorate your controller methods:

    [Authorize(Roles = "OneRoleHere")]
    [GET("/api/admin/settings/product/allorgs")]
    [HttpGet]
    public List GetAllOrganizations()
    {
        return QueryableDependencies.GetMergedOrganizations().ToList();
    }
    
    [Authorize(Roles = "ADifferentRoleHere")]
    [GET("/api/admin/settings/product/allorgswithapproval")]
    [HttpGet]
    public List GetAllOrganizationsWithApproval()
    {
        return QueryableDependencies.GetMergedOrganizationsWithApproval().ToList();
    }
    

提交回复
热议问题