本文环境 Visual Studio 2022, .Net6
关于Cookie认证见,上篇 http://t.csdn.cn/ebmkT本文是接着添加Jwt认证的。流程简单代码不多。
本案放到一个公用的类里面
///
/// 配置类(假定从配置或库取得的,这些信息多次使用)
///
public class JwtConfig
{
//谁颁发的(本案没用)
public static string Issuer { get; set; } = "serverIssuer";
//颁发给谁(本案没用)
public static string Audience { get; set; } = "clientAudience";
//令牌密码 AddJwtBearer 时用
private static string sKey = "ACB83f96a142409e";
//对称秘钥
public static SymmetricSecurityKey SigningKey { get; private set; } = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(sKey)
);
//生成 Token 串用
public static SigningCredentials Credentials { get; set; } = new SigningCredentials(SigningKey, SecurityAlgorithms.HmacSha256);
}
这里只有Jwt补充的部分,其余见
上篇Cookie认证 http://t.csdn.cn/ebmkT
//...略
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(option => option.LoginPath = "/Home/Login")
//1)添加Jwt
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true, //是否验证秘钥
IssuerSigningKey = JwtConfig.SigningKey, //秘钥
ValidateIssuer = false, //是否验证必须有颁发人,默认是True
ValidateAudience = false, //是否验证必须有颁发给谁 ,默认是True
ValidateLifetime = false, //是否验证必须有生效时间,默认是True
RequireExpirationTime=false, //是否验证必须有过期时间,默认是True
ClockSkew = TimeSpan.Zero //时间偏差
};
options.Events = new JwtBearerEvents()
{
//如下3个事件,如果要拦截或改写一些事儿比较有用
//首次收到协议消息时调用
OnMessageReceived = context => {
var url = context.Request.Path.Value;
appRef?.Logger.LogInformation($"OnMessageReceived Request.Path {url}");
var token = context.Request.Headers["Authorization"];
appRef?.Logger.LogInformation($"OnMessageReceived Token {token}");
return Task.CompletedTask;
},
//安全令牌已通过验证并生成 ClaimsIdentity 后调用
OnTokenValidated = context =>
{
var url = context.Request.Path.Value;
appRef?.Logger.LogInformation($"OnTokenValidated Request.Path {url}");
var cm = context.Principal?.Identities; //下断点,这会看见 Jwt的身份
return Task.CompletedTask;
},
//身份验证失败
OnAuthenticationFailed = context =>
{
var url = context.Request.Path.Value;
appRef?.Logger.LogInformation($"OnAuthenticationFailed Request.Path {url}");
var ex = context.Exception;
appRef?.Logger.LogInformation($"OnAuthenticationFailed Exception {ex}");
return Task.CompletedTask;
}
};
});
//...略
为方便就找个微软 Api Controller 例子添加代码。
代码按 1)、2)…n)这样的顺序看。
[ApiController]
[Route("api/[controller]")]
[Authorize(AuthenticationSchemes = AuthSchemes)] //启用认证
public class WeatherForecastController : ControllerBase, IActionFilter //IActionFilter 为了调试输出
{
//1)是这个串是 "Bearer,Cookies" 支持这两种
private const string AuthSchemes
= JwtBearerDefaults.AuthenticationScheme
+ "," + CookieAuthenticationDefaults.AuthenticationScheme;
private readonly ILogger<WeatherForecastController> _logger; //调试输出用
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[AllowAnonymous]
[HttpGet("JwtLogin")]
public IActionResult JwtLogin(string name, string psw)
{
string rtn = string.Empty;
if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(psw)) //假定认证了
{
//2)登录并返回token串,本例是最小化的token,只有一个登陆名(起码能知道是谁)。
//因为 AddJwtBearer 时关闭了大多数内容,如需要什么根据需求可自行增加
//附加的信息越多token也越长
var claims = new Claim[]
{
new Claim(ClaimTypes.Name, name),
};
var token = new JwtSecurityToken(
claims: claims
, signingCredentials: JwtConfig.Credentials //加密用参数
);
rtn = new JwtSecurityTokenHandler().WriteToken(token);
}
return Ok(rtn);
}
void IActionFilter.OnActionExecuting(ActionExecutingContext context)
{
var cad = (ControllerActionDescriptor)context.ActionDescriptor;
//3)输出调试信息
_logger.LogInformation($"{cad.ActionName}"
+ $" 用户登录状态 {User.Identity?.IsAuthenticated}"
+ $" 登录名 {User.Identity?.Name}"
+ $" 认证类型 {User.Identity?.AuthenticationType}"
+ $" 身份数量 {User.Identities.Count()}"
+ $" 所有身份 {string.Join(" -- ", User.Identities.Select(a => a.Name).ToArray())}" //调试看多个身份的 Name
);
}
void IActionFilter.OnActionExecuted(ActionExecutedContext context)
{
//...本案没用
}
//4)微软的天气预报例子测试用
private static readonly string[] Summaries = new[]{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
[HttpGet(Name = "GetWeatherForecast")]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
}
1)登录
2)用 Swagger 调用 JwtLogin 返回 Token 并复制串
3)把刚才复制的 Token 前面加 Bearer 粘贴到Swagger的认证窗口里面(锁头按钮)
4)调用函数测试
可以看见,传递了 Token返回值正常
5)调试信息,Cookie、Jwt都生效了
完