HTTPBearer认证及JWT的使用

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

HTTPBearer认证及JWT的使⽤
⼀、概述
1、理解Http的⽆状态特性
HTTP是⼀个⽆状态的协议,WEB服务器在处理所有传⼊HTTP请求时,根本就不知道某个请求是否是⼀个⽤户的第⼀次请求与后续请求,或者是另⼀个⽤户的请求。

WEB服务器每次在处理请求时,都会按照⽤户所访问的资源所对应的处理代码,从头到尾执⾏⼀遍,然后输出响应内容,WEB服务器根本不会记住已处理了哪些⽤户的请求,因此,我们通常说HTTP协议是⽆状态的。

2、为什么需要认证
虽然HTTP协议与WEB服务器是⽆状态,但我们的业务需求却要求有状态,典型的就是⽤户登录,在这种业务需求中,要求WEB服务器端能区分某个请求是不是⼀个已登录⽤户发起的,或者当前请求是哪个⽤户发出的。

在开发WEB应⽤程序时,我们通常会使⽤Cookie来保存⼀些简单的数据供服务端维持必要的状态。

总的来说,加⼊认证的根本原因就是确保请求的合法性以及资源的安全性,如下图:
⼆、HTTP Bearer认证
http认证根据凭证协议的不同,划分为不同的⽅式。

常⽤的⽅式有:
HTTP基本认证
HTTP摘要认证
HTTP Bearer认证
本篇⽂章介绍HTTP Bearer认证。

1、原理解析
下⾯通过图详细的了解下HTTP Bearer认证过程:
Bearer认证也是http协议中的标准认证⽅式,在Bearer认证中的凭证称为Bearer_token 或者Access_token。

该种⽅式的优点就是灵活⽅便,因为凭证的⽣成和验证完全由开发⼈员设计和实现。

⼀般凭证的设计尽量能保证以下⼏点:
⽤户信息安全性,即保证重要信息不被泄露和恶意破解
避免重放攻击
更⽅便的适应更多的应⽤场景,主要体现在在服务端不需要凭证状态保存,分布式场景中,不需要状态共享
少查库,减少服务端负担。

⽬前最流⾏的token编码协议就是JWT(JSON WEB TOKEN),下⾯通过jwt协议说明。

⼆、JWT协议
1、什么是JWT
Json web token (JWT),是⼀种基于JSON的开放标准()。

2、JWT协议优缺点
优点:
1、可以避免⽤户信息泄露。

2、payload中可以携带⼀些必要的⾮敏感信息,⽐如⽤户名、⽤户邮箱,供前端使⽤
3、服务端不⽤存放jwt的状态信息,减轻服务端压⼒,在分布式场景中更⽅便,不需要状态共享
4、通过时间戳的⽅式避免重放攻击,token的时效性尽量短
缺点:
1、可以使⽤jwt 提供的jti,即jwt的id,来避免重放攻击,但是jti的值需要存储,不管是存储在redis还是mysql,都是要损耗性能的。

3、JWT组成
JWT是由三段信息构成的,将这三段信息⽂本连接起来就构成了Jwt字符串。

第⼀部分我们称它为头部(header),第⼆部分我们称其为载荷(payload),第三部分是签证(signature),下⾯是个JWT实例:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySWQiOiJBZG1pbiIsIkV4cGlyZSI6IjIwMjAtMDctMTEgMTY6NDc6MTYifQ.9ev6IGc1K3xvYaEfmMYeyFz5oHCM57fRGOvSZ-jvArw
(1)Header
在这⾥,我们说明了这是⼀个JWT,并且我们所⽤的签名算法是HS256算法。

对它也要进⾏Base64编码,之后的字符串就成了JWT的Header(头部):
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
(2)Payload
将上⾯的JSON对象进⾏[base64编码]可以得到下⾯的字符串。

这个字符串我们将它称作JWT的Payload(载荷):
eyJpc3MiOiJKb2huIFd1IEpXVCIsImlhdCI6MTQ0MTU5MzUwMiwiZXhwIjoxNDQxNTk0NzIyLCJhdWQiOiJ3d3cuZXhhbXBsZS5jb20iLCJzdWIiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiZnJvbV91c2VyIjoiQiIsInRhcmdldF91c2VyIjo (3)signature
jwt的第三部分是⼀个签证信息,这个签证信息由三部分组成:
header (base64后的)
payload (base64后的)
secret
这个部分需要base64加密后的header和base64加密后的payload使⽤.连接组成的字符串,然后通过header中声明的加密⽅式进⾏secret组合加密,然后就构成了jwt的第三部分。

