.net core webapi集成jwt实现身份认证
为什么使用JWT?
JWT实际上就是一个字符串,它由三部分组成,头部、载荷与签名。JWT不仅可用于认证,还可用于信息交换。善用JWT有助于减少服务器请求数据库的次数。适用于多客户端的前后端解决方案,JWT 是无状态化的,更适用于 RESTful 风格的接口验证。本文主要介绍使用JWT进行接口身份认证。
一.准备工作
(1).添加NuGet程序包
Microsoft.AspNetCore.Authentication.JwtBearer
(2).appsettings.json
"JwtConfig": {
"SecretKey": "d0ecd23c-dfdb-4005-a2ea-0fea210c858a", // **
"Issuer": "liucheng", // 颁发者
"Audience": "liucheng", // 接收者
"Expired": 30 // 过期时间(30min)
},
二.创建JWT配置类
/// <summary>
/// jwt配置
/// </summary>
public class JwtConfig : IOptions<JwtConfig>
{
public JwtConfig Value => this;
public string SecretKey { get; set; }
public string Issuer { get; set; }
public string Audience { get; set; }
public int Expired { get; set; }
public DateTime NotBefore => DateTime.UtcNow;
public DateTime IssuedAt => DateTime.UtcNow;
public DateTime Expiration => IssuedAt.AddMinutes(Expired);
private SecurityKey SigningKey => new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecretKey));
public SigningCredentials SigningCredentials =>
new SigningCredentials(SigningKey, SecurityAlgorithms.HmacSha256);
}
三.创建JWT工具类
public class GenerateJwt
{
private readonly JwtConfig _jwtConfig;
public GenerateJwt(IOptions<JwtConfig> jwtConfig)
{
_jwtConfig = jwtConfig.Value;
}
/// <summary>
/// 生成token
/// </summary>
/// <param name="sub"></param>
/// <param name="customClaims">携带的用户信息</param>
/// <returns></returns>
public JwtTokenResult GenerateEncodedTokenAsync(string sub, LoginUserModel customClaims)
{
//创建用户身份标识,可按需要添加更多信息
var claims = new List<Claim>
{
new Claim("userid", customClaims.userid),
new Claim("username", customClaims.username),
new Claim("realname",customClaims.realname),
new Claim("roles", string.Join(";",customClaims.roles)),
new Claim("permissions", string.Join(";",customClaims.permissions)),
new Claim("normalPermissions", string.Join(";",customClaims.normalPermissions)),
new Claim(JwtRegisteredClaimNames.Sub, sub),
};
//创建令牌
var jwt = new JwtSecurityToken(
issuer: _jwtConfig.Issuer,
audience: _jwtConfig.Audience,
claims: claims,
notBefore: _jwtConfig.NotBefore,
expires: _jwtConfig.Expiration,
signingCredentials: _jwtConfig.SigningCredentials);
string access_token = new JwtSecurityTokenHandler().WriteToken(jwt);
return new JwtTokenResult()
{
access_token = access_token,
expires_in = _jwtConfig.Expired * 60,
token_type = JwtBearerDefaults.AuthenticationScheme,
user = customClaims
};
}
}
四.JwtTokenResult
登录成功生成jwt返回给前端的model,LoginUserModel是用户信息model,我这里携带是为了避免前端解析token。
/// <summary>
/// 登录成功返回model
/// </summary>
public class JwtTokenResult
{
public string access_token { get; set; }
public string refresh_token { get; set; }
/// <summary>
/// 过期时间(单位秒)
/// </summary>
public int expires_in { get; set; }
public string token_type { get; set; }
public LoginUserModel user { get; set; }
}
public class LoginUserModel
{
public string userid { get; set; }
public string username { get; set; }
public string realname { get; set; }
public string roles { get; set; }
public string permissions { get; set; }
public string normalPermissions { get; set; }
}
五.JwtUser,用户解析jwt 获取当前用户信息
public static class JwtUser
{
/// <summary>
/// 解析jwt 获取当前用户信息
/// </summary>
/// <param name="request"></param>
/// <returns>用户信息</returns>
public static LoginUserModel GetRequestUser(this HttpRequest request)
{
var authorization = request.Headers["Authorization"].ToString();
var auth = authorization.Split(" ")[1];
var jwtArr = auth.Split('.');
var dic = JsonConvert.DeserializeObject<Dictionary<string, string>>(Base64UrlEncoder.Decode(jwtArr[1]));
//解析Claims
var reqUser = new LoginUserModel
{
userid = dic["userid"],
username = dic["username"],
realname = dic["realname"],
roles = dic["roles"],
permissions = dic["permissions"],
normalPermissions = dic["normalPermissions"],
};
return reqUser;
}
六.Startup配置
(1) .ConfigureServices注入jwt
//注入jwt
services.AddScoped<GenerateJwt>();
services.Configure<JwtConfig>(Configuration.GetSection("JwtConfig"));
#region jwt验证
var jwtConfig = new JwtConfig();
Configuration.Bind("JwtConfig", jwtConfig);
services
.AddAuthentication(option =>
{
//认证middleware配置
option.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
option.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
//Token颁发机构
ValidIssuer = jwtConfig.Issuer,
//颁发给谁
ValidAudience = jwtConfig.Audience,
//这里的key要进行加密
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtConfig.SecretKey)),
//是否验证Token有效期,使用当前时间与Token的Claims中的NotBefore和Expires对比
ValidateLifetime = true,
};
});
#endregion
(2).Configure
app.UseAuthentication();//jwt
七.登录接口生成jwt(部分代码)
这里需要注意,将refreshToken存入redis 设置15天过期
var claims = new LoginUserModel()
{
userid = user.Id.ToString(),
username = user.UserName,
realname = user.RealName,
roles = string.Join(";", rolesReturn),
permissions = string.Join(";", permissionReturn),
normalPermissions = string.Join(";", PermissionHelper.GetPermission(2)),
};
var refreshToken = Guid.NewGuid().ToString();
//refreshToken设置15天过期
await RedisHelper.SetAsync(refreshToken, claims, 60 * 60 * 24 * 15);
var jwtTokenResult = _generateJwt.GenerateEncodedTokenAsync(user.Id.ToString(), claims);
jwtTokenResult.refresh_token = refreshToken;
八.刷新token
设置的token有效期为30min,前端拿到token后需要定时刷新token(在token失效临界点刷新)。
根据refreshToken获取新的token,首先验证在redis中能否找到refreshToken,找不到则证明登录已过期,找到后则生成新的token。
/// <summary>
/// 刷新token
/// </summary>
/// <param name="refreshToken"></param>
/// <returns></returns>
[HttpGet]
[Route("refreshToken")]
public async Task<JsonResult> RefreshToken(string refreshToken)
{
if (string.IsNullOrEmpty(refreshToken))
return AjaxHelper.JsonResult(HttpStatusCode.BadRequest, "refreshToken不能为空");
var claims = await RedisHelper.GetAsync<LoginUserModel>(refreshToken);
if (claims == null)
return AjaxHelper.JsonResult(HttpStatusCode.Unauthorized, "登录已过期");
var jwtTokenResult = _generateJwt.GenerateEncodedTokenAsync(claims.userid, claims);
jwtTokenResult.refresh_token = refreshToken;
return AjaxHelper.JsonResult(HttpStatusCode.OK, "成功", jwtTokenResult);//200
}
九.使用
在接口上标记身份验证
上一篇: Go语言中切片slice的声明与使用
下一篇: 怎么保护网站不被盗用
推荐阅读