欢迎光临
我们一直在努力

ASP.NET 8 - Cookie 身份验证

目录

什么是 Cookie?

它是如何运作的?

如何在.NET8中实现?

项目运行


如果您喜欢此文章,请收藏、点赞、评论,谢谢,祝您快乐每一天。

示例代码:https://download.csdn.net/download/hefeng_aspnet/92599924

什么是 Cookie?

在网络环境中,Cookie是存储用户信息的对象,用于根据用户在网站上的操作来识别、跟踪和个性化用户体验。

它是如何运作的?

当您登录网站时,处理您请求的服务器会在登录成功后创建一个包含 ID、用户数据以及一些信息(例如 cookie 类型、过期时间、刷新策略等)的 cookie。浏览器收到 cookie 后,会负责存储该 cookie 并将其在每次请求中发送给服务器。服务器会在每次请求时检查 cookie 的属性。

如何在.NET8中实现?

本教程需要您创建一个 ASP.NET Core Web API 并安装以下软件包:

<PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="2.3.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.16" />

我创建了一个简单的实体来表示用户,包含姓名、邮箱、密码和角色。您可以通过此链接查看。

对于表映射,请使用以下配置:

public class UserConfiguration : IEntityTypeConfiguration<User> {     public void Configure(EntityTypeBuilder<User> builder)     {         builder.HasKey(x => x.Id);         builder.HasIndex(x => x.Email).IsUnique();

        builder.Property(x => x.Name).HasMaxLength(100).IsRequired();         builder.Property(x => x.Email).HasMaxLength(100).IsRequired();         builder.Property(x => x.Password).HasMaxLength(250).IsRequired();     } }

我将Email属性设置为索引,并将其标记为唯一,以确保不同用户无法使用相同的电子邮件地址注册。这样,它还可以有效地用作登录时的筛选参数。

在 DbContext 中,请确保按如下方式配置 UserRoles 关系:

public class AppDbContext : DbContext {     public DbSet<User> Users { get; set; }     public DbSet<Role> Roles { get; set; }

    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)     {     }

    protected override void OnModelCreating(ModelBuilder modelBuilder)     {         base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<User>()             .HasMany(u => u.Roles)             .WithMany(r => r.Users)             .UsingEntity<Dictionary<string, object>>(                 "UserRoles",                 j => j.HasOne<Role>().WithMany().HasForeignKey("RoleId"),                 j => j.HasOne<User>().WithMany().HasForeignKey("UserId"),                 j =>                 {                     j.HasKey("UserId", "RoleId");                     j.ToTable("UserRoles");                 });     } }

在代码仓库中,我们只需要通过用户的电子邮件地址进行搜索:

public class UserRepository : IUserRespository {     private readonly AppDbContext _context;     private readonly DbSet<User> _users;

    public UserRepository(AppDbContext context)     {         _context = context;         _users = _context.Set<User>();     }

    public async Task<User?> GetByEmail(string email)     {         return await _users             .Where(x => x.Email == email)             .Include(x => x.Roles)             .FirstOrDefaultAsync();     } } 现在我们可以专注于登录服务了,它非常简单:它接收用户输入,根据电子邮件地址在数据库中查找用户,验证密码,然后返回该用户。以下是实现代码:

public class LoginService : ILoginService {     protected readonly IUserRespository _respository;

    public LoginService(IUserRespository respository)     {         _respository = respository;     }

    public async Task<TResult<User>> Login(LoginDto dto)     {         var result = new TResult<User>();         var user = await _respository.GetByEmail(dto.Email);

        if (user == null)         {             result.AddError($"User {dto.Email} not found!");             return result;         }

        if (!user.IsPasswordMatch(dto.Password))         {             result.AddError($"Password Incorrect!");             return result;         }

        result.Success(user);         return result;     } } 现在需要配置Cookie 身份验证,请打开Program.cs文件并添加以下配置:

builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)     .AddCookie(options =>     {         options.Cookie.Name = "CookieAuthSystem";         options.Cookie.HttpOnly = true;         options.Cookie.SecurePolicy = CookieSecurePolicy.Always;         options.Cookie.SameSite = SameSiteMode.Strict;         options.ExpireTimeSpan = TimeSpan.FromMinutes(5);         options.SlidingExpiration = true;         options.LoginPath = "/auth/login";         options.AccessDeniedPath = "/auth/access-denied";         // Hanlder the unauthorized access         options.Events.OnRedirectToLogin = context =>         {             context.Response.StatusCode = 401;             return Task.CompletedTask;         };     }) 配置说明: Cookie.HttpOnly = true >> 确保客户端 JavaScript 无法访问身份验证 cookie,从而有助于防止 XSS(跨站脚本)攻击。

Cookie.SecurePolicy = CookieSecurePolicy.Always >> Cookie 将仅通过 HTTPS 请求发送。

Cookie.SameSite = SameSiteMode.Strict >> 防止 cookie 随跨域请求发送,有助于防止 CSRF(跨站请求伪造)攻击。

ExpireTimeSpan = TimeSpan.FromMinutes(5) >> 设置 Cookie 生命周期为 5 分钟。

SlidingExpiration = true >> 如果当前 cookie 即将过期(剩余时间过半),则请求新的 cookie。这可以防止用户在连续访问网站期间重复登录,从而改善用户体验。

配置好Cookie 身份验证后,我们就可以实现生成 Cookie 的服务了:

public class CookieService : ICookieService {

    public CookieDto GenerateCookie(User user)     {         var claims = new List<Claim>         {             new Claim(ClaimTypes.Name, user.Name),             new Claim(ClaimTypes.Email, user.Email),         };

        foreach (var role in user.Roles)         {             claims.Add(new Claim(ClaimTypes.Role, role.Name));         }

        var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);

        var authProperties = new AuthenticationProperties         {             IsPersistent = true,             AllowRefresh = true,             ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(5),         };

        return new CookieDto(claimsIdentity, authProperties);     } 在声明身份中,我们提供用户名、电子邮件及其角色,并定义身份验证类型,即基于 cookie 的身份验证。

在 Cookie 属性中,我们设置了持久性、允许请求新 cookie 以及定义过期时间。

现在我们来看身份验证控制器:

[ApiController] [Route("auth")] public class AuthController : Controller {     protected readonly ILoginService _loginService;     protected readonly ICookieService _cookieService;

    public AuthController(ILoginService loginService, ICookieService cookieService)     {         _loginService = loginService;         _cookieService = cookieService;     }

    [HttpPost("login")]     public async Task<IActionResult> Login([FromBody] LoginDto request)     {         var userResult = await _loginService.Login(request);

        // Check if login is successful         if (!userResult.IsSuccess || userResult.Value == null)         {             return BadRequest(userResult.ErrorMessage);         }

        var cookie = _cookieService.GenerateCookie(userResult.Value);

        await HttpContext.SignInAsync(             CookieAuthenticationDefaults.AuthenticationScheme,             new ClaimsPrincipal(cookie.Claims),             cookie.Properties);

        return LocalRedirect("/auth/logged");     }

    [HttpPost("logout")]     public async Task<IActionResult> Logout()     {         await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);         return Ok("User Logout Successful");     }

    [HttpGet("access-denied")]     public IActionResult NotAllowed()     {         return Unauthorized("You not allowed to access this resource");     }

    [Authorize]     [HttpGet("logged")]     public IActionResult Logged()     {         var userName = User.FindFirst(ClaimTypes.Name)?.Value;

        return Ok($"Hello {userName}");     } }

登录:创建一个包含用户信息的 cookie,并将用户重定向到/logged页面,该页面包含存储在 brownser 中的 cookie。

注销:将 Cookie 变为无效并从浏览器中删除。

访问被拒绝:每当有人尝试访问未经授权的资源时,他们都会被重定向到此路径。

受保护的端点:

[ApiController] [Route("api")] public class ApiController : Controller {

    [Authorize(Roles = "User")]     [HttpGet("user")]     public ActionResult UserController()     {         return Ok("Hello User");     }

    [Authorize(Roles = "Manager")]     [HttpGet("manager")]     public ActionResult ManagerController()     {         return Ok("Hello Manager");     }

    [Authorize(Roles = "Admin")]     [HttpGet("admin")]     public ActionResult AdminController()     {         return Ok("Hello Admin");     } }

项目运行

使用 Swagger 登录成功后,用户将收到以下内容:

您可以在浏览器中检查 cookie,打开浏览器的检查工具,进入应用程序,然后选择 cookie,您会看到类似这样的内容:

但是,如果您尝试使用以下 JavaScript 代码在浏览器控制台中访问 c​​ookie:

document.cookie 你可能会收到一个空字符串,因为我们在 program.cs 文件中配置了 cookie,Cookie.HttpOnly = true。

如果用户尝试访问无权访问的资源,将被重定向到以下路径:

在本教程中,您注意到我们配置 cookie 以使其在 https 环境中工作,但我运行在 http 环境中,这是因为浏览器将 localhost 视为特殊情况。

如果您喜欢此文章,请收藏、点赞、评论,谢谢,祝您快乐每一天。

赞(0)
未经允许不得转载:171主机测评 » ASP.NET 8 - Cookie 身份验证
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址