教你實踐ASP.NET Core Authorization

来源:http://www.cnblogs.com/rohelm/archive/2016/06/16/Authorization.html
-Advertisement-
Play Games

本文目錄 Asp.net Core 對於授權的改動很友好,非常的靈活,本文以MVC為主,當然如果說webapi或者其他的分散式解決方案授權,也容易就可以實現單點登錄都非常的簡單,可以使用現成的IdentityServer框架或者自定義實現動非常方便和乾凈,如果你在運行示例代碼的時候未達到預期效果,請 ...


本文目錄

   Asp.net Core 對於授權的改動很友好,非常的靈活,本文以MVC為主,當然如果說webapi或者其他的分散式解決方案授權,也容易就可以實現單點登錄都非常的簡單,可以使用現成的IdentityServer框架或者自定義實現動非常方便和乾凈,如果你在運行示例代碼的時候未達到預期效果,請把文章拉到結尾尋找答案。

本文示例代碼下載,github我這訪問不了,暫且直接上傳博客園存儲了。

準備

    1. 創建一個名為AuthorizationForoNetCore的(web)解決方案,選擇Empty模板
    2. 添加相關nuget包引用Microsoft.AspNetCore.Mvc(選擇最新版本)
    3. 編輯Startup.cs文件,添加mvcservice併進行預設路由配置
       1 public class Startup
       2     {
       3         public void ConfigureServices(IServiceCollection services)
       4         {
       5             services.AddMvc();
       6         }
       7 
       8         public void Configure(IApplicationBuilder app)
       9         {
      10             app.UseMvc(routes =>
      11             {
      12                 routes.MapRoute(
      13                      name: "default",
      14                      template: "{controller=Home}/{action=Index}/{id?}");
      15             });
      16         }
      17     }

       

    4. 添加Controllers文件夾,添加HomeContrller 

       public class HomeController : Controller
          {
              public IActionResult Index()
              {
                  return View();
              }
          }

       

    5. 創建Views/Home文件夾,並添加Index(Action)對應的Index.cshtml文件

      <!--Index.cshtml-->
      假如生活欺騙了你
      假如生活欺騙了你,
      不要悲傷,不要心急!
      憂郁的日子里須要鎮靜:
      相信吧,快樂的日子將會來臨!   

 使用Authorization

  1. 添加相關nuget包(均使用最新版本)
    1. Microsoft.AspNetCore.Authorization
    2. Microsoft.AspNetCore.Authentication.Cookies
  2. 在ConfigureServices()方法中添加對應服務:  services.AddAuthorization()
  3. Index(Action)方法上添加 [Authorize] 特性,毫無疑問,添加後執行dotnet run 指令後後會返回401的授權碼,那麼接著操作
  4. 編輯Startup.csConfigureapp.UseMvc()方法之前,我們添加一個cookie 中間件,用於持久化請求管道中的身份配置信息
     app.UseCookieAuthentication(new CookieAuthenticationOptions
    {
                   AuthenticationScheme = "MyCookieMiddlewareInstance",
                   LoginPath = new PathString("/Account/Unauthorized/"),
                   AccessDeniedPath = new PathString("/Account/Forbidden/"),
                   AutomaticAuthenticate = true,
                   AutomaticChallenge = true
    });  
  5. tip:相關配置參數細節請參閱:https://docs.asp.net/en/latest/security/authentication/cookie.html

  6. 添加Controllers/Account文件夾,添加 AccountController.cs 控制器文件,實現上述指定的方法,可能這裡你會疑惑,為什麼文檔里不是一個 /Account/Login 這類的,文檔說了別較真,這就是個例子而已,繼續你就明白了。
  7. 添加並實現上述中間件重定向的action 方法如下,你可以看到其實Unauthorized方法模擬實現了登陸的過程。tip:假如你添加Unauthorized視圖,並且沒有該不實現模擬登陸,那麼運行你會直接看到 Unauthorized.cshtml 的內容,這裡我們不需要添加該視圖,僅作說明。
  8. public class AccountController : Controller
    {
         public async Task<IActionResult> Unauthorized(string returnUrl = null)
         {
             List<Claim> claims = new List<Claim>();
             claims.Add(new Claim(ClaimTypes.Name, "halower", ClaimValueTypes.String, "https://www.cnblogs.com/rohelm"));
             var userIdentity = new ClaimsIdentity("管理員");
             userIdentity.AddClaims(claims);
             var userPrincipal = new ClaimsPrincipal(userIdentity);
             await HttpContext.Authentication.SignInAsync("MyCookieMiddlewareInstance", userPrincipal,
                 new AuthenticationProperties
                 {
                     ExpiresUtc = DateTime.UtcNow.AddMinutes(20),
                     IsPersistent = false,
                     AllowRefresh = false
                 });
         
            if (Url.IsLocalUrl(returnUrl))
             {
                 return Redirect(returnUrl);
             }
             else
             {
                 return RedirectToAction("Index", "Home");
             }
         }
         
         public IActionResult Forbidden()
         {
             return View();
         }
    }
  9. 編輯 Home/Index.schtml

    @using System.Security.Claims;
    
    @if (User.Identities.Any(u => u.IsAuthenticated))
    {
    <h1>
    歡迎登陸 @User.Identities.First(u => u.IsAuthenticated).FindFirst(ClaimTypes.Name).Value
    </h1> 
    <h2>所使用的身份驗證的類型:@User.Identity.AuthenticationType</h2> 
    } 
    <article>
    假如生活欺騙了你<br />
    假如生活欺騙了你<br />
    不要悲傷,不要心急<br />
    憂郁的日子里須要鎮靜<br />
    相信吧,快樂的日子將會來臨  
    </article>
  10. 運行代碼你會看到如下結果(程式獲取我們提供的由issuer發佈claims並展示在視圖中,後續會檢查Claims看他們是否匹配)

