在ASP.NET Core Web Api中使用JWT授权检查IP

时间:2018-11-19 11:38:15

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

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

我考虑过添加一个包含请求用户IP的Claim,并针对每个请求以某种方式对其进行检查。通常,我会在ASP.NET MVC中使用OnActionExecuting

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

我这样创建我的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索赔的可能解决方案? 也许我必须像这样阅读声明(测试代码,没有null检查等):

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;
}

2 个答案:

答案 0 :(得分:2)

您可以通过Policy-based authorization来执行此操作(以及所有其他授权操作)。

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"))
})

答案 1 :(得分:0)

我通过在每个控制器上应用[Authorize(Policy =“ SameIpPolicy”)]使其与@Tseng解决方案配合使用,非常感谢! 只是为了纠正一个错字错误:

缺少半冒号:

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