Asp.Net Core基于JWT認(rèn)證的數(shù)據(jù)接口網(wǎng)關(guān)實(shí)例代碼
前言
近日,應(yīng)一位朋友的邀請(qǐng)寫了個(gè)Asp.Net Core基于JWT認(rèn)證的數(shù)據(jù)接口網(wǎng)關(guān)Demo。朋友自己開(kāi)了個(gè)公司,接到的一個(gè)升級(jí)項(xiàng)目,客戶要求用Aps.Net Core做數(shù)據(jù)網(wǎng)關(guān)服務(wù)且基于JWT認(rèn)證實(shí)現(xiàn)對(duì)前后端分離的數(shù)據(jù)服務(wù)支持,于是想到我一直做.Net開(kāi)發(fā),問(wèn)我是否對(duì).Net Core有所了解?能不能做個(gè)簡(jiǎn)單Demo出來(lái)看看?我說(shuō),分道揚(yáng)鑣之后我不是調(diào)用別人的接口就是提供接口給別人調(diào)用,于是便有了以下示例代碼。
示例要求能演示獲取Token及如何使用該Token訪問(wèn)數(shù)據(jù)資源,在Demo中實(shí)現(xiàn)了JWT的頒發(fā)及驗(yàn)證以及重寫一個(gè)ActionAuthorizeAttribute實(shí)現(xiàn)對(duì)具體數(shù)據(jù)接口的調(diào)用權(quán)限控制,先看一下項(xiàng)目截圖:
[項(xiàng)目截圖]
項(xiàng)目文件介紹
解決方案下只有一個(gè)項(xiàng)目,項(xiàng)目名稱就叫Jwt.Gateway,包含主要文件有:
- Controllers目錄下的ApiActionFilterAttribute.cs文件,繼承Microsoft.AspNetCore.Mvc.Filters.ActionFilterAttribute,用于校驗(yàn)接口調(diào)用者對(duì)具體接口的訪問(wèn)權(quán)限。
- Controllers目錄下的ApiBase.cs文件,繼承Microsoft.AspNetCore.Mvc.Controller,具有Microsoft.AspNetCore.Authorization.Authorize特性引用,用于讓所有數(shù)據(jù)接口用途的控制器繼承,定義有CurrentAppKey屬性(來(lái)訪應(yīng)用程序的身份標(biāo)識(shí))并在OnActionExecuting事件中統(tǒng)一分析Claims并賦值。
- Controllers目錄下的TokenController.cs控制器文件,用于對(duì)調(diào)用方應(yīng)用程序獲取及注銷Token。
- Controllers目錄下的UsersController.cs控制器文件,繼承ApiBase.cs,作為數(shù)據(jù)調(diào)用示例。
- MiddleWares目錄下的ApiCustomException.cs文件,是一個(gè)數(shù)據(jù)接口的統(tǒng)一異常處理中間件。
- Models目錄下的ApiResponse.cs文件,用于做數(shù)據(jù)接口的統(tǒng)一數(shù)據(jù)及錯(cuò)誤信息輸出實(shí)體模型。
- Models目錄下的User.cs文件,示例數(shù)據(jù)實(shí)體模型。
- Program.cs及Startup.cs文件就不介紹了,隨便建個(gè)空項(xiàng)目都有。
項(xiàng)目文件代碼
ApiActionFilterAttribute.cs
Controllers目錄下的ApiActionFilterAttribute.cs文件,繼承Microsoft.AspNetCore.Mvc.Filters.ActionFilterAttribute,用于校驗(yàn)接口調(diào)用者對(duì)具體接口的訪問(wèn)權(quán)限。
設(shè)想每一個(gè)到訪的請(qǐng)求都是一個(gè)應(yīng)用程序,每一個(gè)應(yīng)用程序都分配有基本的Key和Password,每一個(gè)應(yīng)用程序具有不同的接口訪問(wèn)權(quán)限,所以在具體的數(shù)據(jù)接口上應(yīng)該聲明該接口所要求的權(quán)限值,比如修改用戶信息的接口應(yīng)該在接口方法上聲明需要具有“修改用戶”的權(quán)限,用例: [ApiActionFilter("用戶修改")] 。
大部分情況下一個(gè)接口(方法)對(duì)應(yīng)一個(gè)操作,這樣基本上就能應(yīng)付了,但是不排除有時(shí)候可能需要多個(gè)權(quán)限組合進(jìn)行驗(yàn)證,所以該文件中有一個(gè)對(duì)多個(gè)權(quán)限值進(jìn)行校驗(yàn)的“與”和“和”枚舉,用例: [ApiActionFilter(new string[] { "用戶修改", "用戶錄入", "用戶刪除" },ApiActionFilterAttributeOption.AND)] ,這樣好像就差不多了。
由于在一個(gè)接口調(diào)用之后可能需要將該接口所聲明需要的權(quán)限值記入日志等需求,因此權(quán)限值集合將被寫入到HttpContext.Items["Permissions"]中以方便可能的后續(xù)操作訪問(wèn),看代碼:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Filters;
namespace Jwt.Gateway.Controllers
{
public enum ApiActionFilterAttributeOption
{
OR,AND
}
public class ApiActionFilterAttribute : Microsoft.AspNetCore.Mvc.Filters.ActionFilterAttribute
{
List<string> Permissions = new List<string>();
ApiActionFilterAttributeOption Option = ApiActionFilterAttributeOption.AND;
public ApiActionFilterAttribute(string permission)
{
Permissions.Add(permission);
}
public ApiActionFilterAttribute(string[] permissions, ApiActionFilterAttributeOption option)
{
foreach(var permission in permissions) {
if (Permissions.Contains(permission))
{
continue;
}
Permissions.Add(permission);
}
Option = option;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
var key = GetAppKey(context);
List<string> keyPermissions = GetAppKeyPermissions(key);
var isAnd = Option == ApiActionFilterAttributeOption.AND;
var permissionsCount = Permissions.Count;
var keyPermissionsCount = keyPermissions.Count;
for (var i = 0; i < permissionsCount; i++)
{
bool flag = false;
for (var j = 0; j < keyPermissions.Count; j++)
{
if (flag = string.Equals(Permissions[i], keyPermissions[j], StringComparison.OrdinalIgnoreCase))
{
break;
}
}
if (flag)
{
continue;
}
if (isAnd)
{
throw new Exception("應(yīng)用“" + key + "”缺少“" + Permissions[i] + "”的權(quán)限");
}
}
context.HttpContext.Items.Add("Permissions", Permissions);
base.OnActionExecuting(context);
}
private string GetAppKey(ActionExecutingContext context)
{
var claims = context.HttpContext.User.Claims;
if (claims == null)
{
throw new Exception("未能獲取到應(yīng)用標(biāo)識(shí)");
}
var claimKey = claims.ToList().Find(o => string.Equals(o.Type, "AppKey", StringComparison.OrdinalIgnoreCase));
if (claimKey == null)
{
throw new Exception("未能獲取到應(yīng)用標(biāo)識(shí)");
}
return claimKey.Value;
}
private List<string> GetAppKeyPermissions(string appKey)
{
List<string> li = new List<string>
{
"用戶明細(xì)","用戶列表","用戶錄入","用戶修改","用戶刪除"
};
return li;
}
}
}
ApiActionAuthorizeAttribute.cs
ApiBase.cs
Controllers目錄下的ApiBase.cs文件,繼承Microsoft.AspNetCore.Mvc.Controller,具有Microsoft.AspNetCore.Authorization.Authorize特性引用,用于讓所有數(shù)據(jù)接口用途的控制器繼承,定義有CurrentAppKey屬性(來(lái)訪應(yīng)用程序的身份標(biāo)識(shí))并在OnActionExecuting事件中統(tǒng)一分析Claims并賦值。
通過(guò)驗(yàn)證之后,Aps.Net Core會(huì)在HttpContext.User.Claims中將將來(lái)訪者的身份信息記錄下來(lái),我們可以通過(guò)該集合得到來(lái)訪者的身份信息。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
namespace Jwt.Gateway.Controllers
{
[Microsoft.AspNetCore.Authorization.Authorize]
public class ApiBase : Microsoft.AspNetCore.Mvc.Controller
{
private string _CurrentAppKey = "";
public string CurrentAppKey { get { return _CurrentAppKey; } }
public override void OnActionExecuting(ActionExecutingContext context)
{
var claims = context.HttpContext.User.Claims.ToList();
var claim = claims.Find(o => o.Type == "appKey");
if (claim == null)
{
throw new Exception("未通過(guò)認(rèn)證");
}
var appKey = claim.Value;
if (string.IsNullOrEmpty(appKey))
{
throw new Exception("appKey不合法");
}
_CurrentAppKey = appKey;
base.OnActionExecuting(context);
}
}
}
ApiBase.cs
TokenController.cs
Controllers目錄下的TokenController.cs控制器文件,用于對(duì)調(diào)用方應(yīng)用程序獲取及注銷Token。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace Jwt.Gateway.Controllers
{
[Route("api/[controller]/[action]")]
public class TokenController : Controller
{
private readonly Microsoft.Extensions.Configuration.IConfiguration _configuration;
public TokenController(Microsoft.Extensions.Configuration.IConfiguration configuration)
{
_configuration = configuration;
}
// /api/token/get
public IActionResult Get(string appKey, string appPassword)
{
try
{
if (string.IsNullOrEmpty(appKey))
{
throw new Exception("缺少appKey");
}
if (string.IsNullOrEmpty(appKey))
{
throw new Exception("缺少appPassword");
}
if (appKey != "myKey" && appPassword != "myPassword")//固定的appKey及appPassword,實(shí)際項(xiàng)目中應(yīng)該來(lái)自數(shù)據(jù)庫(kù)或配置文件
{
throw new Exception("配置不存在");
}
var key = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(_configuration["JwtSecurityKey"]));
var creds = new Microsoft.IdentityModel.Tokens.SigningCredentials(key, Microsoft.IdentityModel.Tokens.SecurityAlgorithms.HmacSha256);
var claims = new List<System.Security.Claims.Claim>();
claims.Add(new System.Security.Claims.Claim("appKey", appKey));//僅在Token中記錄appKey
var token = new System.IdentityModel.Tokens.Jwt.JwtSecurityToken(
issuer: _configuration["JwtTokenIssuer"],
audience: _configuration["JwtTokenAudience"],
claims: claims,
expires: DateTime.Now.AddMinutes(30),
signingCredentials: creds);
return Ok(new Models.ApiResponse { status = 1, message = "OK", data = new System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler().WriteToken(token) });
}
catch(Exception ex)
{
return Ok(new Models.ApiResponse { status = 0, message = ex.Message, data = "" });
}
}
// /api/token/delete
public IActionResult Delete(string token)
{
//code: 加入黑名單,使其無(wú)效
return Ok(new Models.ApiResponse { status = 1, message = "OK", data = "" });
}
}
}
TokenController.cs
UsersController.cs
Controllers目錄下的UsersController.cs控制器文件,繼承ApiBase.cs,作為數(shù)據(jù)調(diào)用示例。
該控制器定義了對(duì)User對(duì)象常規(guī)的明細(xì)、列表、錄入、修改、刪除等操作。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
namespace Jwt.Gateway.Controllers
{
[Produces("application/json")]
[Route("api/[controller]/[action]")]
public class UsersController : ApiBase
{
/*
* 1.要訪問(wèn)訪問(wèn)該控制器提供的接口請(qǐng)先通過(guò)"/api/token/get"獲取token
* 2.訪問(wèn)該控制器提供的接口http請(qǐng)求頭必須具有值為"Bearer+空格+token"的Authorization鍵,格式參考:
* "Authorization"="Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiQXBwIiwiYXBwS2V5IjoibXlLZXkiLCJleHAiOjE1NTE3ODc2MDMsImlzcyI6IkdhdGV3YXkiLCJhdWQiOiJhdWRpZW5jZSJ9.gQ9_Q7HUT31oFyfl533T-bNO5IWD2drl0NmD1JwQkMI"
*/
/// <summary>
/// 臨時(shí)用戶測(cè)試數(shù)據(jù),實(shí)際項(xiàng)目中應(yīng)該來(lái)自數(shù)據(jù)庫(kù)等媒介
/// </summary>
static List<Models.User> _Users = null;
static object _Lock = new object();
public UsersController()
{
if (_Users == null)
{
lock (_Lock)
{
if (_Users == null)
{
_Users = new List<Models.User>();
var now = DateTime.Now;
for(var i = 0; i < 10; i++)
{
var num = i + 1;
_Users.Add(new Models.User { UserId = num, UserName = "name"+num, UserPassword = "pwd"+num, UserJoinTime = now });
}
}
}
}
}
// /api/users/detail
[ApiActionFilter("用戶明細(xì)")]
public IActionResult Detail(long userId)
{
/*
//獲取appKey(在ApiBase中寫入)
var appKey = CurrentAppKey;
//獲取使用的權(quán)限(在ApiActionAuthorizeAttribute中寫入)
var permissions = HttpContext.Items["Permissions"];
*/
var user = _Users.Find(o => o.UserId == userId);
if (user == null)
{
throw new Exception("用戶不存在");
}
return Ok(new Models.ApiResponse { data = user, status = 1, message = "OK" });
}
// /api/users/list
[ApiActionFilter("用戶列表")]
public IActionResult List(int page, int size)
{
page = page < 1 ? 1 : page;
size = size < 1 ? 1 : size;
var total = _Users.Count();
var pages = total % size == 0 ? total / size : ((long)Math.Floor((double)total / size + 1));
if (page > pages)
{
return Ok(new Models.ApiResponse { data = new List<Models.User>(), status = 1, message = "OK", total = total });
}
var li = new List<Models.User>();
var startIndex = page * size - size;
var endIndex = startIndex + size - 1;
if (endIndex > total - 1)
{
endIndex = total - 1;
}
for(; startIndex <= endIndex; startIndex++)
{
li.Add(_Users[startIndex]);
}
return Ok(new Models.ApiResponse { data = li, status = 1, message = "OK", total = total });
}
// /api/users/add
[ApiActionFilter("用戶錄入")]
public IActionResult Add()
{
return Ok(new Models.ApiResponse { status = 1, message = "OK" });
}
// /api/users/update
[ApiActionFilter(new string[] { "用戶修改", "用戶錄入", "用戶刪除" },ApiActionFilterAttributeOption.AND)]
public IActionResult Update()
{
return Ok(new Models.ApiResponse { status = 1, message = "OK" });
}
// /api/users/delete
[ApiActionFilter("用戶刪除")]
public IActionResult Delete()
{
return Ok(new Models.ApiResponse { status = 1, message = "OK" });
}
}
}
UsersController.cs
ApiCustomException.cs
MiddleWares目錄下的ApiCustomException.cs文件,是一個(gè)數(shù)據(jù)接口的統(tǒng)一異常處理中間件。
該文件整理并抄襲自:https://www.cnblogs.com/ShenNan/p/10197231.html
在此特別感謝一下作者的先行貢獻(xiàn),并請(qǐng)?jiān)徫覠o(wú)恥的抄襲。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
namespace Jwt.Gateway.MiddleWares
{
//參考: https://www.cnblogs.com/ShenNan/p/10197231.html
public enum ApiCustomExceptionHandleType
{
JsonHandle = 0,
PageHandle = 1,
Both = 2
}
public class ApiCustomExceptionMiddleWareOption
{
public ApiCustomExceptionMiddleWareOption(
ApiCustomExceptionHandleType handleType = ApiCustomExceptionHandleType.JsonHandle,
IList<PathString> jsonHandleUrlKeys = null,
string errorHandingPath = "")
{
HandleType = handleType;
JsonHandleUrlKeys = jsonHandleUrlKeys;
ErrorHandingPath = errorHandingPath;
}
public ApiCustomExceptionHandleType HandleType { get; set; }
public IList<PathString> JsonHandleUrlKeys { get; set; }
public PathString ErrorHandingPath { get; set; }
}
public class ApiCustomExceptionMiddleWare
{
private RequestDelegate _next;
private ApiCustomExceptionMiddleWareOption _option;
private IDictionary<int, string> _exceptionStatusCodeDic;
public ApiCustomExceptionMiddleWare(RequestDelegate next, ApiCustomExceptionMiddleWareOption option)
{
_next = next;
_option = option;
_exceptionStatusCodeDic = new Dictionary<int, string>
{
{ 401, "未授權(quán)的請(qǐng)求" },
{ 404, "找不到該頁(yè)面" },
{ 403, "訪問(wèn)被拒絕" },
{ 500, "服務(wù)器發(fā)生意外的錯(cuò)誤" }
//其余狀態(tài)自行擴(kuò)展
};
}
public async Task Invoke(HttpContext context)
{
Exception exception = null;
try
{
await _next(context);
}
catch (Exception ex)
{
context.Response.Clear();
context.Response.StatusCode = 200;//手動(dòng)設(shè)置狀態(tài)碼(總是成功)
exception = ex;
}
finally
{
if (_exceptionStatusCodeDic.ContainsKey(context.Response.StatusCode) &&
!context.Items.ContainsKey("ExceptionHandled"))
{
var errorMsg = string.Empty;
if (context.Response.StatusCode == 500 && exception != null)
{
errorMsg = $"{_exceptionStatusCodeDic[context.Response.StatusCode]}\r\n{(exception.InnerException != null ? exception.InnerException.Message : exception.Message)}";
}
else
{
errorMsg = _exceptionStatusCodeDic[context.Response.StatusCode];
}
exception = new Exception(errorMsg);
}
if (exception != null)
{
var handleType = _option.HandleType;
if (handleType == ApiCustomExceptionHandleType.Both)
{
var requestPath = context.Request.Path;
handleType = _option.JsonHandleUrlKeys != null && _option.JsonHandleUrlKeys.Count(
k => requestPath.StartsWithSegments(k, StringComparison.CurrentCultureIgnoreCase)) > 0 ?
ApiCustomExceptionHandleType.JsonHandle :
ApiCustomExceptionHandleType.PageHandle;
}
if (handleType == ApiCustomExceptionHandleType.JsonHandle)
await JsonHandle(context, exception);
else
await PageHandle(context, exception, _option.ErrorHandingPath);
}
}
}
private Jwt.Gateway.Models.ApiResponse GetApiResponse(Exception ex)
{
return new Jwt.Gateway.Models.ApiResponse() { status = 0, message = ex.Message };
}
private async Task JsonHandle(HttpContext context, Exception ex)
{
var apiResponse = GetApiResponse(ex);
var serialzeStr = Newtonsoft.Json.JsonConvert.SerializeObject(apiResponse);
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(serialzeStr, System.Text.Encoding.UTF8);
}
private async Task PageHandle(HttpContext context, Exception ex, PathString path)
{
context.Items.Add("Exception", ex);
var originPath = context.Request.Path;
context.Request.Path = path;
try
{
await _next(context);
}
catch { }
finally
{
context.Request.Path = originPath;
}
}
}
public static class ApiCustomExceptionMiddleWareExtensions
{
public static IApplicationBuilder UseApiCustomException(this IApplicationBuilder app, ApiCustomExceptionMiddleWareOption option)
{
return app.UseMiddleware<ApiCustomExceptionMiddleWare>(option);
}
}
}
ApiCustomException.cs
配置相關(guān)
appsettings.json
算法'HS256'要求SecurityKey.KeySize大于'128'位,所以JwtSecurityKey可不要太短了哦。
{
"Urls": "http://localhost:60000",
"AllowedHosts": "*",
"JwtSecurityKey": "areyouokhhhhhhhhhhhhhhhhhhhhhhhhhhh",
"JwtTokenIssuer": "Jwt.Gateway",
"JwtTokenAudience": "App"
}
appsettings.json
Startup.cs
關(guān)于JWT的配置可以在通過(guò)JwtBearerOptions加入一些自己的事件處理邏輯,共有4個(gè)事件可供調(diào)用:
OnAuthenticationFailed,OnMessageReceived,OnTokenValidated,OnChallenge, 本示例中是在OnTokenValidated中插入Token黑名單的校驗(yàn)邏輯。黑名單應(yīng)該是Jwt應(yīng)用場(chǎng)景中主動(dòng)使Token過(guò)期的主流做法了。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Jwt.Gateway.MiddleWares;
using Microsoft.Extensions.DependencyInjection;
namespace Jwt.Gateway
{
public class Startup
{
private readonly Microsoft.Extensions.Configuration.IConfiguration _configuration;
public Startup(Microsoft.Extensions.Configuration.IConfiguration configuration)
{
_configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options => {
options.Events = new Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerEvents
{
/*OnMessageReceived = context =>
{
context.Token = context.Request.Query["access_token"];
return Task.CompletedTask;
},*/
OnTokenValidated = context =>
{
var token = ((System.IdentityModel.Tokens.Jwt.JwtSecurityToken)context.SecurityToken).RawData;
if (InBlacklist(token))
{
context.Fail("token in blacklist");
}
return Task.CompletedTask;
}
};
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidAudience = _configuration["JwtTokenAudience"],
ValidIssuer = _configuration["JwtTokenIssuer"],
IssuerSigningKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(_configuration["JwtSecurityKey"]))
};
});
services.AddMvc().AddJsonOptions(option=> {
option.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss.fff";
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseApiCustomException(new ApiCustomExceptionMiddleWareOption(
handleType: ApiCustomExceptionHandleType.Both,
jsonHandleUrlKeys: new PathString[] { "/api" },
errorHandingPath: "/home/error"));
app.UseAuthentication();
app.UseMvc();
}
bool InBlacklist(string token)
{
//code: 實(shí)際項(xiàng)目中應(yīng)該查詢數(shù)據(jù)庫(kù)或配置文件進(jìn)行比對(duì)
return false;
}
}
}
Startup.cs
Program.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace Jwt.Gateway
{
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args)
{
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true)
.Build();
return WebHost.CreateDefaultBuilder(args)
.UseKestrel()
.UseConfiguration(config)
.UseStartup<Startup>()
.Build();
}
}
}
Program.cs
運(yùn)行截圖
[運(yùn)行截圖-獲取Token]
[運(yùn)行截圖-配置Fiddler調(diào)用接口獲取數(shù)據(jù)]
[運(yùn)行截圖-獲取到數(shù)據(jù)]
如果Token校驗(yàn)失敗將會(huì)返回401錯(cuò)誤!
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)腳本之家的支持。
- .NET?Core支持Cookie和JWT混合認(rèn)證、授權(quán)的方法
- .net?core?api接口JWT方式認(rèn)證Token
- ASP.NET?Core應(yīng)用JWT進(jìn)行用戶認(rèn)證及Token的刷新方案
- asp.net core3.1cookie和jwt混合認(rèn)證授權(quán)實(shí)現(xiàn)多種身份驗(yàn)證方案
- AntDesign Pro + .NET Core 實(shí)現(xiàn)基于JWT的登錄認(rèn)證功能
- .Net Core官方JWT授權(quán)驗(yàn)證的全過(guò)程
- 詳解ASP.NET Core Web Api之JWT刷新Token
- ASP.NET Core使用JWT認(rèn)證授權(quán)的方法
- ASP.NET Core學(xué)習(xí)之使用JWT認(rèn)證授權(quán)詳解
- 淺談ASP.NET Core 中jwt授權(quán)認(rèn)證的流程原理
- ASP.Net Core3.0中使用JWT認(rèn)證的實(shí)現(xiàn)
- .NET core 3.0如何使用Jwt保護(hù)api詳解
- asp.net core集成JWT的步驟記錄
- .net core webapi jwt 更為清爽的認(rèn)證詳解
- .Net Core實(shí)現(xiàn)JWT授權(quán)認(rèn)證
相關(guān)文章
ASP.NET網(wǎng)站聊天室的設(shè)計(jì)與實(shí)現(xiàn)(第3節(jié))
這篇文章主要介紹了ASP.NET網(wǎng)站聊天室的設(shè)計(jì)與實(shí)現(xiàn),了解Session、Application對(duì)象的屬性和事件,并且掌握利用它們?cè)陧?yè)面間保存和傳遞數(shù)據(jù)的方法,需要的朋友可以參考下2015-08-08
.net中如何以純二進(jìn)制的形式在內(nèi)存中繪制一個(gè)對(duì)象
這篇文章主要介紹了如何以純二進(jìn)制的形式在內(nèi)存中繪制一個(gè)對(duì)象,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-07-07
在ASP.NET Core5.0中訪問(wèn)HttpContext的方法步驟
這篇文章主要介紹了在ASP.NET Core5.0中訪問(wèn)HttpContext的方法步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
ASP.NET Core MVC 中實(shí)現(xiàn)中英文切換的示例代碼
這篇文章主要介紹了ASP.NET Core MVC 中實(shí)現(xiàn)中英文切換的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02
利用ASP.NET MVC+Bootstrap搭建個(gè)人博客之打造清新分頁(yè)Helper(三)
這篇文章主要介紹了利用ASP.NET MVC+Bootstrap搭建個(gè)人博客之打造清新分頁(yè)Helper(三)的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-06-06
asp.net下無(wú)法循環(huán)綁定投票的標(biāo)題和選項(xiàng)的解決方法
asp.net下無(wú)法循環(huán)綁定投票的標(biāo)題和選項(xiàng)與無(wú)法循環(huán)獲得用戶的選擇的解決方法。2010-12-12

