前言
http协议本身是一种无状态的协议。所以客户端的每次请求,服务端是不清楚其身份的,需要客户端每次都要将身份信息传入,服务进行验证,才能达到安全验证的目的。
传统的Web用户验证:
1、客户端传入用户名和密码---2、后端验证成功后保存session信息,并将session信息返回给客户端--3、客户端保存session信息到cookie中---
4、客户端再请求需要的资源时,将cookie中的session信息传给服务端--5、服务端验证信息是否正确,正确则进行相应的操作,不正确则返回登录页面或者提示错误。
当然传统的session-cookie方式是可以达到用户身份验证的目的,但是在分布式的场景下就不好使了。因为session是存在当时对应的服务器中的,当我们部署多台服务器,并采用了负载均衡时,session不共享时,只能在其验证的服务器上才能通过,而无法达到多台服务器协同负载工作。(当然也可以通过将session信息存于数据库或者是分布式缓存中,每次需要验证时从数据库或者缓存中获取做验证,但是这样就多了一重数据库操作,或者是缓存出问题时无法验证)。
Token验证模式:
1、客户端使用用户名密码来请求服务器--2、服务器验证用户信息,成功则返回客户端一个token信息--3、客户端留存token信息,在每次请求时将token一并传给服务端--4、服务端验证token,正确则返回数据。
Jwt(Json web token), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的。它主要由3部分数据组成:头部(header),载荷(payload),签名(signature)。其实它就是token,服务端验证成功后按照算法组成jwt,返回给客户端;客户端请求服务端附带jwt,服务端验证jwt正确性,正确则返回对应的数据
.netcore实现jwt身份验证(同时集成swagger)
1、在appsettings.json文件中配置(注意这些配置信息的安全性,特别是secretkey。secretkey长度一般要求16位或以上)
"JwtSettings": {
"Issuer": "john_yong",
"Audience": "user",
"SecretKey": "123456789123456789"
},
2、在startup.cs的ConfigureServices(IServiceCollection services)方法中配置
services.Configure<JwtSetting>(Configuration.GetSection("JwtSettings"));
JwtSetting setting = new JwtSetting();
Configuration.Bind("JwtSettings", setting);
#region swagger
services.AddSwaggerGen(p =>
{
p.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo
{
Version = "v1",
Title = "SimpleTest API 接口文档",
//Contact = new OpenApiContact
//{
// Name = "john_yong",
// Email = "gxunet@163.com",
// Url = new Uri("http://xxxx.com"),
//},
});
//p.OperationFilter<HttpHeaderOperation>(); // 添加httpHeader参数
#region 启用swagger验证功能
p.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme()
{
Description = "在下框中输入请求头中需要添加Jwt授权Token:Bearer Token",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey,
BearerFormat = "JWT",
Scheme = "Bearer"
});
p.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference {
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
new string[] { }
}
});
#endregion
// 为 Swagger JSON and UI设置xml文档注释路径
var xmlPath = Path.Combine(AppContext.BaseDirectory, "SimpleTestApplication.xml");
p.IncludeXmlComments(xmlPath );
});
#endregion
#region 添加验证服务
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(p =>
{
p.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ClockSkew = TimeSpan.FromSeconds(30),
ValidateIssuerSigningKey = true,
ValidAudience = setting.Audience,
ValidIssuer = setting.Issuer,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(setting.SecretKey))
};
});
#endregion
在 Configure(IApplicationBuilder app, IWebHostEnvironment env)方法中配置
//启用授权
app.UseAuthentication();
app.UseAuthorization();
JwtSetting.cs
/// <summary>
///
/// </summary>
public class JwtSetting
{
/// <summary>
/// 证书颁发者
/// </summary>
public string Issuer { get; set; }
/// <summary>
/// 角色
/// </summary>
public string Audience { get; set; }
/// <summary>
/// 加密字符串
/// </summary>
public string SecretKey { get; set; }
}
3、创建AuthController提供api验证获取jwt
[ApiController]
[Route("[controller]")]
public class AuthController : Controller
{
private JwtSetting _jwtSettings;
public AuthController(IOptions<JwtSetting> jwtSetting)
{
_jwtSettings = jwtSetting.Value;
}
/// <summary>
/// 获取token
/// </summary>
/// <param name="userName"></param>
/// <param name="pwd"></param>
/// <returns></returns>
[AllowAnonymous]
[HttpGet("getToken")]
public ResponseFormatDto<TokenInfo> GetToken(string userName, string pwd)
{
double expiredMinute = 30; //过期时间30分钟
DateTime expiredTime = DateTime.Now.AddMinutes(expiredMinute);
//TODO验证用户
if (userName == "admin" && pwd == "1234560")
{
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Nbf,$"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}") ,
new Claim (JwtRegisteredClaimNames.Exp,$"{new DateTimeOffset(expiredTime).ToUnixTimeSeconds()}"),
new Claim(ClaimTypes.Name, userName)
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSettings.SecretKey));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var jwtSecurityToken = new JwtSecurityToken(
issuer: _jwtSettings.Issuer,
audience: _jwtSettings.Audience,
claims: claims,
expires: expiredTime,
signingCredentials: creds);
string token = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);
TokenInfo tokenInfo = new TokenInfo()
{
Token = token,
Expired = expiredTime
};
return HelperTool.GetResponse(tokenInfo);
}
else
{
return HelperTool.GetResponse<TokenInfo>(null, Status.Fail, "username or password is incorrect.");
}
}
}
TokenInfo.cs
/// <summary>
/// token information
/// </summary>
public class TokenInfo
{
/// <summary>
/// token
/// </summary>
public string Token { get; set; }
/// <summary>
/// 过期时间
/// </summary>
public DateTime Expired { get; set; }
}
HelperTool.cs是简单通用的构造统一的返回实体ResponseFormatDto(此处略)
结果示例
-------------------------------------
以上就是jwt的简单介绍以及应用。但是在实际项目中(特别是微服务流行的当下),用户验证、token管理一般不会在业务项目中实现的,基本上都是建立用户中心管理用户,鉴权用户信息,生成token等。
一般用Identity 4实现的,实际的业务项目则是提供token参数传入,在用户中心中验证。