ASP.NET 8 - Cookie 身份验证

目录

[什么是 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 视为特殊情况。

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

相关推荐
笔画人生2 小时前
Cursor + 蓝耘API:用自然语言完成全栈项目开发
前端·后端
有来技术3 小时前
ASP.NET Core 权限管理系统(RBAC)设计与实现|vue3-element-admin .NET 后端
vue.js·后端·c#·asp.net·.net
qq_12498707533 小时前
基于springboot的林业资源管理系统设计与实现(源码+论文+部署+安装)
java·vue.js·spring boot·后端·spring·毕业设计·计算机毕业设计
shuair3 小时前
springboot整合redisson单机模式
java·spring boot·后端
qq_12498707534 小时前
基于springboot的竞赛团队组建与管理系统的设计与实现(源码+论文+部署+安装)
java·vue.js·spring boot·后端·信息可视化·毕业设计·计算机毕业设计
Dr.Kun4 小时前
【鲲码园PsychoPy】Go/No-go范式
开发语言·后端·golang
源代码•宸4 小时前
Redis 攻略(Redis Object)
数据库·redis·后端·缓存·字符串·哈希表·type
林shir4 小时前
3-14-后端Web进阶(SpringBoot原理)
java·spring boot·后端
90的程序爱好者5 小时前
flask入门
后端·python·flask