使用全局授權策略

  1. 去除Home/Index (Action)上的  [Authorize]  特性
  2. 添加 Views/Account/Forbidden.cshtml 頁面,內容為 <h1>拒絕訪問</h1> 
  3. 修改 ConfigureServices 方法中的 services.AddMvc() 使用它的 AddMvc(this IServiceCollection services, Action<MvcOptions> setupAction) 重載
  4. 運行查看結果,你會發現這幾乎成了一個無限的重定向從而造成錯誤,因為每個頁面都需要授權。
  5. 為 AccountController 添加 [AllowAnonymous] 特性,啟動匿名訪問,再次運行項目,查看結果
  6. 結果就是重定向到了 Forbidden.cshtml 頁面

使用角色授權

  1. 在 HomeController 上添加 [Authorize(Roles = "Administrator")] 特性
  2. 在模擬登陸處( Unauthorized方法中 )添加角色說明的身份信息條目:
  3.   claims.Add(new Claim(ClaimTypes.Role, "Administrator", ClaimValueTypes.String, "https://www.cnblogs.com/rohelm"));
  4. 運行項目查看結果

可以使用中你會發現Asp.net Core安全驗證方面較以往的版本最大的改變就是全部採用中間件的方式進行驗證授權,並很好的使用了Policy (策略)這個概念,下那麼繼續~。

基於聲明的授權

  1. 返回Startup.cs,修改 services.AddAuthorization() 方法如下:
    services.AddAuthorization(options =>
    {
        options.AddPolicy("EmployeeOnly", policy => policy.RequireClaim("EmployeeNumber"));
    });
  2. 修改HomeController上的特性,添加 [Authorize(Policy = "EmployeeOnly")] 
  3. 運行項目查看結果,發現被拒絕了
  4. 在模擬登陸處 Unauthorize方法添加:
    claims.Add(new Claim("EmployeeNumber", "123456", ClaimValueTypes.String, "http://www.cnblogs.com/rohelm"));
  5. goto 3.
  6. 多重策略的應用,與之前的版本幾乎一樣,例如本次修改的結果可以為:
     [Authorize(Roles = "Administrator")]
        public class HomeController:Controller 
        {
            [Authorize(Policy = "EmployeeOnly")]
            public IActionResult Index()
            {
                return View();
            }
        } 
  7. 詳情請參閱:https://docs.asp.net/en/latest/security/authorization/claims.html的說明

 自定義授權策略 

