您现在的位置是:网站首页> 编程资料编程资料
Asp.Net Core基于JWT认证的数据接口网关实例代码_实用技巧_
2023-05-24
343人已围观
简介 Asp.Net Core基于JWT认证的数据接口网关实例代码_实用技巧_
前言
近日,应一位朋友的邀请写了个Asp.Net Core基于JWT认证的数据接口网关Demo。朋友自己开了个公司,接到的一个升级项目,客户要求用Aps.Net Core做数据网关服务且基于JWT认证实现对前后端分离的数据服务支持,于是想到我一直做.Net开发,问我是否对.Net Core有所了解?能不能做个简单Demo出来看看?我说,分道扬镳之后我不是调用别人的接口就是提供接口给别人调用,于是便有了以下示例代码。
示例要求能演示获取Token及如何使用该Token访问数据资源,在Demo中实现了JWT的颁发及验证以及重写一个ActionAuthorizeAttribute实现对具体数据接口的调用权限控制,先看一下项目截图:
[项目截图]
项目文件介绍
解决方案下只有一个项目,项目名称就叫Jwt.Gateway,包含主要文件有:
- Controllers目录下的ApiActionFilterAttribute.cs文件,继承Microsoft.AspNetCore.Mvc.Filters.ActionFilterAttribute,用于校验接口调用者对具体接口的访问权限。
- Controllers目录下的ApiBase.cs文件,继承Microsoft.AspNetCore.Mvc.Controller,具有Microsoft.AspNetCore.Authorization.Authorize特性引用,用于让所有数据接口用途的控制器继承,定义有CurrentAppKey属性(来访应用程序的身份标识)并在OnActionExecuting事件中统一分析Claims并赋值。
- Controllers目录下的TokenController.cs控制器文件,用于对调用方应用程序获取及注销Token。
- Controllers目录下的UsersController.cs控制器文件,继承ApiBase.cs,作为数据调用示例。
- MiddleWares目录下的ApiCustomException.cs文件,是一个数据接口的统一异常处理中间件。
- Models目录下的ApiResponse.cs文件,用于做数据接口的统一数据及错误信息输出实体模型。
- Models目录下的User.cs文件,示例数据实体模型。
- Program.cs及Startup.cs文件就不介绍了,随便建个空项目都有。
项目文件代码
ApiActionFilterAttribute.cs
Controllers目录下的ApiActionFilterAttribute.cs文件,继承Microsoft.AspNetCore.Mvc.Filters.ActionFilterAttribute,用于校验接口调用者对具体接口的访问权限。
设想每一个到访的请求都是一个应用程序,每一个应用程序都分配有基本的Key和Password,每一个应用程序具有不同的接口访问权限,所以在具体的数据接口上应该声明该接口所要求的权限值,比如修改用户信息的接口应该在接口方法上声明需要具有“修改用户”的权限,用例: [ApiActionFilter("用户修改")] 。
大部分情况下一个接口(方法)对应一个操作,这样基本上就能应付了,但是不排除有时候可能需要多个权限组合进行验证,所以该文件中有一个对多个权限值进行校验的“与”和“和”枚举,用例: [ApiActionFilter(new string[] { "用户修改", "用户录入", "用户删除" },ApiActionFilterAttributeOption.AND)] ,这样好像就差不多了。
由于在一个接口调用之后可能需要将该接口所声明需要的权限值记入日志等需求,因此权限值集合将被写入到HttpContext.Items["Permissions"]中以方便可能的后续操作访问,看代码:
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 Permissions = new List(); 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 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("应用“" + key + "”缺少“" + Permissions[i] + "”的权限"); } } 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("未能获取到应用标识"); } var claimKey = claims.ToList().Find(o => string.Equals(o.Type, "AppKey", StringComparison.OrdinalIgnoreCase)); if (claimKey == null) { throw new Exception("未能获取到应用标识"); } return claimKey.Value; } private List GetAppKeyPermissions(string appKey) { List li = new List { "用户明细","用户列表","用户录入","用户修改","用户删除" }; return li; } } } ApiActionAuthorizeAttribute.cs ApiBase.cs
Controllers目录下的ApiBase.cs文件,继承Microsoft.AspNetCore.Mvc.Controller,具有Microsoft.AspNetCore.Authorization.Authorize特性引用,用于让所有数据接口用途的控制器继承,定义有CurrentAppKey属性(来访应用程序的身份标识)并在OnActionExecuting事件中统一分析Claims并赋值。
通过验证之后,Aps.Net Core会在HttpContext.User.Claims中将将来访者的身份信息记录下来,我们可以通过该集合得到来访者的身份信息。
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("未通过认证"); } var appKey = claim.Value; if (string.IsNullOrEmpty(appKey)) { throw new Exception("appKey不合法"); } _CurrentAppKey = appKey; base.OnActionExecuting(context); } } } ApiBase.csTokenController.cs
Controllers目录下的TokenController.cs控制器文件,用于对调用方应用程序获取及注销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,实际项目中应该来自数据库或配置文件 { 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(); 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: 加入黑名单,使其无效 return Ok(new Models.ApiResponse { status = 1, message = "OK", data = "" }); } } } TokenController.cs UsersController.cs
Controllers目录下的UsersController.cs控制器文件,继承ApiBase.cs,作为数据调用示例。
该控制器定义了对User对象常规的明细、列表、录入、修改、删除等操作。
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.要访问访问该控制器提供的接口请先通过"/api/token/get"获取token * 2.访问该控制器提供的接口http请求头必须具有值为"Bearer+空格+token"的Authorization键,格式参考: * "Authorization"="Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiQXBwIiwiYXBwS2V5IjoibXlLZXkiLCJleHAiOjE1NTE3ODc2MDMsImlzcyI6IkdhdGV3YXkiLCJhdWQiOiJhdWRpZW5jZSJ9.gQ9_Q7HUT31oFyfl533T-bNO5IWD2drl0NmD1JwQkMI" */ /// /// 临时用户测试数据,实际项目中应该来自数据库等媒介 /// static List _Users = null; static object _Lock = new object(); public UsersController() { if (_Users == null) { lock (_Lock) { if (_Users == null) { _Users = new List(); 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("用户明细")] public IActionResult Detail(long userId) { /* //获取appKey(在ApiBase中写入) var appKey = CurrentAppKey; //获取使用的权限(在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(), status = 1, message = "OK", total = total }); } var li = new List(); 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 Ad
相关内容
- 未在本地计算机上注册“microsoft.ACE.oledb.12.0”提供程序报错的解决办法_实用技巧_
- asp.net上传Excel文件并读取数据的实现方法_实用技巧_
- 详解.NET Core 使用HttpClient SSL请求出错的解决办法_实用技巧_
- .Net Core自定义配置源从配置中心读取配置的方法_实用技巧_
- ASP.NET Core实现单体程序的事件发布/订阅详解_实用技巧_
- DotNetCore深入了解之HttpClientFactory类详解_实用技巧_
- Asp.Net Core2.1前后使用HttpClient的两种方式_实用技巧_
- Asp.Net Core对接钉钉群机器人的完整步骤记录_实用技巧_
- Asp.net mvc在view中用C#代码动态创建元素_实用技巧_
- 如何使用Rotativa在ASP.NET Core MVC中创建PDF详解_实用技巧_
点击排行
本栏推荐
