.net的各种框架啥的都不是很熟悉,当时只是想怎么实现快速校验授权确保api是通过验证之后才能打开。
我说的快速就是不需要写重复的样板代码,通过总体控制,最后发现,似乎也只能通过注解实现,不过也算是比较合理的,不然无法区分哪些需要哪些不需要了
那么实现原理就设计到cookie,session, token,前面的session是需要服务器存档用户信息的,服务器压力比较大,token的实现原理,在登录之后返回 一个token, 之后再每一个接口提交都传递token,
都传递token可以通过请求头提交也可以通过get,post这些提交,我这里探索两者均实现。
问了一些网友,说要实现便捷授权可以实现拦截器,拦截器类似注解,
那么只是单纯的拦截器还不是根本,而且我还是没看懂所以然咋自己通过写拦截器代码实现,
最后不得不直接用框架jwt bearer实现
jwt bearer 接入是只需要添加注解Auth 就可以实现对指定方法的鉴权。
using coreapiauth;
using coreapiauth.TestWebApi.AuthCenter.Utility;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
//注入JWT服务
builder.Services.AddScoped<IJwtService, JwtService>();
#region 注册JWT鉴权
var issuer = "study"; //Configuration["issuer"];
var audience = "lozn"; //Configuration["audience"];
var securityKey = "4A9A70D2-B8AD-42E1-B002-553BDEF4E76F";// Configuration["SecurityKey"];
//配置认证服务
builder.Services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
});
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) //默认授权机制名称
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters();
options.Events = new JwtBearerEvents()
{
OnMessageReceived = context =>
{
context.Token = context.Request.Query["access_token"];
return Task.CompletedTask;
}
};
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true, //是否验证Issuer
ValidateAudience = true, //是否验证Audience
ValidateLifetime = true, //是否验证失效时间
RequireExpirationTime = true, //过期时间
ValidateIssuerSigningKey = true, //是否验证IssuerSigningKey
ValidAudience = audience,
ValidIssuer = issuer,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(securityKey)),
//自定义校验规则:新登录后,之前的token无效
//AudienceValidator = (audiences, securityToken, validationParameters) =>
//{
// return audiences != null && audiences.FirstOrDefault().Equals(audience);
//}
};
});
#endregion
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
//app.UseHttpsRedirection();
//1.先开启认证
app.UseAuthentication();
//2.再开启授权
app.UseAuthorization();
app.MapControllers();
app.Run();
网上的文章似乎有些将的不是 core api,有些是core api,但是和我这边开发工具生成的模板代码又不一样,所以我这边是这样子的。
其中
app.UseAuthentication();
app.UseAuthorization();
似乎影响了最后能否正常通过鉴权,我之前是成功获取了token,但是一直卡无法鉴权成功, 请求头应该传递什么,最后得出的结论:Authorization
为请求头的key ,value为Bearer 登录返回的key
也可以通过get提交,上面的完整代码之所以可以get生效是因为有如下代码生效
options.Events = new JwtBearerEvents()
{
OnMessageReceived = context =>
{
context.Token = context.Request.Query["access_token"];
return Task.CompletedTask;
}
};
那么访问
http://lozn.vaiwan.com/api/Auth/GetAuthData?access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImN0eSI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiYWRtaW4iLCJOaWNrTmFtZSI6Ik5ldENvcmUiLCJSb2xlIjoiQWRtaW5pc3RyYXRvciIsImV4cCI6MTY1NDA3MDY2NywiaXNzIjoic3R1ZHkiLCJhdWQiOiJsb3puIn0.7GQOQybhFLFXpL0FO31WNdm-o8MYJHS5F-y1xB-PQPU
我写的这个源码有两个地方都用到了秘钥,
其中在启动的代码中配置的是如下
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true, //是否验证Issuer
ValidateAudience = true, //是否验证Audience
ValidateLifetime = true, //是否验证失效时间
RequireExpirationTime = true, //过期时间
ValidateIssuerSigningKey = true, //是否验证IssuerSigningKey
ValidAudience = audience,
ValidIssuer = issuer,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(securityKey)),
//自定义校验规则:新登录后,之前的token无效
//AudienceValidator = (audiences, securityToken, validationParameters) =>
//{
// return audiences != null && audiences.FirstOrDefault().Equals(audience);
//}
};
而在登录的时候返回 token用到的代码
public string GetToken(string name)
{
/**
* Claims(Payload)
* Claims包含了一些跟这个token有关的重要信息。JWT标准规定的字段:
*
* iss: The issuer of the token, 签发人
* sub: The subject of the token, 主题
* exp: Expiration Time. 过期时间(Unix时间戳格式)
* iat: Issued At. 签发时间(Unix时间戳格式)
* jti: JWT ID. 编号
* aud: audience. 受众
* nbf: Not Before. 生效时间
*
* 除了规定的字段外,可以包含其他任何JSON兼容的字段。
* */
var claims = new[]
{
new Claim(ClaimTypes.Name, name),
new Claim("NickName", "NetCore"),
new Claim("Role", "Administrator")
};
if (_configuration["SecurityKey"] == null)
{
throw new Exception("please config SecurityKey issuer audience at appsettings.json");
}
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["SecurityKey"]));
var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: _configuration["issuer"], //签发人
audience: _configuration["audience"],
claims: claims,
expires: DateTime.Now.AddMinutes(20), //20分钟有效期
signingCredentials: credentials);
var tokenStr = new JwtSecurityTokenHandler().WriteToken(token);
return tokenStr;
}
如果两者的issuer,audience,或者key不一致,一样是无法鉴权成功的,为了确保 SecurityKey在appsetting.json里面定义我直接加了检测,不存在直接抛出异常。
就可以成功实现访问,而非返回401,如果鉴权不成功是返回401的
最后能成功实现,还是参考了如下比较有参考意义的文章,前者并没有提到怎么去传递请求头,以及configuration参数怎么传递也没提及,所以搞的我一直没成功,
完整源码暂时不放出来了,因为第一篇文章已经算文章了,而我提供的program.cs也解决了如何读取configration的问题,以及写了一篇关于配置文件configration是啥玩意的问题。