2017-07-15 48 views
0

我正在構建一個在ASP.NET Core中使用JWT承載身份驗證的應用程序。我需要阻止用戶同時打開多個會話。我想知道是否有辦法使用Microsoft.AspNetCore.Authentication.JwtBearer中間件列出用戶的所有令牌,然後驗證是否有其他令牌爲該用戶發出,以使傳入的驗證請求無效。防止用戶與JWT令牌進行多個會話

如果聲明能夠在服務器上進行驗證,我猜想爲了做到這一點,服務器會記錄這些聲明以及擁有它們的用戶。對?

任何想法我如何實現這一目標?

+1

使用參考令牌當用戶relogs撤銷所有舊令牌。 – Mardoxx

+0

參考令牌?你能否詳細說明你的答案?謝謝。 –

+0

或者一個例子會很棒。謝謝。 –

回答

0

我已經實現了我的目標,在用戶登錄時在db中保存時間戳,在令牌的有效負載中添加該時間戳,然後添加額外的安全層以驗證JWT對數據庫,如果時間戳不匹配。這是用.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核2.0

Startup.cs配置JWT承載驗證:

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

     app.UseAuthentication(); 

     app.UseMvc(); 

     ........... 

    } 

要配置JWT承載認證:

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