如何在.net core 3.1中添加Logout方法,并在cookie中存储jwt令牌



我是c#和。net的新手。我无法理解如何实现注销方法与JWT令牌在这里我附加我的整个代码。我已经使用JWT实现了身份验证和授权,我正在从appsetting.json访问登录凭据。

这是我的appsetting。Json中我提到了用户名,密码和秘钥

{"UserCred":{
"Username":"test",
"Password":"test"
},

"AppSettings": {
"Secret": "This is my secret key"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}

DataAcess UserRepository

using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using loginApi.Helpers;
using loginApi.Models;
using Microsoft.Extensions.Configuration;
namespace loginApi.DataAccess
{
public class UserRepository : IUserRepository
{

private List<User> _users = new List<User>
{

};
private readonly AppSettings _appSettings;
private readonly IConfiguration _configuration;
public UserRepository(IOptions<AppSettings> appSettings,IConfiguration configuration)
{
_appSettings = appSettings.Value;
_configuration = configuration;
var _username = _configuration.GetSection("UserCred").GetSection("Username").Value;
var _Password = _configuration.GetSection("UserCred").GetSection("Password").Value;

_users = new List<User> 
{ 
new User { Username = _username.ToString(), Password = _Password.ToString()}
};
}
public AuthenticateResponse Authenticate(AuthenticateRequest model)
{

var user = _users.SingleOrDefault(x => x.Username == model.Username && x.Password == model.Password);

if (user == null) return null;

var token = generateJwtToken(user);
return new AuthenticateResponse(user, token);
}
public IEnumerable<User> GetAll()
{
return _users;
}
public User GetByUserName(string username)
{
return _users.FirstOrDefault(x => x.Username== username);
}

private string generateJwtToken(User user)
{

var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[] { new Claim("username", user.Username.ToString()) }),
Expires = DateTime.UtcNow.AddDays(7),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
}
}

DataAccess IUserRepository

using loginApi.Models;
using System.Collections.Generic;
namespace loginApi.DataAccess
{
public interface IUserRepository
{
AuthenticateResponse Authenticate(AuthenticateRequest model);
IEnumerable<User> GetAll();
User GetByUserName(string username);
}
}

控制器 UserController.cs

using Microsoft.AspNetCore.Mvc;
using loginApi.Models;
using loginApi.DataAccess;
namespace loginApi.Controllers
{
[ApiController]
[Route("[controller]")]
public class UserController : ControllerBase
{
private IUserRepository _userRepository;
public UserController(IUserRepository userRepository)
{
_userRepository = userRepository;
}
[HttpPost("Controller")]
public IActionResult Authenticate(AuthenticateRequest model)
{
var response = _userRepository.Authenticate(model);
if (response == null)
return BadRequest(new { message = "Username or password is incorrect" });
return Ok(response);
}
}
}

Helpers文件夹:-

助手 AuthorizeAttribute.cs

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using loginApi.Models;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class AuthorizeAttribute : Attribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
var user = (User)context.HttpContext.Items["User"];
if (user == null)
{

context.Result = new JsonResult(new { message = "Unauthorized" }) { StatusCode = StatusCodes.Status401Unauthorized };
}
}
}

助手 JwtMiddleware.cs

using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using loginApi.DataAccess;
using loginApi.Models;
namespace loginApi.Helpers
{
public class JwtMiddleware
{
private readonly RequestDelegate _next;
private readonly AppSettings _appSettings;
public JwtMiddleware(RequestDelegate next, IOptions<AppSettings> appSettings)
{
_next = next;
_appSettings = appSettings.Value;
}
public async Task Invoke(HttpContext context, IUserRepository userRepository)
{
var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();
if (token != null)
attachUserToContext(context, userRepository, token);
await _next(context);
}
private void attachUserToContext(HttpContext context, IUserRepository userRepository, string token)
{
try
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
tokenHandler.ValidateToken(token, new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false,
ClockSkew = TimeSpan.Zero
}, out SecurityToken validatedToken);
var jwtToken = (JwtSecurityToken)validatedToken;               
var userName = jwtToken.Claims.First(x => x.Type == "username").Value;

context.Items["User"] = userRepository.GetByUserName(userName);
}
catch
{

}
}
}
}

Models文件夹

模型 AppSettings.cs

