【问题标题】:Prevent users to have multiple sessions with JWT Tokens防止用户使用 JWT 令牌进行多个会话
【发布时间】:2017-12-20 05:38:37
【问题描述】:

我正在构建一个在 ASP.NET Core 中使用 JWT 不记名身份验证的应用程序。我需要防止用户同时打开多个会话。我想知道是否有办法使用 Microsoft.AspNetCore.Authentication.JwtBearer 中间件列出用户的所有令牌,然后验证是否为该用户颁发了其他令牌以使传入的身份验证请求无效。

如果声明能够在服务器上得到验证,我猜为了做到这一点,服务器会记录这些声明以及拥有它们的用户。对吧?

任何想法我该如何实现这一目标?

【问题讨论】:

  • 使用参考令牌并在用户重新登录时撤销所有旧令牌。
  • 参考标记?您能否详细说明您的答案?谢谢。
  • 或者一个例子会很棒。谢谢。
  • @Mardoxx 是对的。有关更多信息,请参阅youtu.be/BdKmZ7mPNns?t=12m54s 换句话说:您想要实现的功能无法通过 JWT 实现,除非您添加令牌撤销支持。如果你这样做了,那么你将失去 JWT(无状态)的主要好处,因此应该使用引用令牌。
  • 取决于您使用的 sts,可能已经作为 iat 声明包含在令牌中。

标签: asp.net-core jwt bearer-token


【解决方案1】:

我已经实现了我的目标,将用户登录时的时间戳保存在数据库中,将该时间戳添加到令牌的有效负载中,然后添加额外的安全层以针对数据库验证 JWT,如果时间戳返回 401不匹配。如果有人需要,这是使用 .net Core 2.0 实现的代码。

控制器:

    [HttpPost]
    [Route("authenticate")]
    public async Task<IActionResult> AuthenticateAsync([FromBody] UserModel user)
    {
        try
        {
            .......

            if (userSecurityKey != null)
            {
                var tokenHandler = new JwtSecurityTokenHandler();
                var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
                var tokenDescriptor = new SecurityTokenDescriptor
                {
                    Subject = new ClaimsIdentity(new Claim[]
                    {
                       // This claim allows us to store information and use it without accessing the db
                        new Claim("userSecurityKey", userDeserialized.SecurityKey.ToString()),
                        new Claim("timeStamp",timeStamp),
                        new Claim("verificationKey",userDeserialized.VerificationKey.ToString()),
                        new Claim("userName",userDeserialized.UserName)

                    }),
                    Expires = DateTime.UtcNow.AddDays(7),
                    SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key),
                        SecurityAlgorithms.HmacSha256Signature)
                };
                var token = tokenHandler.CreateToken(tokenDescriptor);
                var tokenString = tokenHandler.WriteToken(token);

               // Updates timestamp for the user if there is one
                VerificationPortalTimeStamps userTimeStamp = await _context.VerificationPortalTimeStamps.AsNoTracking().FirstOrDefaultAsync(e => e.UserName == userDeserialized.UserName);

                if (userTimeStamp != null)
                {
                    userTimeStamp.TimeStamp = timeStamp;
                    _context.Entry(userTimeStamp).State = EntityState.Modified;
                   await _context.SaveChangesAsync();
                }
                else
                {
                    _context.VerificationPortalTimeStamps.Add(new VerificationPortalTimeStamps { TimeStamp = timeStamp, UserName = userDeserialized.UserName });
                    await _context.SaveChangesAsync();
                }


                // return basic user info (without password) and token to store client side                   
                return Json(new
                {
                    userName = userDeserialized.UserName,
                    userSecurityKey = userDeserialized.SecurityKey,
                    token = tokenString
                });
            }

            return Unauthorized();

        }
        catch (Exception)
        {
            return Unauthorized();
        }
    }

然后,使用 .Net Core 2.0 配置 JWT 承载身份验证

Startup.cs:

 public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IServiceProvider provider)
    {
        .................     

        app.UseAuthentication();

        app.UseMvc();

        ...........

    }

配置 JWT Bearer 身份验证:

public IServiceProvider ConfigureServices(IServiceCollection services)
    {

        ............


        var key = Configuration["AppSettings:Secret"];

        byte[] keyAsBytes = Encoding.ASCII.GetBytes(key);

        services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        })
            .AddJwtBearer(o =>
            {
                o.RequireHttpsMetadata = false;
                o.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(keyAsBytes),
                    ValidateIssuer = false,
                    ValidateAudience = false,
                    ValidateLifetime = true

                };

                o.Events = new JwtBearerEvents
                {
                    OnAuthenticationFailed = context =>
                    {
                        if (Configuration["AppSettings:IsGodMode"] != "true")
                            context.Response.StatusCode = 401;


                        return Task.FromResult<object>(0);
                    }
                };
                o.SecurityTokenValidators.Clear();
                o.SecurityTokenValidators.Add(new MyTokenHandler());
            });

        services.AddMvc()                
            .AddJsonOptions(opt =>
            {
                opt.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
            });


        var provider = services.BuildServiceProvider();


        return provider;
    }

然后,我们在控制器中实现自定义验证如下:

    [Authorize]
    [HttpGet]
    [Route("getcandidate")]
    public async Task<IActionResult> GetCandidateAsync()
    {

        try
        {
            .....

            //Get user from the claim
            string userName = User.FindFirst("UserName").Value;

            //Get timestamp from the db for the user
            var currentUserTimeStamp = _context.VerificationPortalTimeStamps.AsNoTracking().FirstOrDefault(e => e.UserName == userName).TimeStamp;

           // Compare timestamp from the claim against timestamp from the db
            if (User.FindFirst("timeStamp").Value != currentUserTimeStamp)
            {
                return NotFound();
            }

            ...........

        }
        catch (Exception)
        {
            return NotFound();
        }
    }

【讨论】:

    猜你喜欢
    • 2019-08-09
    • 2016-10-21
    • 2016-11-28
    • 2020-02-13
    • 2019-09-24
    • 2010-12-19
    • 2021-08-17
    • 2016-12-13
    • 2018-11-01
    相关资源
    最近更新 更多