【问题标题】:Check IP with JWT Authorization in ASP.NET Core Web Api在 ASP.NET Core Web Api 中使用 JWT 授权检查 IP
【发布时间】:2019-04-21 18:22:31
【问题描述】:

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

我考虑添加一个包含请求它的用户 IP 的声明,并以某种方式检查每个请求。通常我会在 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 索赔的可能解决方案? 也许我必须像这样阅读声明(测试代码,没有空检查等):

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-core jwt asp.net-core-webapi


    【解决方案1】:

    您可以通过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"))
    })
    

    【讨论】:

    • 嘿,谢谢!我明白了,HandleRequirementAsync 方法被触发。但是context.User.FindFirst 给了我我猜的身份实例的用户声明?我需要在我的 JWT 令牌中添加的声明(请参阅有关如何添加这些声明的原始帖子。顺便说一句,JWT 授权工作正常。
    • 那么你有一个奇怪的设置。通常,您的身份服务器(生成 JWT 令牌)和资源服务器是两个不同的应用程序。在资源服务器(仅限 WebAPI,无 MVC)中,您通常使用 JWT 进行授权,使用 Cookie 授权 MVC。您是否真的通过 Ajax(或邮递员或其他插件)调用给定的方法,并在请求中包含 JWT 不记名令牌?
    • 是的,我使用 Postman。不使用令牌会给我一个未经授权的错误代码。在我的设置中只有一台服务器。我编辑了我的帖子,添加了一个“可能的”解决方案。这样我就可以从 jwt 中读取声明。目前我的身份用户没有声明集。我不想打数据库。但我想我必须添加身份声明并将它们与我的 JWT 同步并更新 IP 声明的 IP 地址?!
    • 我认为您的(默认)身份验证方案有问题。身份验证方案告诉系统使用谁的声明(如果您使用多个身份验证)。如果您将 Cookie 身份验证方案设置为默认值,它将始终使用其声明,而不是来自 JWT 的声明。您可以将 JWT 身份验证模式设置为默认值,并在需要 [Authorize(AuthenticationSchemes = "CookieAuthenticationDefaults.AuthenticationScheme")] 的地方注释 Cookie/Identity。
    • 是的,对不起。我错过了它。是的,如果您希望它评估为成功,您需要致电context.Succeed(requirement);。但是,如果您想保证它失败,请仅调用.Fail()(不会针对此要求评估其他授权处理程序)。通过不调用.Fail(),您允许下一个处理程序成功。见Why would I want multiple handlers for a requirement?
    【解决方案2】:

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

    缺少分号:

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

    【讨论】:

      【解决方案3】:

      ClaimsPrincipal.Current.FindFirst(claim => claim.Type == "ipaddr")

      【讨论】:

        猜你喜欢
        • 2023-03-12
        • 2020-02-11
        • 2016-12-11
        • 2018-01-09
        • 2021-10-05
        • 1970-01-01
        • 2020-05-18
        • 2018-08-19
        • 2020-09-25
        相关资源
        最近更新 更多