asp.net core 3.1 自定义中间件实现jwt token认证

栏目: IT技术 · 发布时间: 4年前

内容简介:暂时是使用微软提供类库生成,如果有想法可以自己生成这里借鉴国外大牛的代码,主要就是验证jwt并且存把解析出来的数据存放到当前上下文这个根据刚才中间件的存放的信息判断是否授权成功,支持匿名特性
/// <summary>
/// 认证用户信息
/// </summary>
public class DyUser
{
    /// <summary>
    /// 用户ID
    /// </summary>
    public int UserId { get; set; }
    /// <summary>
    /// 所属商户ID
    /// </summary>
    public int? TenantId { get; set; }
}

Jwt配置对象

public class AuthOptions
{
    /// <summary>
    /// Jwt认证Key
    /// </summary>
    public string Security { get; set; }
    /// <summary>
    /// 过期时间【天】
    /// </summary>
    public int Expiration { get; set; }
}

JWT管理接口

public interface IAuthManage
{
    /// <summary>
    /// 生成JwtToken
    /// </summary>
    /// <param name="user">用户信息</param>
    /// <returns></returns>
    string GenerateJwtToken(DyUser user);
}

JWT管理接口实现

暂时是使用微软提供类库生成,如果有想法可以自己生成

public class MicrosoftJwtAuthManage : IAuthManage
{
    private readonly AuthOptions _authOptions;
    public MicrosoftJwtAuth(AuthOptions authOptions)
    {
        _authOptions = authOptions;
    }

    public string GenerateJwtToken(DyUser user)
    {
        var tokenHandler = new JwtSecurityTokenHandler();
        var key = Encoding.ASCII.GetBytes(_authOptions.Security);
        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(new Claim[]
            {
                new Claim("user",user.ToJson())
            }),
            Expires = DateTime.UtcNow.AddDays(_authOptions.Expiration),//一周过期
            SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
        };
        var token = tokenHandler.CreateToken(tokenDescriptor);
        return tokenHandler.WriteToken(token);
    }
}

处理JWT中间件

这里借鉴国外大牛的代码,主要就是验证jwt并且存把解析出来的数据存放到当前上下文

public class JwtMiddleware
{
    private readonly RequestDelegate _next;
    private readonly AuthOptions _authOptions;

    public JwtMiddleware(RequestDelegate next, AuthOptions authOptions)
    {
        _next = next;
        _authOptions = authOptions;
    }

    public async Task Invoke(HttpContext context)
    {
        //获取上传token,可自定义扩展
        var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last()
                    ?? context.Request.Headers["X-Token"].FirstOrDefault()
                    ?? context.Request.Query["Token"].FirstOrDefault()
                    ?? context.Request.Cookies["Token"];

        if (token != null)
            AttachUserToContext(context, token);

        await _next(context);
    }

    private void AttachUserToContext(HttpContext context, string token)
    {
        try
        {
            var tokenHandler = new JwtSecurityTokenHandler();
            var key = Encoding.ASCII.GetBytes(_authOptions.Security);
            tokenHandler.ValidateToken(token, new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(key),
                ValidateIssuer = false,
                ValidateAudience = false,
                // set clockskew to zero so tokens expire exactly at token expiration time (instead of 5 minutes later)
                ClockSkew = TimeSpan.Zero
            }, out SecurityToken validatedToken);

            var jwtToken = (JwtSecurityToken)validatedToken;
            var user = jwtToken.Claims.First(x => x.Type == "user").Value.ToJsonEntity<DyUser>();

            //写入认证信息,方便业务类使用
            var claimsIdentity = new ClaimsIdentity(new Claim[] { new Claim("user", jwtToken.Claims.First(x => x.Type == "user").Value) });
            Thread.CurrentPrincipal = new ClaimsPrincipal(claimsIdentity);

            // attach user to context on successful jwt validation
            context.Items["User"] = user;
        }
        catch
        {
            // do nothing if jwt validation fails
            // user is not attached to context so request won't have access to secure routes
            throw;
        }
    }
}

权限过滤器

这个根据刚才中间件的存放的信息判断是否授权成功,支持匿名特性

public class ApiAuthorizeAttribute : Attribute, IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationFilterContext context)
    {
        var user = context.HttpContext.Items["User"];
        //验证是否需要授权和授权信息
        if (HasAllowAnonymous(context) == false && user == null)
        {
            // not logged in
            context.Result = new JsonResult(new {message = "Unauthorized"})
                {StatusCode = StatusCodes.Status401Unauthorized};
        }
    }

    private static bool HasAllowAnonymous(AuthorizationFilterContext context)
    {
        var filters = context.Filters;
        if (filters.OfType<IAllowAnonymousFilter>().Any())
        {
            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();
        return endpoint?.Metadata?.GetMetadata<IAllowAnonymous>() != null;
    }
}

扩展IServiceCollection

方便以后管理和维护,主要就是把需要的对象注入到IOC容器里面

