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
  • 在一些複雜的業務表中間查詢數據,有時候操作會比較複雜一些,不過基於SqlSugar的相關操作,處理的代碼會比較簡單一些,以前我在隨筆《基於SqlSugar的開發框架循序漸進介紹(2)-- 基於中間表的查詢處理》介紹過基於主表和中間表的聯合查詢,而往往實際會比這個會複雜一些。本篇隨筆介紹聯合多個表進行... ...
  • 從按鈕、文本框到下拉框、列表框,WPF提供了一系列常用控制項,每個控制項都有自己獨特的特性和用途。通過靈活的佈局容器,如網格、堆棧面板和換行面板,我們可以將這些控制項組合在一起,實現複雜的界面佈局。而通過樣式和模板,我們可以輕鬆地定製控制項的外觀和行為,以符合我們的設計需求。本篇記錄WPF入門需要瞭解的樣式... ...
  • 以MySQL資料庫為例 # 一. 安裝 NuGet搜索Dapper.Lite並安裝最新版本。 ![](https://img2023.cnblogs.com/blog/174862/202306/174862-20230602155913303-757935399.jpg) NuGet搜索MySql ...
  • # 圖片介面JWT鑒權實現 # 前言 之前做了個返回圖片鏈接的介面,然後沒做授權,然後今天鍵盤到了,也是用JWT來做介面的許可權控制。 然後JTW網上已經有很多文章來說怎麼用了,這裡就不做多的解釋了,如果不懂的可以參考下列鏈接的 文章。 圖片介面文章:[還在愁個人博客沒有圖片放?](https://w ...
  • ![線程各屬性縱覽](https://img2023.cnblogs.com/blog/1220983/202306/1220983-20230603114109107-477345835.png) 如上圖所示,線程有四個屬性: - 線程ID - 線程名稱 - 守護線程 - 線程優先順序 ### 1. ...
  • 本次主要介紹golang中的標準庫`bytes`,基本上參考了 [位元組 | bytes](https://cloud.tencent.com/developer/section/1140520) 、[Golang標準庫——bytes](https://www.jianshu.com/p/e6f7f2 ...
  • 歡迎來到本篇文章!通過上一篇什麼是 Spring?為什麼學它?的學習,我們知道了 Spring 的基本概念,知道什麼是 Spring,以及為什麼學習 Spring。今天,這篇就來說說 Spring 中的核心概念之一 IoC。 ...
  • # 2022版本IDEA+Maven+Tomcat的第一個程式(傻瓜教學) ​ 作為學習Javaweb的一個重要環節,如何實現在IDEA中利用Maven工具創建一個Javaweb程式模版並連接Tomcat發佈是非常重要的。我比較愚鈍(小白),而且自身電腦先前運行過spring或maven的程式,系統 ...
  • 本篇專門扯一下有關 QCheckBox 組件的一個問題。老周不水字數,直接上程式,你看了就明白。 #include <QApplication> #include <QWidget> #include <QPushButton> #include <QCheckBox> #include <QVBo ...
  • # 1.列表數據元素排序 在創建的列表中,數據元素的排列順序常常是無法預測的。這雖然在大多數情況下都是不可避免的,但經常需要以特定的順序呈現信息。有時候希望保留列表數據元素最初的排列順序,而有時候又需要調整排列順序。python提供了很多列表數據元素排序的方式,可根據情況選用。 ## 1.永久性排序 ...