教你實踐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
更多相關文章
  • http://pan.baidu.com/s/1pLByvUF ...
  • 在使用第三方類庫時,使用cocoaPods是非常方便的,具體使用方法可以參考:CocoaPods安裝和使用教程 的安裝使用方法。今天討論的問題是,我在使用的時候遇到了一些問題:用cocoaPod sinstall完成所需要的第三方類庫之後,在要用的地方導入(#import)發現不提示,雖然自己手寫之 ...
  • 在Linux中要修改一個文件夾或文件的許可權我們需要用到linux chmod命令來做. 語法如下: 命令中各選項的含義為 操作符號可以是: 設置mode所表示的許可權可用下述字母的任意組合: X 只有目標文件對某些用戶是可執行的或該目標文件是目錄時才追加x 屬性。 實例 修改文件可讀寫屬性的方法 例如 ...
  • 一. 準備工作 下載nsis相關工具包,點擊此下載 1. 安裝程式:nsis-2.46-setup.exe 2. 編輯程式:cnisedit203.exe 3. 幫助文檔:NSIS205幫助文檔.rar 4. 第三方庫:ExecCmd.dll、AddPath.nsh、WriteEnvStr.nsh ...
  • 我們知道,現在能調試.net程式通常有兩個,第一個是ILSpy,還是一個是Reflector,這兩個小反編譯軟體算是我們研究底層代碼中所擁有的一把 鋒利小尖刀~~~,比如你看到的ILSpy這樣的界面圖: 但是呢!!! 用過ILSpy的同學大概都知道,這個毛軟體是調試不了web代碼的。。。也只能調試調 ...
  • 一直以來苦苦尋求適合自己的ORM,之前也用過Entity Framework、ormlite、nhibernate、dapper,都感覺準備步驟繁瑣,除非公司提前已經搭建起來一套成熟的框架,那樣只能做下添肉的工作,短時間內不能獨立搭建。--廢話終止 直接上圖: 就這麼簡單。 ...
  • 方法、函數、過程、靜態變數、類、結構體、構造函數、析構函數、運算符重載、索引器、類型轉換重載、嵌套類型 ...
  • 類 定義新的數據類型以及這些新的數據類型進行相互操作的方法 定義方式: C#中所有的類都是預設由object類派生來的,顯示指定或者省略效果是一樣的,所以上面的兩個例子是完全相同的。 C#中類包括:抽象類、密封類、非抽象類 abstract:表示修飾的類不完整,也就是抽象類,只能用做基類。 在使用是 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...