自定義授權策略的實現,包括實現一個 IAuthorizationRequirement 的Requirement,和實現 AuthorizationHandler<TRequirement> 的處理器,這裡使用文檔

https://docs.asp.net/en/latest/security/authorization/policies.html中的Code。

  1. 添加 MinimumAgeHandler 處理器實現
    public class MinimumAgeRequirement: AuthorizationHandler<MinimumAgeRequirement>, IAuthorizationRequirement
        {
            int _minimumAge;
    
            public MinimumAgeRequirement(int minimumAge)
            {
                _minimumAge = minimumAge;
            }
    
            protected override void Handle(AuthorizationContext context, MinimumAgeRequirement requirement)
            {
                if (!context.User.HasClaim(c => c.Type == ClaimTypes.DateOfBirth))
                {
                    return;
                }
    
                var dateOfBirth = Convert.ToDateTime(
                    context.User.FindFirst(c => c.Type == ClaimTypes.DateOfBirth).Value);
    
                int calculatedAge = DateTime.Today.Year - dateOfBirth.Year;
                if (dateOfBirth > DateTime.Today.AddYears(-calculatedAge))
                {
                    calculatedAge--;
                }
    
                if (calculatedAge >= _minimumAge)
                {
                    context.Succeed(requirement);
                }
            }
        }
  2. 在 AddAuthorization  中添加一個名為Over21的策略
    options.AddPolicy("Over21", policy => policy.Requirements.Add(new MinimumAgeRequirement(21)));
  3. 在HomeController上應用該策略  [Authorize(Policy = "Over21")] 
  4. 在 Unauthorized 函數中添加對應的聲明信息條目 claims.Add(new Claim(ClaimTypes.DateOfBirth, "1900-01-01", ClaimValueTypes.Date));
  5. 修改時間(例如小於21歲的生日,2000-01-01)並運行調試,查看結果

對一個Requirement應用多個處理器

tip:上面的演示,我們使用了一個同時實現AuthorizationHandler<MinimumAgeRequirement>, IAuthorizationRequirement的MinimumAgeRequirement來做演示,但是如果一個Requirement徐要實現多個處理器就需要分開寫了,原因很簡單,這裡無法實現類的多重繼承。

下麵我們實現一個使用Token登陸的需求

  1.  添加一個LoginRequirement的需求
        public class LoginRequirement: IAuthorizationRequirement
        {
        }
  2. 添加一個使用用戶名密碼登陸的處理器
     public class HasPasswordHandler : AuthorizationHandler<LoginRequirement>
        {
            protected override void Handle(AuthorizationContext context, LoginRequirement requirement)
            {
                if (!context.User.HasClaim(c => c.Type == "UsernameAndPassword" && c.Issuer == "http://www.cnblogs.com/rohelm"))
                    return;
                context.Succeed(requirement);
            }
        }
  3. 在一些場景中我們也會使用發放訪問令牌的方式讓用戶登陸
    public class HasAccessTokenHandler : AuthorizationHandler<LoginRequirement>
        {
            protected override void Handle(AuthorizationContext context, LoginRequirement requirement)
            {
                if (!context.User.HasClaim(c => c.Type == "AccessToken" && c.Issuer == "http://www.cnblogs.com/rohelm"))
                    return;
    
                var toeknExpiryIn = Convert.ToDateTime(context.User.FindFirst(c => c.Type == "AccessToken" && c.Issuer == "http://www.cnblogs.com/rohelm").Value);
    
                if (toeknExpiryIn > DateTime.Now)
                {
                    context.Succeed(requirement);
                }
            }
        }
  4. 在 AddAuthorization  中添加一個名為CanLogin的策略
     options.AddPolicy("CanLogin", policy => policy.Requirements.Add(new LoginRequirement()));
  5. 註冊自定義策略
      services.AddSingleton<IAuthorizationHandler, HasPasswordHandler>();
      services.AddSingleton<IAuthorizationHandler, HasAccessTokenHandler>();
  6. 在Unauthorized 函數中添加對應的聲明信息條目
      claims.Add(new Claim("UsernameAndPassword", "123456", ClaimValueTypes.String, "http://www.cnblogs.com/rohelm"));
    // 測試切換登陸聲明方式
    // claims.Add(new Claim("AccessToken", DateTime.Now.AddMinutes(1).ToString(), ClaimValueTypes.String, "http://www.cnblogs.com/rohelm"));
  7. 在HomeController上應用該策略  [Authorize(Policy = "CanLogin")]  
  8. 運行並查看結果。