var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);
var signature = HMACSHA256(encodedString, 'secret'); // TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
将这三部分⽤.连接成⼀个完整的字符串,构成了最终的jwt:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
注意:secret是保存在服务器端的,jwt的签发⽣成也是在服务器端的,secret就是⽤来进⾏jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。

⼀旦客户端得知这个
secret, 那就意味着客户端是可以⾃我签发jwt了。

4、JWT签名的⽬的
最后⼀步签名的过程,实际上是对头部以及载荷内容进⾏签名。

⼀般⽽⾔,加密算法对于不同的输⼊产⽣的输出总是不⼀样的。

所以,如果有⼈对头部以及载荷的内容解码之后进⾏修改,再进⾏编码的
话,那么新的头部和载荷的签名和之前的签名就将是不⼀样的。

⽽且,如果不知道服务器加密的时候⽤的密钥的话,得出来的签名也⼀定会是不⼀样的。

服务器应⽤在接受到JWT后,会⾸先对头部和载荷的内容⽤同⼀算法再次签名。

那么服务器应⽤是怎么知道我们⽤的是哪⼀种算法呢?别忘了,我们在JWT的头部中已经⽤alg字段指明了我们的加密算法
了。

如果服务器应⽤对头部和载荷再次以同样⽅法签名之后发现,⾃⼰计算出来的签名和接受到的签名不⼀样,那么就说明这个Token的内容被别⼈动过的,我们应该拒绝这个Token,返回⼀个HTTP
401 Unauthorized响应。

5、JWT安全性
使⽤JWT会暴露信息吗?是的。

所以,在JWT中,不应该在载荷⾥⾯加⼊任何敏感的数据。

在上⾯的例⼦中,我们传输的是⽤户的User ID。

这个值实际上不是什么敏感内容,⼀般情况下被知道也是安全
的。

但是像密码这样的内容就不能被放在JWT中了。

如果将⽤户的密码放在了JWT中,那么怀有恶意的第三⽅通过Base64解码就能很快地知道你的密码了。

总结如下:
不应该在jwt的payload部分存放敏感信息,因为该部分是客户端可解密的部分。

保护好secret私钥,该私钥⾮常重要。

如果可以,请使⽤https协议
6、JWT的适⽤场景
JWT适合⽤于向Web应⽤传递⼀些⾮敏感信息。

其实JWT还经常⽤于设计⽤户认证和授权系统,甚⾄实现Web应⽤的单点登录。

三、基于JWT协议的Bearer认证⽰例
1、⾃定义CheckJWTAttribute特性⽅式
之前使⽤的是这种⽅式,根据jwt原理⾃定义⽣成JWT、验证jwt,感觉挺好。

原理就是⾃定义⼀个拦截器(特性),拦截器对每个请求都优先进⾏处理,认证成功的进⾏下⼀步操作。