namespace loginApi.Models
{
public class AppSettings
{
public string Secret { get; set; }
}
}

模型 AuthenticateRequest.cs

using System.ComponentModel.DataAnnotations;
namespace loginApi.Models
{
public class AuthenticateRequest
{
[Required]
public string Username { get; set; }
[Required]
public string Password { get; set; }
}
}

模型 AuthenticateResponse.cs

using loginApi.Models;
namespace loginApi.Models
{
public class AuthenticateResponse
{

public string Username { get; set; }
public string Token { get; set; }

public AuthenticateResponse(User user, string token)
{            
Username = user.Username;
Token = token;
}
}
}

模型 User.cs

using System.Text.Json.Serialization;
namespace loginApi.Models
{
public class User
{
public string Username { get; set; }
[JsonIgnore]
public string Password { get; set; }
}
}

Startup.cs

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using loginApi.Helpers;
using loginApi.DataAccess;
using loginApi.Models;
namespace loginApi
{
public class Startup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
// add services to the DI container
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
services.AddControllers();
services.AddSwaggerGen(); 
// configure strongly typed settings object
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
services.Configure<User>(Configuration.GetSection("UserCred"));
// configure DI for application services
services.AddScoped<IUserRepository, UserRepository>();
}
// configure the HTTP request pipeline
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseRouting();
// global cors policy
app.UseCors(x => x
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
// custom jwt auth middleware
app.UseMiddleware<JwtMiddleware>();
app.UseEndpoints(x => x.MapControllers());
app.UseSwagger();  
app.UseSwaggerUI(c => {  
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V2");  
}); 
}
}
}

JWT不是为注销而设计的,因为框架没有提供安全的开箱即用的解决方案。要了解关于注销JWT的更深入的解释,您可以阅读此回答并继续研究注销JWT的特定方法。

如果不需要严格的安全策略,那么实际上您可以从HTTP响应中删除Authorization标头。这样,前端将无法在接下来的请求中设置JWT。

例如,创建一个Logout()控制器动作,并在主体中添加以下代码:Response.Headers.Remove("Authorization");

对于登录和注销,我们将看到如何获取带有用户声明的JWT令牌,并将其存储在会话存储键JWToken&quot中,然后它可以按角色应用身份验证过滤器。将其分配给用户,并限制其他用户使用未经授权的用户以及如何注销用户。

我看到您已经完成了登录步骤,所以首先添加服务。AddAuthentication、服务。AddSession到Startup.cs文件。下面是代码:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddControllersWithViews();
services.AddRazorPages();

#region "JWT Token For Authentication Login"    
SiteKeys.Configure(Configuration.GetSection("AppSettings"));
var key = Encoding.ASCII.GetBytes(SiteKeys.Token);
services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromMinutes(60);
});

services.AddAuthentication(auth =>
{
auth.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
auth.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(token =>
{
token.RequireHttpsMetadata = false;
token.SaveToken = true;
token.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = true,
ValidIssuer = SiteKeys.WebSiteDomain,
ValidateAudience = true,
ValidAudience = SiteKeys.WebSiteDomain,
RequireExpirationTime = true,
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
};
});
}

然后在请求头的Authorization Bearer中添加JWToken:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
#region "JWT Token For Authentication Login"    
app.UseCookiePolicy();
app.UseSession();
app.Use(async (context, next) =>
{
var JWToken = context.Session.GetString("JWToken");
if (!string.IsNullOrEmpty(JWToken))
{
context.Request.Headers.Add("Authorization", "Bearer " + JWToken);
}
await next();
});
app.UseAuthentication();
app.UseAuthorization();
#endregion
app.UseEndpoints(endpoints =>
{
//Routing Area Admin    
endpoints.MapAreaControllerRoute(
name: "routeArea",
areaName: "Admin",
pattern: "Admin/{controller=Home}/{action=Index}/{id?}");
endpoints.MapRazorPages();
//Routing Front     
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Main}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
}

然后在appsetting中添加connection。json文件:

"ConnectionStrings": {
"DefaultConnection": "Server=#####;Database=YourDataBase;user=YourUser;password=YourPW;Trusted_Connection=False;"
}

最后,每个用户的注销函数在这里:

[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Logout()
{
HttpContext.Session.Clear();
return RedirectToAction("Index");
}

最新更新