.net core 使用 System.IdentityModel.Tokens.Jwt 来实现JWT 鉴权。
可自定义鉴权失败的出参。支持Swagger验证
话不多说,直接上码
第一步: JWT实现servic:DiJWTServiceCollectionExtensions
/// <summary>
/// jwt 用户权限
/// </summary>
public static class JwtRole
{
/// <summary>
/// 用户登录的
/// </summary>
public const string UserInfo = "UserInfo";
/// <summary>
/// 设备登录的
/// </summary>
public const string DeviceInfo = "DeviceInfo";
}
/// <summary>
/// 微软 System.IdentityModel.Tokens.Jwt
/// </summary>
public class JwtService
{
private TokenValidationParameters _tokenValidationParameters { get; set; }
/// <summary>
///
/// </summary>
/// <param name="tokenValidationParameters"></param>
public JwtService(TokenValidationParameters tokenValidationParameters)
{
_tokenValidationParameters = tokenValidationParameters;
}
/// <summary>
/// 生产JWTToken
/// </summary>
/// <param name="claim"></param>
/// <param name="role"></param>
/// <param name="expires">过期时间</param>
/// <returns></returns>
public string GenerateJwtToken(Claim claim, string role, DateTime expires)
{
var tokenHandler = new JwtSecurityTokenHandler();
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
claim,
new Claim(ClaimTypes.Role,role)
}),
Expires = expires,
SigningCredentials = new SigningCredentials(_tokenValidationParameters.IssuerSigningKey, SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
/// <summary>
/// 验证token是否合理,返回对应的值
/// </summary>
/// <param name="authenticationToken"></param>
/// <param name="user"></param>
/// <returns></returns>
public bool ValidatedJwtToken(string authenticationToken, out ClaimsPrincipal user)
{
bool result = false;
user = null;
try
{
if (string.IsNullOrEmpty(authenticationToken))
{
return false;
}
//校验并解析token
var handler = new JwtSecurityTokenHandler();
var claimsPrincipal = handler.ValidateToken(authenticationToken, _tokenValidationParameters,
out SecurityToken validatedToken);//validatedToken:解密后的对象
//user = ((JwtSecurityToken)validatedToken).Payload.SerializeToJson(); //获取payload中的数据
result = claimsPrincipal.Identity.IsAuthenticated;
user = claimsPrincipal;
}
catch (SecurityTokenExpiredException stee)
{
//表示过期
}
catch (SecurityTokenException ste)
{
//表示token错误
}
catch (Exception e)
{
}
return result;
}
/// <summary>
///
/// </summary>
/// <param name="authenticationToken"></param>
/// <param name="payload"></param>
/// <returns></returns>
public bool ValidatedJwtToken(string authenticationToken, out object payload)
{
bool result = false;
payload = null;
try
{
if (string.IsNullOrEmpty(authenticationToken))
{
return false;
}
//校验并解析token
var handler = new JwtSecurityTokenHandler();
var claimsPrincipal = handler.ValidateToken(authenticationToken, _tokenValidationParameters,
out SecurityToken validatedToken);//validatedToken:解密后的对象
//user = ((JwtSecurityToken)validatedToken).Payload.SerializeToJson(); //获取payload中的数据
result = claimsPrincipal.Identity.IsAuthenticated;
var jwtVT = ((JwtSecurityToken)validatedToken);
payload = jwtVT.Payload.GetValueOrDefault(ClaimTypes.Authentication) ?? jwtVT.Payload.GetValueOrDefault(ClaimTypes.UserData);
}
catch (SecurityTokenExpiredException stee)
{
//表示过期
Console.WriteLine("Token过期");
}
catch (SecurityTokenException ste)
{
//表示token错误
Console.WriteLine("Token错误");
}
catch (Exception e)
{
}
return result;
}
}
public static class DiJWTServiceCollectionExtensions
{
/// <summary>
///
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
public static IServiceCollection AddJwtService(this IServiceCollection services,string serkey)
{
var tokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = false,//是否验证Issuer
ValidIssuer = "ApiServer",
ValidateAudience = false,//是否验证Audience
ValidateLifetime = true,//是否验证失效时间
ClockSkew = TimeSpan.FromSeconds(60),
ValidateIssuerSigningKey = true,//是否验证SecurityKey
//ValidAudience = Const.Domain,//Audience
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(serkey))//拿到SecurityKey
};
//将此配置 注入服务,因为还有其他地方需要使用
services.AddSingleton(tokenValidationParameters);
//注册 自定义服务
services.AddSingleton<JwtService>();
return services;
}
}
第二步:自定义过滤器 MyAuthorizeFilter
可自定义Headers,Query,cookies,token的入参名称:如下代码
var token = context.HttpContext.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last()
?? context.HttpContext.Request.Headers["X-Token"].FirstOrDefault()
?? context.HttpContext.Request.Query["Token"].FirstOrDefault()
?? context.HttpContext.Request.Cookies["Token"];
当然大家可随意设置,只需要和前端约定好即可。
/// <summary>
/// 只为重写 OnAuthorizationAsync 自定义返回数据消息
/// </summary>
public class MyAuthorizeFilter : IAsyncAuthorizationFilter
{
private JwtService _jwtService { get; set; }
/// <summary>
///
/// </summary>
/// <param name="jwtService"></param>
public MyAuthorizeFilter(JwtService jwtService)
{
_jwtService = jwtService;
}
private static bool HasAllowAnonymous(AuthorizationFilterContext context)
{
var filters = context.Filters;
for (var i = 0; i < filters.Count; i++)
{
if (filters[i] is IAllowAnonymousFilter)
{
return true;
}
}
// When doing endpoint routing, MVC does not add AllowAnonymousFilters for AllowAnonymousAttributes that
// were discovered on controllers and actions. To maintain compat with 2.x,
// we'll check for the presence of IAllowAnonymous in endpoint metadata.
var endpoint = context.HttpContext.GetEndpoint();
if (endpoint?.Metadata?.GetMetadata<IAllowAnonymous>() != null)
{
return true;
}
return false;
}
/// <summary>
///
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public virtual async Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (!context.IsEffectivePolicy(this))
{
return;
}
// Allow Anonymous skips all authorization
if (HasAllowAnonymous(context))
{
return;
}
else
{
var token = context.HttpContext.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last()
?? context.HttpContext.Request.Headers["X-Token"].FirstOrDefault()
?? context.HttpContext.Request.Query["Token"].FirstOrDefault()
?? context.HttpContext.Request.Cookies["Token"];
ClaimsPrincipal user = context.HttpContext.User;
if (_jwtService.ValidatedJwtToken(token, out user))
{
//验证通过,验证成功 赋值给User
context.HttpContext.User = user;
}
else
{
context.Result = new JsonResult(new
{
Code = 401,
Message = "没有权限"
});
}
}
//Console.WriteLine(JsonHelper.SerializeObject(context.HttpContext.Items));
}
}
第三步:Startup 使用
第四步:添加OAuthController.cs 获取token 方法,以及测试方法
展示效果:
没有token 之前,调用Get接口 提示无权限。
为自定义出参哦,可以和自己项目整体出参自定义一致。
{
"code": 401,
"message": "没有权限"
}
获取token
点击下图所示第一步:将上图token填入文本框中。点击绿色按钮,最后点击close按钮。
重新执行get方法,就得到我们想要的了。
源码地址:
https://github.com/BigMaJx/OOPDemo/blob/master/Api/Controllers/OAuthController.cs
https://github.com/BigMaJx/OOPDemo/blob/master/Api/SecuritysDI/