(1)新建netcore webapi项⽬
(2)定义JWTPayload类
using System;
namespace JwtAuthenticationOne
{
public class JWTPayload
{
public string UserName { get; set; }
public string Email { get; set; }
public string UserId { get; set; }
public DateTime Expire { get; set; }
}
}
(3)定义CheckJWTAttribute特性
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Threading.Tasks;
namespace JwtAuthenticationOne
{
///<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 pletedTask;
}
}
}
(4)定义NoCheckJWTAttribute类
namespace JwtAuthenticationOne
{
///<summary>
///忽略JWT校验
///</summary>
public class NoCheckJWTAttribute : BaseActionFilterAsync
{
}
}
(5)定义BaseActionFilterAsync类
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Threading.Tasks;
namespace JwtAuthenticationOne
{
public class BaseActionFilterAsync : Attribute, IAsyncActionFilter
{
///<summary>
/// action执⾏之前执⾏
///</summary>
///<param name="context"></param>
///<returns></returns>
public async virtual Task OnActionExecuting(ActionExecutingContext context)
{
await pletedTask;
}
///<summary>
/// action执⾏之后执⾏
///</summary>
///<param name="context"></param>
///<returns></returns>
public async virtual Task OnActionExecuted(ActionExecutedContext context)
{
await pletedTask;
}
///<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());
}
}
}
(6)定义AjaxResult类
namespace JwtAuthenticationOne
{
///<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; }
}
///<summary>
/// Ajax请求结果
///</summary>
public class AjaxResult<T> : AjaxResult
{
///<summary>
///返回数据
///</summary>
public T Data { get; set; }
}
}
(7)定义扩展类Extention
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Filters;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
namespace JwtAuthenticationOne
{
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);
}
///<summary>
///是否拥有某过滤器
///</summary>
///<typeparam name="T">过滤器类型</typeparam>
///<param name="actionExecutingContext">上下⽂</param>
///<returns></returns>
public static bool ContainsFilter<T>(this FilterContext actionExecutingContext)
{
return actionExecutingContext.Filters.Any(x => x.GetType() == typeof(T));
}
///<summary>
///获取Token
///</summary>
///<param name="req">请求</param>
///<returns></returns>
public static string GetToken(this HttpRequest req)
{
string tokenHeader = req.Headers["Authorization"].ToString();
if (tokenHeader.IsNullOrEmpty()) return null;
string pattern = "^Bearer (.*?)$";
if (!Regex.IsMatch(tokenHeader, pattern)) throw new Exception("token格式不对!格式为:Bearer {token}");
string token = Regex.Match(tokenHeader, pattern).Groups[1]?.ToString();
if (token.IsNullOrEmpty()) throw new Exception("token不能为空!");
return token;
}
///<summary>
///判断是否为Null或者空
///</summary>
///<param name="obj">对象</param>
///<returns></returns>
public static bool IsNullOrEmpty(this object obj)
{
if (obj == null)
return true;
else
{
string objStr = obj.ToString();
return string.IsNullOrEmpty(objStr);
}
}
///<summary>
/// Base64Url编码
///</summary>
///<param name="text">待编码的⽂本字符串</param>
///<returns>编码的⽂本字符串</returns>
public static string Base64UrlEncode(this string text)
{
var plainTextBytes = Encoding.UTF8.GetBytes(text);
var base64 = Convert.ToBase64String(plainTextBytes).Replace('+', '-').Replace('/', '_').TrimEnd('=');
return base64;
}
///<summary>
/// Base64Url解码
///</summary>
///<param name="base64UrlStr">使⽤Base64Url编码后的字符串</param>
///<returns>解码后的内容</returns>
public static string Base64UrlDecode(this string base64UrlStr)
{
base64UrlStr = base64UrlStr.Replace('-', '+').Replace('_', '/');
switch (base64UrlStr.Length % 4)
{
case2:
base64UrlStr += "==";
break;
case3:
base64UrlStr += "=";
break;
}
var bytes = Convert.FromBase64String(base64UrlStr);
return Encoding.UTF8.GetString(bytes);
}
///<summary>
///将Json字符串转为JObject
///</summary>
///<param name="jsonStr">Json字符串</param>
///<returns></returns>
public static JObject ToJObject(this string jsonStr)
{
return jsonStr == null ? JObject.Parse("{}") : JObject.Parse(jsonStr.Replace("&nbsp;", ""));
}
///<summary>
///将Json字符串反序列化为对象
///</summary>
///<typeparam name="T">对象类型</typeparam>
///<param name="jsonStr">Json字符串</param>
///<returns></returns>
public static T ToObject<T>(this string jsonStr)
{
return JsonConvert.DeserializeObject<T>(jsonStr);
}
///<summary>
/// HMACSHA256算法
///</summary>
///<param name="text">内容</param>
///<param name="secret">密钥</param>
///<returns></returns>
public static string ToHMACSHA256String(this string text, string secret)
{
secret = secret ?? "";
byte[] keyByte = Encoding.UTF8.GetBytes(secret);
byte[] messageBytes = Encoding.UTF8.GetBytes(text);
using (var hmacsha256 = new HMACSHA256(keyByte))
{
byte[] hashmessage = puteHash(messageBytes);
return Convert.ToBase64String(hashmessage).Replace('+', '-').Replace('/', '_').TrimEnd('=');
}
}
}
}
(8)定义JWTHelper类
using Newtonsoft.Json.Linq;
namespace JwtAuthenticationOne
{
///<summary>
/// JWT帮助类
///</summary>
public class JWTHelper
{
private static readonly string _headerBase64Url = "{\"alg\":\"HS256\",\"typ\":\"JWT\"}".Base64UrlEncode();
public static readonly string JWTSecret = AppsettingsHelper.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;
}
}
}
(9)定义AppsettingsHelper类
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace JwtAuthenticationOne
{
public class AppsettingsHelper
{
private static IConfiguration _config;
private static object _lock = new object();
public static IConfiguration Configuration
{
get
{
if (_config == null)
{
lock (_lock)
{
if (_config == null)
{
var builder = new ConfigurationBuilder().SetBasePath(AppContext.BaseDirectory).AddJsonFile("appsettings.json"); _config = builder.Build();
}
}
}
return _config;
}
set
{
_config = value;
}
}
///<summary>
///从AppSettings获取key的值
///</summary>
///<param name="key">key</param>
///<returns></returns>
public static string GetValue(string key)
{
return Configuration[key];
}
///<summary>
///获取连接字符串
///</summary>
///<param name="nameOfCon">连接字符串名</param>
///<returns></returns>
public static string GetConnectionString(string nameOfCon)
{
return Configuration.GetConnectionString(nameOfCon);
}
///<summary>
///封装要操作的字符
///</summary>
///<param name="sections">节点配置</param>
///<returns></returns>
public static string GetApp(params string[] sections)
{
try
{
if (sections.Any())
{
return Configuration[string.Join(":", sections)];
}
}
catch (Exception) { }
return"";
}
///<summary>
///递归获取配置信息数组
///</summary>
///<typeparam name="T"></typeparam>
///<param name="sections"></param>
///<returns></returns>
public static List<T> GetApp<T>(params string[] sections)
{
List<T> list = new List<T>();
// 引⽤ Microsoft.Extensions.Configuration.Binder 包
Configuration.Bind(string.Join(":", sections), list);
return list;
}
}
}
(10)使⽤CheckJWTAttribute
定义⼀个BaseApiController,所有的controller都继承该类,BaseApiController类使⽤CheckJWTAttribute特性
using Microsoft.AspNetCore.Mvc;
namespace JwtAuthenticationOne.Controllers
{
///<summary>
/// Mvc对外接⼝基控制器
///</summary>
[CheckJWT]
public class BaseApiController : ControllerBase
{
}
}
不需要认证的,使⽤NoCheckJWT限制,如下:
[HttpGet]
[NoCheckJWT]
public IEnumerable<WeatherForecast> Get()
{
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
}).ToArray();
}
(11)最终项⽬结构及效果
运⾏项⽬,查看效果:
2、使⽤aspnetcore提供的组件
(1)新建netcore webapi项⽬
(2)安装组件
通过nugut搜索安装Microsoft.AspNetCore.Authentication.JwtBearer
(3)jwtconfig配置
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"Jwt": {
"Issuer": "Issuer",
"Audience": "Audience",
"SigningKey": "EF1DA5B4-C7FA-4240-B997-7D1701BF9BE2"
}
}
(4)定义jwtconfig对应的实体
namespace JwtAuthenticationTwo
{
public class JwtConfig
{
public string Issuer { get; set; }
public string Audience { get; set; }
public string SigningKey { get; set; }
}
}
(5)Startup.cs 配置
ConfigureServices 中需要进⾏添加的信息
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
#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"));
#endregion
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "JwtAuthenticationTwo", Version = "v1" });
});
}
Configure需要添加的信息
// JWT⾝份认证
eAuthentication();
eAuthorization();
以上配置好了就基本上可以使⽤JWT验证了,下⾯介绍如何⽣成jwt token
(6)定义JwtHelper.cs
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
namespace JwtAuthenticationTwo
{
///<summary>
/// JWT帮助类信息
///</summary>
public class JwtHelper
{
///<summary>
///颁发JWT字符串
///</summary>
///<param name="tokenModel"></param>
///<returns></returns>
public static string IssueJwt(Claim[] claim)
{
// 读取对应的配置信息
string iss = AppsettingsHelper.GetApp("Jwt:Issuer");
string aud = AppsettingsHelper.GetApp("Jwt:Audience");
string secret = AppsettingsHelper.GetApp("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;
}
}
}
(7)定义AppsettingsHelper.cs
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
namespace JwtAuthenticationTwo
{
public class AppsettingsHelper
{
private static IConfiguration _config;
private static object _lock = new object();
public static IConfiguration Configuration
{
get
{
if (_config == null)
{
lock (_lock)
{
if (_config == null)
{
var builder = new ConfigurationBuilder().SetBasePath(AppContext.BaseDirectory).AddJsonFile("appsettings.json"); _config = builder.Build();
}
}
}
return _config;
}
set
{
_config = value;
}
}
///<summary>
///从AppSettings获取key的值
///</summary>
///<param name="key">key</param>
///<returns></returns>
public static string GetValue(string key)
{
return Configuration[key];
}
///<summary>
///获取连接字符串
///</summary>
///<param name="nameOfCon">连接字符串名</param>
///<returns></returns>
public static string GetConnectionString(string nameOfCon)
{
return Configuration.GetConnectionString(nameOfCon);
}
///<summary>
///封装要操作的字符
///</summary>
///<param name="sections">节点配置</param>
///<returns></returns>
public static string GetApp(params string[] sections)
{
try
{
if (sections.Any())
{
return Configuration[string.Join(":", sections)];
}
}
catch (Exception) { }
return"";
}
///<summary>
///递归获取配置信息数组
///</summary>
///<typeparam name="T"></typeparam>
///<param name="sections"></param>
///<returns></returns>
public static List<T> GetApp<T>(params string[] sections)
{
List<T> list = new List<T>();
// 引⽤ Microsoft.Extensions.Configuration.Binder 包
Configuration.Bind(string.Join(":", sections), list);
return list;
}
}
}
(8)控制器层使⽤⽰例
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
namespace JwtAuthenticationTwo.Controllers
{
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" };
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[Authorize]
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
}
///<summary>
///测试获取token信息
///</summary>
///<returns></returns>
[HttpPost]
public ActionResult<string> GetToken()
{
//参数存储的信息
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 });
}
}
}
(9)项⽬结构和效果
项⽬运⾏效果:
然后整体就基本上结束了。

