在 ASP.NET 核心 Web Api 应用程序中使用 System.IdentityModel.Tokens.Jwt 时是否可以检查 IP 地址?
我考虑添加一个声明,其中包含请求它的用户的 IP,并以某种方式为每个请求检查它。通常我会在 ASP.NET MVC 中使用 OnActionExecuting。
是否有基于中间件/授权的解决方案?
我这样创建我的 Jwt token 声明:
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;
}
请您参考如下方法:
您可以通过 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")] 注释要应用它的 Controller 或添加全局策略:
services.AddMvc(options =>
{
options.Filters.Add(new AuthorizeFilter("SameIpPolicy"))
})
