• HTTP Bearer认证及JWT的使用


     

     

     

     

     

     

     

     

     

     

     

     

     

     

    一、自定义CheckJWTAttribute特性方式

    之前使用的是这种方式,根据jwt原理自定义生成JWT、验证jwt,感觉挺好。原理就是自定义一个拦截器(特性),拦截器对每个请求都优先进行处理,认证成功的进行下一步操作。

    1、定义JWTPayload类

    using System;
    namespace HyDataMiddleground.Util
    {
        public class JWTPayload
        {
            public string UserName { get; set; }
            public string Email { get; set; }
            public string UserId { get; set; }
            public DateTime Expire { get; set; }
        }
    }

     

    2、定义CheckJWTAttribute特性

    用于验证jwt:

    using Microsoft.AspNetCore.Mvc.Filters;
    using System;
    using System.Threading.Tasks;
    
    namespace HyDataMiddleground.Util
    {
        /// <summary>
        /// JWT校检
        /// </summary>
        public class CheckJWTAttribute : BaseActionFilterAsync
        {
            private static readonly int _errorCode = 401;
            public override async Task OnActionExecuting(ActionExecutingContext context)
            {
                if (context.ContainsFilter<NoCheckJWTAttribute>()) return;
                try
                {
                    var req = context.HttpContext.Request;
                    string token = req.GetToken();
                    if (token.IsNullOrEmpty())
                    {
                        context.Result = Error("缺少token", _errorCode);
                        return;
                    }
                    if (!JWTHelper.CheckToken(token, JWTHelper.JWTSecret))
                    {
                        context.Result = Error("token校检失败!", _errorCode);
                        return;
                    }
    
                    var payload = JWTHelper.GetPayload<JWTPayload>(token);
                    if (payload.Expire < DateTime.Now)
                    {
                        context.Result = Error("token过期!", _errorCode);
                        return;
                    }
                }
                catch (Exception ex)
                {
                    context.Result = Error(ex.Message, _errorCode);
                }
                await Task.CompletedTask;
            }
        }
    }

    3、定义其他帮助类

    (1)BaseActionFilterAsync类

    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Mvc.Filters;
    using System;
    using System.Threading.Tasks;
    
    namespace HyDataMiddleground.Util
    {
        public class BaseActionFilterAsync : Attribute, IAsyncActionFilter
        {
            /// <summary>
            /// action执行之前执行
            /// </summary>
            /// <param name="context"></param>
            /// <returns></returns>
            public async virtual Task OnActionExecuting(ActionExecutingContext context)
            {
                await Task.CompletedTask;
            }
            /// <summary>
            /// action执行之后执行
            /// </summary>
            /// <param name="context"></param>
            /// <returns></returns>
            public async virtual Task OnActionExecuted(ActionExecutedContext context)
            {
                await Task.CompletedTask;
            }
            /// <summary>
            /// 在模型绑定完成后,在操作之前异步调用。
            /// </summary>
            /// <param name="context"></param>
            /// <param name="next"></param>
            /// <returns></returns>
            public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
            {
                await OnActionExecuting(context);
                if (context.Result == null)
                {
                    var nextContext = await next();
                    await OnActionExecuted(nextContext);
                }
            }
    
            /// <summary>
            /// 返回JSON
            /// </summary>
            /// <param name="json">json字符串</param>
            /// <returns></returns>
            public ContentResult JsonContent(string json)
            {
                return new ContentResult { Content = json, StatusCode = 200, ContentType = "application/json; charset=utf-8" };
            }
    
            /// <summary>
            /// 返回成功
            /// </summary>
            /// <returns></returns>
            public ContentResult Success()
            {
                AjaxResult res = new AjaxResult
                {
                    Success = true,
                    Msg = "请求成功!"
                };
    
                return JsonContent(res.ToJson());
            }
    
            /// <summary>
            /// 返回成功
            /// </summary>
            /// <param name="msg">消息</param>
            /// <returns></returns>
            public ContentResult Success(string msg)
            {
                AjaxResult res = new AjaxResult
                {
                    Success = true,
                    Msg = msg
                };
    
                return JsonContent(res.ToJson());
            }
    
            /// <summary>
            /// 返回成功
            /// </summary>
            /// <param name="data">返回的数据</param>
            /// <returns></returns>
            public ContentResult Success<T>(T data)
            {
                AjaxResult<T> res = new AjaxResult<T>
                {
                    Success = true,
                    Msg = "请求成功!",
                    Data = data
                };
    
                return JsonContent(res.ToJson());
            }
    
            /// <summary>
            /// 返回错误
            /// </summary>
            /// <returns></returns>
            public ContentResult Error()
            {
                AjaxResult res = new AjaxResult
                {
                    Success = false,
                    Msg = "请求失败!"
                };
    
                return JsonContent(res.ToJson());
            }
    
            /// <summary>
            /// 返回错误
            /// </summary>
            /// <param name="msg">错误提示</param>
            /// <returns></returns>
            public ContentResult Error(string msg)
            {
                AjaxResult res = new AjaxResult
                {
                    Success = false,
                    Msg = msg,
                };
    
                return JsonContent(res.ToJson());
            }
    
            /// <summary>
            /// 返回错误
            /// </summary>
            /// <param name="msg">错误提示</param>
            /// <param name="errorCode">错误代码</param>
            /// <returns></returns>
            public ContentResult Error(string msg, int errorCode)
            {
                AjaxResult res = new AjaxResult
                {
                    Success = false,
                    Msg = msg,
                    ErrorCode = errorCode
                };
    
                return JsonContent(res.ToJson());
            }
        }
    }

    (2)AjaxResult类

    namespace HyDataMiddleground.Util
    {
        /// <summary>
        /// Ajax请求结果
        /// </summary>
        public class AjaxResult
        {
            /// <summary>
            /// 是否成功
            /// </summary>
            public bool Success { get; set; } = true;
    
            /// <summary>
            /// 错误代码
            /// </summary>
            public int ErrorCode { get; set; }
    
            /// <summary>
            /// 返回消息
            /// </summary>
            public string Msg { get; set; }
        }
    }

    (3)上述代码中使用到的扩展类Extention

    using Newtonsoft.Json;
    using System;
    using System.ComponentModel;
    using System.IO;
    using System.Reflection;
    using System.Runtime.Serialization;
    using System.Runtime.Serialization.Formatters.Binary;
    
    namespace HyDataMiddleground.Util
    {
        public static partial class Extention
        {
            /// <summary>
            /// 构造函数
            /// </summary>
            static Extention()
            {
                JsonSerializerSettings setting = new JsonSerializerSettings();
                JsonConvert.DefaultSettings = new Func<JsonSerializerSettings>(() =>
                {
                    //日期类型默认格式化处理
                    setting.DateFormatHandling = DateFormatHandling.MicrosoftDateFormat;
                    setting.DateFormatString = "yyyy-MM-dd HH:mm:ss";
                    return setting;
                });
            }
    /// <summary> /// 将对象序列化成Json字符串 /// </summary> /// <param name="obj">需要序列化的对象</param> /// <returns></returns> public static string ToJson(this object obj) { return JsonConvert.SerializeObject(obj); } } }

    (4)JWTHelper类

    using Newtonsoft.Json.Linq;
    
    namespace HyDataMiddleground.Util
    {
        /// <summary>
        /// JWT帮助类
        /// </summary>
        public class JWTHelper
        {
            private static readonly string _headerBase64Url = "{"alg":"HS256","typ":"JWT"}".Base64UrlEncode();
            public static readonly string JWTSecret = ConfigHelper.GetValue("JWTSecret");
    
            /// <summary>
            /// 生成Token
            /// </summary>
            /// <param name="payloadJsonStr">载荷,数据JSON字符串</param>
            /// <param name="secret">秘钥</param>
            /// <returns></returns>
            public static string GetToken(string payloadJsonStr, string secret)
            {
                string payloadBase64Url = payloadJsonStr.Base64UrlEncode();
                string sign = $"{_headerBase64Url}.{payloadBase64Url}".ToHMACSHA256String(secret);
                return $"{_headerBase64Url}.{payloadBase64Url}.{sign}";
            }
    
            /// <summary>
            /// 获取Token中的数据
            /// </summary>
            /// <param name="token"></param>
            /// <returns></returns>
            public static JObject GetPayload(string token)
            {
                return token.Split('.')[1].Base64UrlDecode().ToJObject();
            }
    
            /// <summary>
            /// 获取Token中的数据
            /// </summary>
            /// <typeparam name="T">泛型</typeparam>
            /// <param name="token">token</param>
            /// <returns></returns>
            public static T GetPayload<T>(string token)
            {
                if (token.IsNullOrEmpty())
                    return default;
    
                return token.Split('.')[1].Base64UrlDecode().ToObject<T>();
            }
    
            /// <summary>
            /// 校验Token
            /// </summary>
            /// <param name="token">token</param>
            /// <param name="secret">密钥</param>
            /// <returns></returns>
            public static bool CheckToken(string token, string secret)
            {
                var items = token.Split('.');
                var oldSign = items[2];
                string newSign = $"{items[0]}.{items[1]}".ToHMACSHA256String(secret);
                return oldSign == newSign;
            }
        }
    }

    3、定义NoCheckJWTAttribute类

    namespace HyDataMiddleground.Util
    {
        /// <summary>
        /// 忽略JWT校验
        /// </summary>
        public class NoCheckJWTAttribute : BaseActionFilterAsync
        {
        }
    }

    4、使用CheckJWTAttribute

    (1)定义一个BaseApiController,所有的controller都继承该类,BaseApiController类使用CheckJWTAttribute特性

    using HyDataMiddleground.Util;
    using Microsoft.AspNetCore.Mvc;
    
    namespace HyDataMiddleground.Admin
    {
        /// <summary>
        /// Mvc对外接口基控制器
        /// </summary>
        [CheckJWT]
        public class BaseApiController : BaseController
        {
        }
    }

    (2)不需要认证的,使用NoCheckJWT限制,如下:

         /// <summary>
            /// 用户登录
            /// </summary>
            /// <param name="input">LoginInputDTO实体参数</param>
            /// <returns></returns>
            [Produces("application/json")]
            [HttpPost]
            [NoCheckJWT]
            public async Task<string> SubmitLogin(LoginInputDTO input)
            {
                return await _userBus.SubmitLoginAsync(input);
            }

    二、使用aspnetcore提供的组件

    1、安装组件

    通过nugut搜索安装Microsoft.AspNetCore.Authentication.JwtBearer 

    2、jwtconfig配置

    {  
      "Jwt": {
        "Issuer": "Issuer",
        "Audience": "Audience",
        "SigningKey": "EF1DA5B4-C7FA-4240-B997-7D1701BF9BE2"
      }  
    }

    3、定义jwtconfig对应的实体

    public class JwtConfig
    {
        public string Issuer{get;set;}  
        public string Audience{get;set;}
        public string SigningKey{get;set;}  
    }

    4、Startup.cs 配置

    (1)ConfigureServices 中需要进行添加的信息

    #region   Token验证信息   JWT
    //读取JWT的配置信息
    var jwtconfig = Configuration.GetSection("Jwt").Get<JwtConfig>();
    //JWT身份认证
    services.AddAuthentication(option =>
    {
        option.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        option.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    })
    .AddJwtBearer(option =>
    {
        option.TokenValidationParameters = new TokenValidationParameters
        {
            ValidIssuer = jwtconfig.Issuer,
            ValidAudience = jwtconfig.Audience,
            ValidateIssuer = true,
            ValidateLifetime = true,
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtconfig.SigningKey)),
            // 缓冲过期时间,总的有效时间等于这个时间加上jwt的过期时间,如果不配置,默认是5分钟
            ClockSkew = TimeSpan.FromSeconds(5)
        };
    
        option.Events = new JwtBearerEvents
        {
            //此处为权限验证失败后触发的事件
            OnChallenge = context =>
            {
                //此处代码为终止.Net Core默认的返回类型和数据结果,这个很重要哦,必须
                context.HandleResponse();
                //自定义自己想要返回的数据结果,我这里要返回的是Json对象,通过引用Newtonsoft.Json库进行转换
                var payload = JsonConvert.SerializeObject(new { message = "授权未通过,Token无效", status = false, code = 401 });
                //自定义返回的数据类型
                context.Response.ContentType = "application/json";
                //自定义返回状态码,默认为401 我这里改成 200
                context.Response.StatusCode = StatusCodes.Status200OK;
                //输出Json数据结果
                context.Response.WriteAsync(payload);
                return Task.FromResult(0);
             }
        };
    });
    
    services.AddOptions().Configure<JwtConfig>(Configuration.GetSection("Jwt"));
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);                            
    #endregion

    (2)Configure需要添加的信息

    // JWT身份认证
    app.UseAuthentication();
    app.UseAuthorization();

    以上配置好了 就基本上可以使用JWT验证了,下面介绍如何生成jwt token

    5、封装了一个帮助类

        /// <summary>
        /// JWT帮助类信息
        /// </summary>
        public class JwtHelper
        {
            /// <summary>
            /// 颁发JWT字符串
            /// </summary>
            /// <param name="tokenModel"></param>
            /// <returns></returns>
            public static string IssueJwt(Claim[] claim)
            {
                // 读取对应的配置信息
                string iss = ConfigHelper.GetSectionValue("Jwt:Issuer");
                string aud = ConfigHelper.GetSectionValue("Jwt:Audience");
                string secret = ConfigHelper.GetSectionValue("Jwt:SigningKey");
                //加密关键字
                var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret));
                //编码格式
                var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
                //token存储相关信息
                var token = new JwtSecurityToken(
                    issuer: iss,
                    audience: aud,
                    claims: claim,
                    notBefore: DateTime.Now,
                    expires: DateTime.Now.AddSeconds(300),
                    signingCredentials: creds);
                var jwtHandler = new JwtSecurityTokenHandler();
                //生成对应的编码信息
                var encodedJwt = jwtHandler.WriteToken(token);
                return encodedJwt;
            }
            /// <summary>
            /// 解析
            /// </summary>
            /// <param name="jwtStr"></param>
            /// <returns></returns>
            public static List<Claim> SerializeJwt(string jwtStr)
            {
                var jwtHandler = new JwtSecurityTokenHandler();
                JwtSecurityToken jwtToken = jwtHandler.ReadJwtToken(jwtStr);
                object role;
                try
                {
                    jwtToken.Payload.TryGetValue(ClaimTypes.Role, out role);
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                    throw;
                }
                var list = jwtToken.Claims.ToList();
                return list;
            }
        }

    6、控制器层使用示例

            /// <summary>
            /// 测试获取token信息
            /// </summary>
            /// <returns></returns>
            [HttpGet]
            public ActionResult<string> Get()
            {           
                //参数存储的信息
                var claim = new Claim[]{
                new Claim("UserName", "测试"),
                new Claim("UserId", "10086")
            };
                //生成证书
                var token = JwtHelper.IssueJwt(claim);
                //解析证书
                var data=  JwtHelper.SerializeJwt(token);
                return Ok(new { token = token, data= data });
            }
            /// <summary>
            /// 在需要身份认证的方法添加[Authorize]
            /// </summary>
            [Authorize]
            [HttpGet("{id}")]
            public ActionResult<string> Get(int id)
            {
                return "value";
            }

    然后 整体就基本上结束了。虽然说这种方式没有自定义的方式代码容易读,但是也方便了好多。

  • 相关阅读:
    touch创建文件
    excel如何冻结首行或首列及首行首列同时冻结
    cd mkdir mv cp rm 命令目录相关操作
    months_between()用法
    sysdate()简单用法
    round()和trunc()用法
    length() 用法
    replace 用法
    orcl 中upper()和lower()和initcap()的用法
    orcl 复杂查询
  • 原文地址:https://www.cnblogs.com/qtiger/p/14657461.html
Copyright © 2020-2023  润新知