Skip to main content
 首页 » 编程设计

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

2024年02月25日45daizhj

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