首页 > 解决方案 > 在 ASP.NET Core Web Api 中使用 JWT 授权检查 IP

问题描述

System.IdentityModel.Tokens.Jwt在 ASP.NET 核心 Web Api 应用程序中使用时是否可以检查 IP 地址?

我考虑添加一个包含请求它的用户的 IP 的声明,并以某种方式检查每个请求。通常我会OnActionExecuting在 ASP.NET MVC 中使用。

是否有基于中间件/授权的解决方案?

我像这样创建我的 Jwt 令牌声明:

private IEnumerable<Claim> getStandardClaims(IdentityUser user)
{
    var claims = new List<Claim>
    {
        new Claim(ClaimTypes.Name, user.UserName),
        new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
        new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
        new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
        new Claim("ipaddress", HttpContext.Connection.RemoteIpAddress.ToString())
    };

    return claims;
}

这是 JWT 数据的样子:

{
  "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": "username",
  "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier": "5a6b3eb8-ed7f-48c6-b10c-a279ffd4f7c8",
  "sub": "username",
  "jti": "44c95b53-bfba-4f33-b4c3-834127605432",
  "ipaddress": "::1",
  "exp": 1542707081,
  "iss": "https://localhost:5001/",
  "aud": "https://localhost:5001/"
}

编辑:JWT 索赔的可能解决方案?也许我必须像这样阅读声明(测试代码,没有空检查等):

var auth = HttpContext.Request.Headers.FirstOrDefault(x => x.Key == "Authorization");
string token = auth.Value[0].Split(' ')[1];

JwtTokenService<RefreshToken, string> jwtService = new JwtTokenService<RefreshToken, string>(null);
var principal = jwtService.GetPrincipalFromExpiredToken(token, _config["Jwt:Key"]);

Claim ipClaim = principal.FindFirst(claim => claim.Type == "ipaddress");

这是 GetPrincipalFromExpiredToken 方法:

public ClaimsPrincipal GetPrincipalFromExpiredToken(string token, string securityKey)
{
    var tokenValidationParameters = new TokenValidationParameters
    {
        ValidateAudience = false, 
        ValidateIssuer = false,
        ValidateIssuerSigningKey = true,
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(securityKey)),
        ValidateLifetime = false 
    };

    var tokenHandler = new JwtSecurityTokenHandler();
    SecurityToken securityToken;
    var principal = tokenHandler.ValidateToken(token, tokenValidationParameters, out securityToken);
    var jwtSecurityToken = securityToken as JwtSecurityToken;
    if (jwtSecurityToken == null || !jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256, StringComparison.InvariantCultureIgnoreCase))
        throw new SecurityTokenException("Invalid token");

    return principal;
}

标签: c#asp.net-corejwtasp.net-core-webapi

解决方案


您可以通过基于 Policy 的授权来执行此操作(以及所有其他授权内容) 。

public class IpCheckRequirement : IAuthorizationRequirement
{
    public bool IpClaimRequired { get; set; } = true;
}

public class IpCheckHandler : AuthorizationHandler<IpCheckRequirement>
{
    public IpCheckHandler(IHttpContextAccessor httpContextAccessor)
    {
        HttpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
    }

    private IHttpContextAccessor HttpContextAccessor { get; }
    private HttpContext HttpContext => HttpContextAccessor.HttpContext;


    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, IpCheckRequirement requirement)
    {
        Claim ipClaim = context.User.FindFirst(claim => claim.Type == "ipaddress");

        // No claim existing set and and its configured as optional so skip the check
        if(ipClaim == null && !requirement.IpClaimRequired)
        {
            // Optional claims (IsClaimRequired=false and no "ipaddress" in the claims principal) won't call context.Fail()
            // This allows next Handle to succeed. If we call Fail() the access will be denied, even if handlers
            // evaluated after this one do succeed
            return Task.CompletedTask;
        }

        if (ipClaim.Value = HttpContext.Connection.RemoteIpAddress?.ToString())
        {
            context.Succeed(requirement);
        }
        else
        {
            // Only call fail, to guarantee a failure, even if further handlers may succeed
            context.Fail();
        }

        return Task.CompletedTask;
    }
}

然后加

services.AddSingleton<IAuthorizationHandler, IpCheckHandler>();
services.AddAuthorization(options =>
{
    options.AddPolicy("SameIpPolicy",
        policy => policy.Requirements.Add(new IpCheckRequirement { IpClaimRequired = true }));
});

到你的ConfigureServices方法。

现在您可以注释要应用它的控制器[Authroize(Policy = "SameIpPolicy")]或添加全局策略:

services.AddMvc(options =>
{
    options.Filters.Add(new AuthorizeFilter("SameIpPolicy"))
})

推荐阅读