我已經實現了我的目標,在用戶登錄時在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();
}
}
使用參考令牌當用戶relogs撤銷所有舊令牌。 – Mardoxx
參考令牌?你能否詳細說明你的答案?謝謝。 –
或者一個例子會很棒。謝謝。 –