虽然说这种⽅式没有⾃定义的⽅式代码容易读,但是也⽅便了好多
四、扩展
第三部分介绍了使⽤Microsoft.AspNetCore.Authentication.JwtBearer 来实现jwt认证,微软官⽹提供了对。

这⾥简单的描述下。

先来熟悉⼏个概念:
⾝份验证:⾝份验证基本上意味着使⽤给定的信息并尝试⽤该信息对⽤户进⾏⾝份验证。

因此,这将尝试创建⼀个⽤户标识,并使其可⽤于框架。

例如,cookie认证⽅案使⽤cookie数据恢复⽤户⾝份。

或者JWT bearing认证⽅案将使⽤在请求中作为Authorization头的⼀部分提供的令牌来创建⽤户⾝份。

挑战:当认证⽅案受到挑战时,该⽅案应提⽰⽤户对⾃⼰进⾏认证。

例如,这可能意味着⽤户被重定向到⼀个登录表单,或者将被重定向到外部⾝份验证提供者。

Forbid:当⼀个认证⽅案被禁⽌时,该⽅案基本上只是响应⼀些东西,告诉⽤户他们可能不做任何他们试图做的事情。

这通常是⼀个HTTP 403错误,可能是重定向到某些错误页⾯。

登录:当⼀个⾝份验证⽅案正在被登录时,该⽅案将被告知获取⼀个现有⽤户(claimsprinprincipal)并以某种⽅式持久化该⽤户。