基於資源的Requirements

在實際開發者中,除了基於用戶的授權驗證外,通過我們也會遇到針對一些資源的授許可權制,例如有的人可以編輯文檔,有的人只能查看文檔,由此引出該話題

https://docs.asp.net/en/latest/security/authorization/resourcebased.html

  1.  定義一個Document類
    public class Document
        {
            public int Id { get; set; }
            public string Author { get; set; }
        }
  2. 定義Document倉儲介面
    public interface IDocumentRepository
        {
            IEnumerable<Document> Get();
            Document Get(int id);
        }
  3. 模擬實現上述介面
    public class FakeDocumentRepository : IDocumentRepository
        {
            static List<Document> _documents = new List<Document> {
                new Document { Id = 1, Author = "halower" },
                new Document { Id = 2, Author = "others" }
            };
            public IEnumerable<Document> Get()
            {
                return _documents;
            }
    
            public Document Get(int id)
            {
                return _documents.FirstOrDefault(d => d.Id == id);
            }
        }
  4. 註冊介面實現類
    services.AddSingleton<IDocumentRepository, FakeDocumentRepository>();
  5. 創建一個 DocumentController 並修改為如下內容
    public class DocumentController : Controller
        {
            IDocumentRepository _documentRepository;
    
            public DocumentController(IDocumentRepository documentRepository)
            {
                _documentRepository = documentRepository;
            }
    
            public IActionResult Index()
            {
                return View(_documentRepository.Get());
            }
    
            public IActionResult Edit(int id)
            {
                var document = _documentRepository.Get(id);
    
                if (document == null)
                    return new NotFoundResult();
    
                return View(document);
            }
        }
  6. 添加對應 Index.cshtml  視圖文件
    @model IEnumerable<AuthorizationForoNetCore.Modles.Document>
    
    <h1>文檔列表</h1>
    @foreach (var document in Model)
    {
        <p>
            @Html.ActionLink("文檔 #" + document.Id, "編輯", new { id = document.Id })
        </p>
    }
  7. 添加對應的 Edit.cshtml 視圖文件
    @model AuthorizationForoNetCore.Modles.Document
    
    <h1>文檔 #@Model.Id</h1>
    <h2>作者: @Model.Author</h2>
  8. 定義EditRequirement
    public class EditRequirement : IAuthorizationRequirement
     {
     }
  9. 添加對應的編輯文檔處理器
    public class DocumentEditHandler : AuthorizationHandler<EditRequirement, Document>
        {
            protected override void Handle(AuthorizationContext context, EditRequirement requirement, Document resource)
            {
                if (resource.Author == context.User.FindFirst(ClaimTypes.Name).Value)
                {
                    context.Succeed(requirement);
                }
            }
        }
  10. 在 ConfigureServices() 方法中註冊處理器實現
    1 services.AddSingleton<IAuthorizationHandler, DocumentEditHandler>();
  11. 由於對於文檔的授權服務僅僅反正在操作方法的內部,因此我們需要直接註入 IAuthorizationService 對象併在需要的Action內部直接處理
    public class DocumentController : Controller
        {
            IDocumentRepository _documentRepository;
            IAuthorizationService _authorizationService;
    
            public DocumentController(IDocumentRepository documentRepository, IAuthorizationService authorizationService)
            {
                _documentRepository = documentRepository;
                _authorizationService = authorizationService;
            }
    
            public IActionResult Index()
            {
                return View(_documentRepository.Get());
            }
    
            public async Task<IActionResult> Edit(int id)
            {
                var document = _documentRepository.Get(id);
    
                if (document == null)
                    return new NotFoundResult();
    
                if (await _authorizationService.AuthorizeAsync(User, document, new EditRequirement()))
                {
                    return View(document);
                }
                else
                {
                    return new ChallengeResult();
                }
            }
        }
  12. 運行查看結果

