我是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"中,然后它可以按角色应用身份验证过滤器。将其分配给用户,并限制其他用户使用未经授权的用户以及如何注销用户。
我看到您已经完成了登录步骤,所以首先添加服务。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");
}