HTTP Bearer认证及JWT的使用

摘要:
其原理是定制拦截器(特性)。拦截器将优先处理每个请求。如果身份验证成功,将采取下一步。“,_errorCode);return;}}catch{context.Result=Error;}awaitTask。完成任务;}}3.使用Microsoft.AspNetCore定义其他帮助类BaseActionFilterAsync类。Mvc;使用Microsoft.AspNetCore.Mvc。过滤器;使用系统;使用System.Threading。任务;namespaceHyDataMiddleground。Util{publicclassBaseActionFilterAsync:Attribute,IAsyncActionFilter{///<summary>///action执行前执行///</summary>//<paramname=“context”>˂/param>//˂return>˂/return>publicsyncvirtualTaskOnActionExecuting{awaitTask.CompletedTask;}/////action执行后////////////publicsyncvirtualTaskOnActionExecuted{awaitTask.CompletedTask;}/////模型绑定完成后,在操作之前异步调用它。
                            一、自定义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";
        }

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

免责声明:文章转载自《HTTP Bearer认证及JWT的使用》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇GitHub建立个人Maven仓库Android图形系统之Surface、SurfaceView、SurfaceHolder及SurfaceHolder.Callback开发实例下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

python基础练习题(题目 递归输出)

day19 --------------------------------------------------------------- 实例027:递归输出 题目 利用递归函数调用方式,将所输入的5个字符,以相反顺序打印出来。 分析:相反顺序可以用列表来,直接pop方法。 1 def reverseprint(a): 2 lit = list(...

jni中关于dll的装载问题[转]

   通常我们在写大型项目时,也就是集成的项目时,单独用JAVA语言去完成所有的事情往往效果不佳,也不能很好的完成,例如:我们要去调硬件,获取电脑的运行状况等等(如果用JAVA语言实现时,往往耗时),基于此,我们就要寻求一种很好的解决方案,那就是利用别的语言的长处了,如:C++(它在对底层的调用和硬件方面确实够强悍)。如果我们能用C++实现对硬件的所有操作...

Recordset属性与方法

Recordset属性与方法 关于分页:   首先,我们可以为 PageSize 属性设置一个值,从而指定从记录组中取出的构成一个页的行数;然后通过RecordCount 属性来确定记录的总数;再用记录总数除以 PageSize 就可得到所显示的页面总数;最后通过 AbsolutePage 属性就能完成对指定页的访问    ----------------...

JSON数据的处理中的特殊字符

     JSON如今是非经常见的处理数据的方式了。但因为自己使用的是反射获取数据,必须自己处理特殊字符,但总是发现有一些看不见的字符在前台 var obj = jQuery.parseJSON(msg);会转换失败。     比如例如以下在Vs中能够看到仅仅有两个字符    可实际上却有三个字符,使用notepad++打开 一直不明确这些字符是怎样进...

转载 Android之网络与通信

2.三种网络接口简述2.1标准Java接口java.net.*提供与联网有关的类,包括流和数据包套接字、Internet协议、常见HTTP处理。使用java.net.*包连接网络代码:Java代码 收藏代码try{ //定义地址 URL url=newURL("http://www.google.com"); //打开连接 HttpURLConn...

ReactiveX 学习笔记(35)使用 RxDart + RxCommand 进行 GUI 编程

课题 程序界面由3个文本编辑框和1个文本标签组成。 要求文本标签实时显示3个文本编辑框所输入的数字之和。 文本编辑框输入的不是合法数字时,将其值视为0。 3个文本编辑框的初值分别为1,2,3。 创建工程 Flutter 安装完毕之后执行以下命令创建工程 flutter create rx_example 打开 Android Studio,File...