asp.net core 一種基於token 和 Permission 的許可權管理中間件示例

来源:https://www.cnblogs.com/NewBigLiang/archive/2023/05/20/17416825.html
-Advertisement-
Play Games

關於JWT,可以說是分散式系統下的一個利器,我在我的很多項目實踐中,認證系統的第一選擇都是JWT。它的優勢會讓你欲罷不能,就像你領優惠券一樣。 ...


1. 先上封裝後的使用效果

        [Permission(Key = "/User/AddUser")]
        [HttpPost]
        public Result AddUser([FromBody] SaUser user)
        {
            //Do sth.
            throw new NotImplementedException();
        }
     [Authorize]
        [HttpPost]
        public Result<UserInfoDto> GetUserInfo()
        {
             //Do sth.
        }   
 

說明:要求登錄即可,不要求特定許可權的,可以使用【Authroize】 attribute 標記,

  要求 特定許可權 如   "/User/AddUser" 的 ,使用 【Permission】特性標記,使用Key指定需要的許可權。 沒有登錄的返回401, 沒有許可權的返回403.

 

2. 實現。主要類及介面說明:

    LoginUser : 登錄用戶,包含用戶基礎信息,許可權等。可以繼承此類封裝更多信息。

namespace WebUtils
{
    public class LoginUser
    {
        public string EnterpriseId { get; set; }
        public string UserName { get; set;} 

        public string Token { get; set; }

        public DateTime LoginTime { get; set;}
        /// <summary>
        /// 可用許可權
        /// </summary>
        public HashSet<string> Permissions { get; set;}
    }
}

 

    ITokenHelper <TUser>: 管理用戶登錄後的token,並根據token 獲取登錄用戶信息。TUser 是LoginUser 的子類。 

namespace WebUtils
{
    public interface  ITokenHelper<TUser>  where TUser :LoginUser 
    {
        public void AddToken(string token, TUser user);
        public void RemoveToken(string token);
        public TUser GetLoginUser (string token);
    }
}

 

    TokenHelper 是 ITokenHelper<LoginUser> 的預設實現,LoginUser 和Token 存記憶體中,進程重啟會丟失。實際應用可以封裝自己的實現,把信息持久化到資料庫或者Redis 中。

namespace WebUtils
{
    public class TokenHelper : ITokenHelper<LoginUser>
    {
        
        private Dictionary<string, LoginUser> UserDic = new Dictionary<string, LoginUser>();
        
        public void AddToken(string token, LoginUser au)
        { 
            UserDic.Add(token, au);
        }
         

        public LoginUser GetLoginUser(string token)
        {
            if (UserDic.ContainsKey(token))
            {
                return UserDic[token];
            }
            return null;
        }

        public void RemoveToken(string token)
        {
            if (UserDic.ContainsKey(token))
            {
                UserDic.Remove(token);
            }
        }
    }
}
View Code

 

    PermissionAuthenticationHandler:檢查請求是否攜帶token,並檢查TokenHelper 中是否包含此token.

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
using System;
using System.Net;
using System.Security.Claims;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using WebUtils;

namespace WebUtils
{
    public class PermissionAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
    {
        private ITokenHelper<LoginUser> _tokenHelper;
        public PermissionAuthenticationHandler(ITokenHelper<LoginUser> tokenHelper, IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
            : base(options, logger, encoder, clock)
        {
            this._tokenHelper = tokenHelper;
        }
        public static string CustomerSchemeName = "Permission";
        protected override Task<AuthenticateResult> HandleAuthenticateAsync()
        {
            AuthenticateResult result;
            Context.Request.Headers.TryGetValue("Authorization", out StringValues values);
            string token = values.ToString();
            if (!string.IsNullOrWhiteSpace(token))
            {
                var loginInfo = _tokenHelper.GetLoginUser(token);

                if (loginInfo == null)
                    result = AuthenticateResult.Fail("未登陸");
                else
                {
                    var claimsIdentity = new ClaimsIdentity(new Claim[]
                        {
                                new Claim(ClaimTypes.Name, loginInfo.UserName),
                                new Claim(ClaimHelper.EnterpriseId,loginInfo.EnterpriseId),
                                new Claim(ClaimHelper.Token, loginInfo.Token)
                        }, CustomerSchemeName);
                    var principal = new ClaimsPrincipal(claimsIdentity);


                    AuthenticationTicket ticket = new AuthenticationTicket(principal, Scheme.Name);

                    result = AuthenticateResult.Success(ticket);
                }
            }
            else
            {
                result = AuthenticateResult.Fail("未登陸");
            }
            return Task.FromResult(result);
        }
    }
}
View Code

 

    PermissionAttribute: 繼承自 Attribute,IFilterFactory ,返回真正的IAuthorizationFilter實例。