例如,在cookie认证⽅案上注册⽤户基本上会创建⼀个包含该⽤户⾝份的c ookie。

退出:这与登录相反,它将告诉⾝份验证⽅案删除持久性。

在cookie⽅案上注销将有效地使cookie过期。

现在来具体说下JwtBearer中涉及的概念,先来说下AuthenticationOptions涉及到的属性
Defaultauthenticatscheme:设置认证时使⽤的默认⽅案。

Defaultchallengesscheme:设置挑战时使⽤的默认⽅案。

DefaultForbidScheme:设置禁⽌访问时使⽤的默认⽅案。

DefaultSignInScheme:设置要登录的默认⽅案。

DefaultSignOutScheme:设置退出的默认⽅案。

DefaultScheme:设置默认回退⽅案。

正常情况下,我们没有必要把这些属性全部进⾏配置,如下说明:
⾝份认证:配置defaultauthenticatscheme或DefaultScheme
挑战:配置defaultchallengesscheme或DefaultScheme
Forbid:配置 DefaultForbidScheme或defaultchallengesscheme或DefaultScheme
Sign-in:配置DefaultSignInScheme或DefaultScheme
Sign-out:配置 DefaultSignOutScheme或DefaultScheme
有些时候我们也可以⾃定义认证或者挑战⽅案。

下⾯来说下JwtBear⽅案中的事件:
OnChallenge:发⽣质询的时,在将询问发送回调⽤⽅之前调⽤。

OnAuthenticationFailed:认证期间出现异常调⽤。

OnForbidden:因为禁⽌⽽导致授权失败调⽤。

OnTokenValidated:令牌通过认证后调⽤。

OnMessageReceived:⾸次收到协议消息时调⽤。

虽然使⽤微软提供的JwtBearer⽅便了很多,但是不是那么好理解了,不太喜欢使⽤该⽅案的可以采⽤⾃定义的⽅式实现jwt认证。

五、源码下载
源码:。

相关文档
最新文档