一、Jwt
JWT(JSON Web Token)是一种用于在网络上安全传输信息的开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间安全地传输信息。JWT常用于身份验证、信息交换和授权等方面。
1、Jwt的结构
JWT由三部分组成,它们通过点(.)分隔:
1.1.Header(头部):包含令牌的元数据,如令牌的类型(JWT)和签名算法(如HMAC SHA256或RSA)。被Base64编码后作为JWT的第一部分。
示例(JSON格式):
{
"alg": "HS256",
"typ": "JWT"
}
1.2.Payload(载荷):包含要传递的信息,如用户ID、角色、权限等。载荷分为注册声明、公共声明和私有声明。被Base64编码后作为JWT的第二部分。
注册声明包括:
- iss(签发者):标识谁签发了这个令牌。
- sub(主题):标识这个令牌所面向的用户。
- aud(受众):标识这个令牌的接收对象。
- exp(过期时间):标识令牌的有效期限。
- nbf(生效时间):标识令牌开始生效的时间。
- iat(签发时间):标识令牌的生成时间。
- jti(JWT ID):令牌的唯一标识符。
示例(JSON格式):
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
1.3.Signature(签名):使用头部和载荷的Base64编码字符串以及一个密钥(secret)进行签名计算,以确保信息传输的安全性。
示例计算签名:
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
2、Jwt的配置
2.1、包 Microsoft.AspNetCore.Authentication.JwtBearer
2.2、配置Jwt参数
在 appsettings.json
文件中,配置JWT的相关参数,如密钥(Secret)、颁发者(Issuer)和受众(Audience)。
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Jwt": {
"Key": "ni-hai-yao-wo-zen-yang-YAOZENYANG!@#$%^&*()",
"Issuer": "JwtIssuer",
"Audience": "JwtAudience"
}
}
2.3、注册JWT认证服务
这包括配置JWT令牌验证参数,并添加JWT Bearer认证中间件。
//添加Jwt Bearer认证
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,//验证签名密钥
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration.GetSection("Jwt:Key").Value)),//签名密钥
ValidateIssuer = true,//验证发行人
ValidIssuer = builder.Configuration.GetSection("Jwt:Issuer").Value,//发行人
ValidateAudience = true,//验证受众
ValidAudience = builder.Configuration.GetSection("Jwt:Audience").Value,//受众
ValidateLifetime = true,//验证Token是否过期
ClockSkew = TimeSpan.Zero // 设置令牌时钟的偏差
};
//如果jwt过期,在返回的header中加入Token-Expired字段为true,前端在获取返回header时判断
options.Events = new JwtBearerEvents()
{
OnAuthenticationFailed = context =>
{
if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
{
context.Response.Headers.Add("Token-Expired", "true");
}
return Task.CompletedTask;
}
};
});
// 启用认证和授权中间件
app.UseAuthentication();
app.UseAuthorization();
2.4、生成JWT令牌
新建个Jwt生成帮助类
namespace Jwt_lianxi
{
public class JwtHelper
{
private readonly IConfiguration _configuration;
public JwtHelper(IConfiguration configuration)
{
_configuration = configuration;
}
public string GenerateJwtToken(User user)
{
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, user.Name),
new Claim(ClaimTypes.Name, user.Name)
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:Key"]));//签名密钥
var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: _configuration["Jwt:Issuer"],
audience: _configuration["Jwt:Audience"],
claims: claims,
expires: DateTime.Now.AddHours(1), // 过期时间
signingCredentials: credentials
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
}
//简单的用户模型
public class User
{
public string Name { get; set; }
public string Password { get; set; }
}
}
2.5、swagger界面的token图形化验证
为了方便验证token,所以在swagger界面添加一个界面化的token验证。
program.cs
//添加swagger服务并配置Jwt授权
builder.Services.AddSwaggerGen(options =>
{
//安全模式配置
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = "请输入token,格式为 Bearer xxxxxxxx(注意中间必须有空格)",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey,
BearerFormat = "JWT",
Scheme = "Bearer"
});
//添加安全要求
options.AddSecurityRequirement(new OpenApiSecurityRequirement {
{
new OpenApiSecurityScheme{
Reference =new OpenApiReference{
Type = ReferenceType.SecurityScheme,
Id ="Bearer"
}
},new string[]{ }
}
});
});
2.6、测试
最后新建控制器来测试
namespace Jwt_lianxi.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class AuthorizeTestController : ControllerBase
{
private readonly IConfiguration _configuration;
public AuthorizeTestController(IConfiguration configuration)
{
_configuration = configuration;
}
[HttpPost]
public string GetJwtToken(User user)
{
return new JwtHelper(_configuration).GenerateJwtToken(user);
}
[HttpGet]
[Authorize]//只有有权限的才可以调用这个接口
public ActionResult<string> Test1()
{
return "请求成功";
}
}
}
结果:调GetJwtToken接口会生成token,如果直接再调Test1接口,则会Error: response status is 401,需要先把GetJwtToken生成的token放入界面右上角的Authorize上验证,再次调Test1接口就成功了。界面如下
二、Cookie
1、cookie概述
cookie 常用于保存用户相关并保存在客户端电脑上的一段数据
1.1、cookie基础
//读取cookie内容
Request.Cookies["Key"];
//添加cookie值
1、正常添加
Response.Cookies.Append(key, value);
2、配置
ookieOptions option = new CookieOptions();
option.Expires = DateTime.Now.AddMilliseconds(10);
Response.Cookies.Append(key, value, option);
//删除cookie
Response.Cookies.Delete("Key");
2、Cookie认证、授权
基于Cookie进行身份认证,通常的方案是用户成功登录后,服务端将用户的必要信息记录在Cookie中,并发送给浏览器,后续当用户发送请求时,浏览器将Cookie传回服务端,服务端就可以通过Cookie中的信息确认用户信息了。
- 配置认证和授权
// 添加 Cookie 认证
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.LoginPath = "/Home/Login";//如果一个未授权的用户尝试访问应用程序安全的URL时将会被自动跳转到/Home/Login
options.LogoutPath = new PathString("/Account/Logout");
options.AccessDeniedPath = "/account/accessdenied"; // 设置拒绝访问路径
options.Cookie.Name = ".AspNetCore.Cookies";//成功登录后,将为用户创建一个cookie,名字是这个
options.ExpireTimeSpan = TimeSpan.FromMinutes(10);
options.SlidingExpiration = true;
//配置回调函数
options.Events.OnSigningIn = context =>
{
Console.WriteLine($"{context.Principal.Identity.Name} 正在登录...");
return Task.CompletedTask;
};
options.Events.OnSignedIn = context =>
{
Console.WriteLine($"{context.Principal.Identity.Name} 已登录");
return Task.CompletedTask;
};
options.Events.OnSigningOut = context =>
{
Console.WriteLine($"{context.HttpContext.User.Identity.Name} 注销");
return Task.CompletedTask;
};
options.Events.OnValidatePrincipal += context =>
{
Console.WriteLine($"{context.Principal.Identity.Name} 验证 Principal");
return Task.CompletedTask;
};
});
// 添加授权
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("RequireLoggedIn", policy =>
policy.RequireAuthenticatedUser());//在需要权限的控制器上可加上特性[Authorize(Policy = "RequireLoggedIn")]。如果没配置这个,则默认[Authorize]就可以了
});
app.UseAuthentication();
app.UseAuthorization();
- 创建测试控制器
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;
namespace MyWebApi.Controllers
{
[ApiController]
[Route("[controller]")]
public class AccountController : ControllerBase
{
[HttpPost("login")]
public async Task<IActionResult> Login(string username, string password)
{
// 验证用户名和密码(这里只是示例,实际应用中应使用更安全的方式)
if (username == "test" && password == "password")
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, username)
};
var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
var authProperties = new AuthenticationProperties
{
IsPersistent = true // 是否持久化Cookie
ExpiresUtc = DateTimeOffset.UtcNow.AddDays(7) // Cookie过期时间
};
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity), authProperties);
return Ok(new { code = 200, message = "登录成功" });
}
return Unauthorized(new { code = 401, message = "登录失败" });
}
[HttpPost("logout")]
public async Task<IActionResult> Logout()
{
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
return Ok(new { code = 200, message = "注销成功" });
}
[HttpGet("accessdenied")]
public IActionResult AccessDenied()
{
return Forbid();
}
}
}
-
Claim
:表示一条信息的声明。以我们的身份证为例,里面包含姓名、性别等信息,如“姓名:张三”、“性别:男”,这些都是Claim。 -
Identity
:表示一个身份。对于一个ClaimsIdentity
来说,它是由一个或多个Claim组成的。我们的身份证就是一个Identity。 -
Principal
:表示用户本人。对于一个ClaimsPrincipal
来说,它是由一个或多个ClaimsIdentity组成的。想一下,我们每个人的身份不仅仅只有一种,除了身份证外,还有驾驶证、会员卡等。
三、Session
1、基础配置
//需要包 Microsoft.Extensions.Caching.StackExchangeRedis
builder.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration = "localhost"; // Redis 服务器地址
options.InstanceName = "aaaa";
});
//配置session
builder.Services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromMinutes(5);// 会话空闲超时时间
options.Cookie.Name = "SessionId1";
options.Cookie.HttpOnly = true;// Cookie仅通过HTTP传输
options.Cookie.IsEssential = true; // 标记为必需
});
app.UseSession();
2、测试
//设置session
HttpContext.Session.SetString("age", "18");//还有SetInt32
//获取session
string str = HttpContext.Session.GetString("age");//还有GetInt32
//移除session
HttpContext.Session.Remove("age");
//清除所有session
HttpContext.Session.Clear();
一般在用户登录成功时,SetString设置username,以此来判断用户是否登录。用户注销则Remove这个username