在視圖中進行授權

問題來了額,上面示例的視圖中怎麼做限制了,那就繼續了

1.使用  @inject 命令註入 AuthorizationService  

2.應用該上述同樣策略,做簡單修改

@using Microsoft.AspNetCore.Authorization
@model IEnumerable<AuthorizationForoNetCore.Modles.Document>
@inject IAuthorizationService AuthorizationService
@using AuthorizationForoNetCore.Policy
<h1>文檔列表</h1>
@{
    var requirement = new EditRequirement();
    foreach (var document in Model)
    {
        if (await AuthorizationService.AuthorizeAsync(User, document, requirement)) {
    <p>
        @Html.ActionLink("文檔 #" + document.Id, "編輯", new { id = document.Id })
    </p>
    }
}
}

 請在運行時清理Cookie,或者在試驗時直接暫時禁用

之前寫的一個插件,誰有時間幫升級支持下asp.net Core:https://github.com/halower/JqGridForMvc


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • WinForm中DataGridView功能強大,除了可以自動綁定數據源外,還可以根據需求編輯列。下麵以截圖說明添加編輯列的步驟(HoverTreeSCJ 項目實際界面)。1.選擇DataGridView控制項,會在右上角出現黑色的小三角形,點擊出現快捷菜單:2.點擊“編輯”,就可以在彈出視窗添,刪除 ...
  • 各個系統之間進行數據交互是重要的部分,WebService是一個平臺獨立的,低耦合的,自包含的、基於可編程的web的應用程式,使用較為方便,閑話少說,正式進入到關於在.Net中調用java開發的介面。 目前JAVA開發WebService的方式很很多種:Axis2、CXF、xfire等等,但是在.N ...
  • 對於d3的BarChart,其數據源為ItemsSource,支持bingding。 <d3:BarChart ItemsSource="{Binding SampleDatas}"> <d3:TemplateMarkerGenerator> <DataTemplate> <Rectangle St ...
  • 上面是生成驗證碼以及驗證碼圖片 下麵是怎麼使用: ...
  • 各位廣大的園友,我想寫博客很久了,但是遲遲沒能走出第一步。今天我終於準備好了我的博客之路,希望以後能夠吧我工作共遇到的困難和乾貨和大家分享。 本篇博客主要是記錄我是如何開發一個Outlook插件的,個人寫博客剛剛開始,很多地方可能表述不是很妥當的,請各位園友多多體諒。 廢話不多說了,開乾。Outlo ...
  • 介面數據處理模塊是什麼意思呢?實際上很簡單,就是使用面向介面的思想和方式來做數據處理。 還提到EntityFramework和Dapper,EntityFramework和Dapper是.net環境下推崇最高的兩種ORM工具。 1、EntityFramework是出自微軟根正苗紅的.net下的ORM ...
  • FileShare Enumeration 包含用於控制其他IsolatedStorageFileStream對象對同一文件可以具有的訪問類型的常數。此枚舉有一個FlagsAttribute屬性,該屬性使其成員值按位組合。(命名空間:System.IO) 語法 [FlagsAttribute] [C... ...
  • 第一種速度最快,第二種次之,第三種最慢 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...