    DonotUsePermissionFilterAttribute 繼承自 Attribute, IAuthorizationFilter 檢查是否擁有指定的許可權。

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WebUtils
{
    [AttributeUsage(AttributeTargets.Class|AttributeTargets.Method)]
    public class PermissionAttribute : Attribute,IFilterFactory
    {
        public string Key { get; set; }

        public bool IsReusable => false;

        public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
        {
            var instance= serviceProvider.GetService<DonotUsePermissionFilterAttribute>();
            instance.Key = this.Key;
            return instance;
        }
    }

    /// <summary>
    /// 防止用戶直接調用,起名DonotUse, 
    /// </summary>
    public class DonotUsePermissionFilterAttribute : Attribute, IAuthorizationFilter
    {
        private ITokenHelper<LoginUser> _tokenHelper;
        public DonotUsePermissionFilterAttribute(ITokenHelper<LoginUser> tokenHelper)
        {
            this._tokenHelper = tokenHelper;
        }
        public string Key { get; set; }
 
        public void OnAuthorization(AuthorizationFilterContext context)
        {
            var token = context.HttpContext.User?.GetValue(ClaimHelper.Token);
            if (token == null)
            {
                context.Result = new ObjectResult("用戶未登錄") { StatusCode = 401 };
                return;
            }
            var user = _tokenHelper.GetLoginUser(token);
            if (user == null)
            {
                context.Result = new ObjectResult("用戶token 已失效") { StatusCode = 401 };
                return;
            }
            if (!user.Permissions.Contains(Key))
            {
                context.Result = new ObjectResult("鑒權失敗,請聯繫管理員授權!") { StatusCode = 403 };
                return;
            }
        }
    }
}
View Code

 

    

    PermissionMiddleWare 把相關實例和PermissionAuthenticationHandler添加到Service 中。

using Microsoft.Extensions.DependencyInjection;

namespace WebUtils
{
    public static class PermissionMiddleWare
    {
        /// <summary>
        /// 基於token和permission 的許可權認證中間件
        /// </summary>
        /// <param name="services"></param>
        /// <param name="TokenHelperType"></param>
        /// <returns></returns>
        public static IServiceCollection AddPermission(this IServiceCollection services,Type TokenHelperType)
        {
            services.AddSingleton(typeof(ITokenHelper<LoginUser>), TokenHelperType);

            services.AddTransient(typeof(PermissionAttribute));
            services.AddTransient(typeof(DonotUsePermissionFilterAttribute));
            services.AddAuthentication(o =>
            {
                o.DefaultAuthenticateScheme = PermissionAuthenticationHandler.CustomerSchemeName;
                o.DefaultChallengeScheme = PermissionAuthenticationHandler.CustomerSchemeName;
                o.AddScheme<PermissionAuthenticationHandler>(PermissionAuthenticationHandler.CustomerSchemeName, PermissionAuthenticationHandler.CustomerSchemeName);
            });


            return services;
        }
    }
}
View Code

 3. 在program.cs 中調用

  在原來添加AddAuthorization 的地方換成下麵這句

  builder.Services.AddPermission(typeof(TokenHelper));

  別忘了後面use

            app.UseAuthentication();
            app.UseAuthorization();

 

簡單就是美。 如果我的文章幫到了你,請告訴我,讓我知道。
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 目錄 一、DNS概念 二、功能變數名稱格式類型 三、查詢類型 四、解析類型 五、配置DNS 六、dns解析實驗 1.配置正向解析 2.反向解析 3.主從解析 一、DNS概念 概念:功能變數名稱和IP地址的相互映射的分散式資料庫,可以更好的訪問互聯網。 電腦只能訪問IP地址,但是IP地址不是方便記住,採用功能變數名稱解析出 ...
  • 一、環境準備 1、虛擬機:ubuntu18.04 64位 2、交叉編譯工具包:gcc-linaro-7.5.0-2019.12-i686_arm-linux-gnueabihf.tar 下載鏈接:https://releases.linaro.org/components/toolchain/bin ...
  • > 本文首發於公眾號:Hunter後端 > 原文鏈接:[es筆記三之term,match,match_phrase 等查詢方法介紹](https://mp.weixin.qq.com/s/3tzD8dEr592WNJFH_1bKRw) 首先介紹一下在 es 里有兩種存儲字元串的欄位類型,一個是 ke ...
  • From Java To Kotlin, 空安全、擴展、函數、Lambda 概述(Summarize) * • Kotlin 是什麼? * • 可以做什麼? * • Android 官方開發語言從Java變為Kotlin,Java 有哪些問題? * • Kotlin的優點 * • Kotlin 特性 ...
  • 一、XSS攻擊 全稱跨站腳本攻擊,簡稱XSS攻擊,攻擊者通過在目標網站上HTML註入篡改網頁來插入惡意腳本, 當用戶在瀏覽網頁時獲取用戶的cookie等敏感信息,進一步做一些其他危害的操作。 根據攻擊的來源,該攻擊還可以分為: 1)存儲型攻擊:一般是在有評論功能的網站將惡意代碼當作評論內容存儲到服務 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 一、導出靜態數據 1、安裝 vue-json-excel npm i vue-json-excel 註意,此插件對node有版本要求,安裝失敗檢查一下報錯是否由於node版本造成! 2、引入並註冊組件(以全局為例) import Vue ...
  • **本文為千鋒資深前端教學老師帶來的【JavaScript全解析】系列,文章內含豐富的代碼案例及配圖,從0到1講解JavaScript相關知識點,致力於教會每一個人學會JS!** **文末有本文重點總結,可以收藏慢慢看\~ 更多技術類內容,主頁關註一波!** # ES6函數中參數的預設值 給函數的形 ...
  • 一.業務描述 最近在負責公司一個語音的微服務模塊優化,這個模塊主要的業務是:1.天貓精靈、小度、若琪、小京魚、小愛同學、思必馳這些第三方音響對我們的用戶進行oauth2/JWT授權; 2.這些第三方音響服務調用我們的設備發現介面對公司的設備信息在第三方平臺進行一個存儲;3.第三方平臺對用戶發出的語音 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...