public static class AuthServiceExtensions
{
    public static void AddAuth(this IServiceCollection services, Action<AuthOptions> configAction)
    {
        var options = new AuthOptions();
        configAction(options);
        services.AddSingleton(options);
        services.AddSingleton<IAuthManage>(new MicrosoftJwtAuthManage(options));
    }
}

NullDySession

这里是为了在非控制器类获取用户信息用

/// <summary>
/// 当前会话对象
/// </summary>
public class NullDySession
{
    /// <summary>
    /// 获取DySession实例
    /// </summary>
    public static NullDySession Instance { get; } = new NullDySession();
    /// <summary>
    /// 获取当前用户信息
    /// </summary>
    public DyUser DyUser
    {
        get
        {
            var claimsPrincipal = Thread.CurrentPrincipal as ClaimsPrincipal;

            var claimsIdentity = claimsPrincipal?.Identity as ClaimsIdentity;

            var userClaim = claimsIdentity?.Claims.FirstOrDefault(c => c.Type == "user");
            if (userClaim == null || string.IsNullOrEmpty(userClaim.Value))
            {
                return null;
            }

            return userClaim.Value.ToJsonEntity<DyUser>();
        }
    }

    private NullDySession()
    {
    }
}

到这为止准备工作完成,开始用起来吧~

修改【Startup.cs->ConfigureServices】

//添加全局权限认证过滤器
services.AddControllersWithViews(options =>
{
	options.Filters.Add<ApiAuthorizeAttribute>();
})
//添加认证配置信息
services.AddAuth(options =>
{
	options.Expiration = 7;//天为单位
	options.Security = apolloConfig.Get("JwtSecret");
});

添加中间件【Startup.cs->Configure(IApplicationBuilder app, IWebHostEnvironment env)方法中】

注意中间件的位置

//启用jwt认证中间件
app.UseMiddleware<JwtMiddleware>();

api使用案例【使用构造注入IAuthManage】

//生成了JwtToken
var newToken = _authManage.CreateJwtToken(para.Sn);

//Controller里面获取用户信息
public DyUser DyUser => (DyUser)this.HttpContext.Items["User"];

//普通class类获取用户信息【如果不是Web应用,需要独立引用Dymg.Core】
NullDySession.Instance.DyUser.UserId;

//如果个别不接口不需要认证,可以使用AllowAnonymous特性
[HttpPost, AllowAnonymous]
public string Noauth()
{
	return "这个不需要授权";
}

前端调用案例

//token放在请求头里面
Authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoie1wiVXNlcklkXCI6MTIzNDU2ODcsXCJUZW5hbnRJZFwiOjY1NDMyMSxcIlN0YXRpb25JZFwiOm51bGwsXCJTbWFydEJveFNuXCI6bnVsbH0iLCJuYmYiOjE1OTU5MDAxMzYsImV4cCI6MTU5NjUwNDkzNiwiaWF0IjoxNTk1OTAwMTM2fQ.lkEunspinGeQK9sFoQs2WLpNticqOR4xv_18CQdOE_Y
//自定义key
x-token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoie1wiVXNlcklkXCI6MTIzNDU2ODcsXCJUZW5hbnRJZFwiOjY1NDMyMSxcIlN0YXRpb25JZFwiOm51bGwsXCJTbWFydEJveFNuXCI6bnVsbH0iLCJuYmYiOjE1OTU5MDAxMzYsImV4cCI6MTU5NjUwNDkzNiwiaWF0IjoxNTk1OTAwMTM2fQ.lkEunspinGeQK9sFoQs2WLpNticqOR4xv_18CQdOE_Y

//使用连接字符串方式
https://xxxxx/user/getUser?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoie1wiVXNlcklkXCI6MTIzNDU2ODcsXCJUZW5hbnRJZFwiOjY1NDMyMSxcIlN0YXRpb25JZFwiOm51bGwsXCJTbWFydEJveFNuXCI6bnVsbH0iLCJuYmYiOjE1OTU5MDAxMzYsImV4cCI6MTU5NjUwNDkzNiwiaWF0IjoxNTk1OTAwMTM2fQ.lkEunspinGeQK9sFoQs2WLpNticqOR4xv_18CQdOE_Y

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

宇宙涟漪中的孩子

宇宙涟漪中的孩子

谢云宁 / 四川科学技术出版社 / 2017-11 / 28.00元

近未来。日冕科技公司通过建造围绕太阳的光幕搜集了近乎无穷的能源,这些能源主要用于地球上的网络空间建设。随着全球网络时间频率的不断提升,越来越多的人选择接驳进虚拟空间,体验现实中难以经历的丰富人生。 网络互动小说作者宁天穹一直自认为是这些人中普通的一员,有一天却被一名读者带进反抗组织,了解到日冕公司的各种秘密,并被告知自己的小说将在抵抗运动中起到重要作用。 起初他拒绝参与,但看到地球被笼......一起来看看 《宇宙涟漪中的孩子》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

URL 编码/解码
URL 编码/解码

URL 编码/解码

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具