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
  • 1、預覽地址:http://139.155.137.144:9012 2、qq群:801913255 一、前言 隨著網路的發展,企業對於信息系統數據的保密工作愈發重視,不同身份、角色對於數據的訪問許可權都應該大相徑庭。 列如 1、不同登錄人員對一個數據列表的可見度是不一樣的,如數據列、數據行、數據按鈕 ...
  • 前言 上一篇文章寫瞭如何使用RabbitMQ做個簡單的發送郵件項目,然後評論也是比較多,也是準備去學習一下如何確保RabbitMQ的消息可靠性,但是由於時間原因,先來說說設計模式中的簡單工廠模式吧! 在瞭解簡單工廠模式之前,我們要知道C#是一款面向對象的高級程式語言。它有3大特性,封裝、繼承、多態。 ...
  • Nodify學習 一:介紹與使用 - 可樂_加冰 - 博客園 (cnblogs.com) Nodify學習 二:添加節點 - 可樂_加冰 - 博客園 (cnblogs.com) 介紹 Nodify是一個WPF基於節點的編輯器控制項,其中包含一系列節點、連接和連接器組件,旨在簡化構建基於節點的工具的過程 ...
  • 創建一個webapi項目做測試使用。 創建新控制器,搭建一個基礎框架,包括獲取當天日期、wiki的請求地址等 創建一個Http請求幫助類以及方法,用於獲取指定URL的信息 使用http請求訪問指定url,先運行一下,看看返回的內容。內容如圖右邊所示,實際上是一個Json數據。我們主要解析 大事記 部 ...
  • 最近在不少自媒體上看到有關.NET與C#的資訊與評價,感覺大家對.NET與C#還是不太瞭解,尤其是對2016年6月發佈的跨平臺.NET Core 1.0,更是知之甚少。在考慮一番之後,還是決定寫點東西總結一下,也回顧一下.NET的發展歷史。 首先,你沒看錯,.NET是跨平臺的,可以在Windows、 ...
  • Nodify學習 一:介紹與使用 - 可樂_加冰 - 博客園 (cnblogs.com) Nodify學習 二:添加節點 - 可樂_加冰 - 博客園 (cnblogs.com) 添加節點(nodes) 通過上一篇我們已經創建好了編輯器實例現在我們為編輯器添加一個節點 添加model和viewmode ...
  • 前言 資料庫併發,數據審計和軟刪除一直是數據持久化方面的經典問題。早些時候,這些工作需要手寫複雜的SQL或者通過存儲過程和觸發器實現。手寫複雜SQL對軟體可維護性構成了相當大的挑戰,隨著SQL字數的變多,用到的嵌套和複雜語法增加,可讀性和可維護性的難度是幾何級暴漲。因此如何在實現功能的同時控制這些S ...
  • 類型檢查和轉換:當你需要檢查對象是否為特定類型,並且希望在同一時間內將其轉換為那個類型時,模式匹配提供了一種更簡潔的方式來完成這一任務,避免了使用傳統的as和is操作符後還需要進行額外的null檢查。 複雜條件邏輯:在處理複雜的條件邏輯時,特別是涉及到多個條件和類型的情況下,使用模式匹配可以使代碼更 ...
  • 在日常開發中,我們經常需要和文件打交道,特別是桌面開發,有時候就會需要載入大批量的文件,而且可能還會存在部分文件缺失的情況,那麼如何才能快速的判斷文件是否存在呢?如果處理不當的,且文件數量比較多的時候,可能會造成卡頓等情況,進而影響程式的使用體驗。今天就以一個簡單的小例子,簡述兩種不同的判斷文件是否... ...
  • 前言 資料庫併發,數據審計和軟刪除一直是數據持久化方面的經典問題。早些時候,這些工作需要手寫複雜的SQL或者通過存儲過程和觸發器實現。手寫複雜SQL對軟體可維護性構成了相當大的挑戰,隨著SQL字數的變多,用到的嵌套和複雜語法增加,可讀性和可維護性的難度是幾何級暴漲。因此如何在實現功能的同時控制這些S ...