1.前言 回顧:認證授權方案之JwtBearer認證 在上一篇中,我們通過JwtBearer的方式認證,瞭解在認證時,都是基於Claim的,因此我們可以通過用戶令牌獲取到用戶的Claims,在授權過程中對這些Claims進行驗證,從而來判斷是否具有獲取或執行目標資源操作的許可權。本章就來介紹一下 AS ...
1.前言
在上一篇中,我們通過JwtBearer的方式認證,瞭解在認證時,都是基於Claim的,因此我們可以通過用戶令牌獲取到用戶的Claims,在授權過程中對這些Claims進行驗證,從而來判斷是否具有獲取或執行目標資源操作的許可權。本章就來介紹一下 ASP.NET Core 的授權系統的簡單使用。
2.說明
授權與身份認證是相互獨立,但是,授權卻需要一種身份驗證機制,因此,身份驗證可以為當前用戶創建一個或多個標識,是確定用戶真實身份的過程。而授權是根據標識確定用戶可執行的操作的過程,其本質就是具有某種特性的用戶會有許可權訪問某個資源或者執行某個操作。例如:一個擁有管理員身份的用戶有創建人員、刪除人員、編輯人員和刪除人員的操作許可權,而一個非管理身份的用戶僅有讀取自己信息的許可權。
這時候,你可能會問,究竟怎樣特性的用戶可以被授權訪問某個資源或執行某個操作。由此我們引出了授權策略的方式,可以根據用戶擁有的角色,也可以根據用戶的職位,部門甚至是性別,年齡等等特性進行授權。
通過建立授權策略方式,檢驗認證的用戶所攜帶的身份聲明(ClaimsPrincipal對象)與授權策略是否一致,從而確定用戶可否執行操作。
3.授權
3.1. 基於角色
3.1.1 添加角色
將角色賦予某個控制器或控制器內的操作,指定當前用戶必須是其角色才能訪問請求資源。
可以使用Authorize
屬性的Roles特性指定所請求資源的角色。
例如:
- 分配了“admin”角色用戶進行訪問操作
[Authorize(Roles ="admin")]
public class WeatherForecastController : ControllerBase
{
}
- 以逗號分隔角色名來允行多個角色訪問操作
[Authorize(Roles ="admin,user")]
public class WeatherForecastController : ControllerBase
{
}
其中只要滿足admmin
或者user
其一就可以進行訪問。
- 同時滿足指定的多個角色進行的訪問操作
[Authorize(Roles = "admin")]
[Authorize(Roles = "user")]
public class WeatherForecastController : ControllerBase
{
}
3.1.2 添加策略的角色
可以創建策略的方式進行訪問控制,在配置授權服務中添加註冊授權服務策略。
在Startup.cs文件中,通過ConfigureServices()
配置服務,創建一個允許具有admin
角色的用戶才能進行訪問的策略
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
//添加授權角色策略
services.AddAuthorization(options =>
{
options.AddPolicy("BaseRole", options => options.RequireRole("admin"));
});
//或者指定多個允許的角色
//services.AddAuthorization(options =>
// {
// options.AddPolicy("MoreBaseRole", options => options.RequireRole("admin","user"));
// });
}
在控制器方法使用特性Policy
的屬性進行策略應用
[Authorize(Policy = "BaseRole")]
public class WeatherForecastController : ControllerBase
{
}
3.2. 基於聲明
3.2.1添加聲明
對當前用戶必須擁有的聲明,並將聲明賦予某個控制器或控制器內的操作,因此,指定聲明必須持有對應的值才能訪問請求資源。
聲明要求基於策略,所以必須進行構建一個表示聲明要求的策略,才能進行授權。
最簡單的類型聲明是將判斷聲明是否存在,而不檢查值。
可以創建策略的方式進行訪問控制,在配置授權服務中添加註冊授權服務策略。
在Startup.cs文件中,通過ConfigureServices()
配置服務,創建一個允許具有聲明的用戶才能進行訪問的策略
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
//添加基於聲明的授權
services.AddAuthorization(options =>
{
options.AddPolicy("BaseClaims", options => options.RequireClaim("name"));
});
}
BaseClaims
聲明策略會檢查name
當前標識是否存在聲明。
在控制器方法使用特性Policy
的屬性進行策略應用
[Authorize(Policy = "BaseClaims")]
public class WeatherForecastController : ControllerBase
{
}
但是,大多時候,我們需要聲明包含值,只有指定允許值的列表,才能授權成功。所以,可以添加指定值。
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
//添加基於聲明的授權,指定允許值列表。
services.AddAuthorization(options =>
{
options.AddPolicy("BaseClaims", options => options.RequireClaim("name","i3yuan"));
});
}
3.3 基於策略
上面介紹的基於角色和基於聲明的授權,都使用了要求、要求處理程式和預配置的策略。這些在構建上提供了便捷,但是最終都是生成授權策略。ASP.NET Core,設計了另一種靈活的授權方式,一種更豐富的可重覆使用的授權結構,基於策略的授權,同時這也是授權的核心。
這節會先講一下授權策略的應用,在下一節中,會對授權策略的核心進行一步步的詳解。
在上面我們簡單的介紹了基於策略的角色授權,但是這種方式無非基於角色或者聲明多一些。
因此,這裡我們基於自定義策略授權的方式,實現授權。
自定義授權,就要我們自己寫策略提供器,自己根據不同的參數來生成不同的策略,重新實現策略的方式。策略要求由以下兩種元素組成:僅保留數據的要求類,以及對用戶驗證數據的授權處理程式。創建自定義要求,還可以進一步表達特定策略。
3.3.1. 定義許可權策略PermissionRequirement
定義一個許可權策略,這個策略並包含一些屬性。
public class PermissionRequirement: IAuthorizationRequirement
{
public string _permissionName { get; }
public PermissionRequirement(string PermissionName)
{
_permissionName = PermissionName;
}
}
3.3.2. 再定義一個策略處理類
public class PermissionRequirementHandler : AuthorizationHandler<PermissionRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
{
var role = context.User.FindFirst(c => c.Type == ClaimTypes.Role);
if (role != null)
{
var roleValue = role.Value;
if (roleValue==requirement._permissionName)
{
context.Succeed(requirement);
}
}
return Task.CompletedTask;
授權處理程式讀取與角色用戶關聯的聲明,並檢查自定義的角色,如果角色匹則成功,否則無法返回成功。
這裡的自定義聲明是寫固定了,但是也可以通過資料庫或外部服務的方式進行運行查詢獲取用戶相關角色信息相對應的判斷條件,從而在處理程式中進行判斷處理。
授權處理程式調用方法 Succeed
,同時傳遞當前要求,以通知此要求已成功得到驗證。如果沒有傳遞要求,處理程式無需執行任何操作,可以直接返回內容。不過,如果處理程式要確定是否不符合要求(無論其他處理程式是否已成功驗證同一要求),將會對授權上下文對象調用方法 Fail
。
3.3.3. 下麵展示瞭如何將自定義要求添加到策略
(請註意,由於這是自定義要求,因此沒有擴展方法,而必須繼續處理策略對象的整個 Requirements
集合):
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
//基於自定義策略授權
services.AddAuthorization(options =>
{
options.AddPolicy("customizePermisson",
policy => policy
.Requirements
.Add(new PermissionRequirement("admin")));
});
//此外,還需要在 IAuthorizationHandler 類型的範圍內向 DI 系統註冊新的處理程式:
services.AddScoped<IAuthorizationHandler, PermissionRequirementHandler>();
// 如前所述,要求可包含多個處理程式。如果為授權層的同一要求向 DI 系統註冊多個處理程式,有一個成功就足夠了。
}
3.3.4. 應用自定義的策略的特性
指定當前用戶必須是應用對控制器或控制器內的操作,如
[Authorize(Policy = "customizePermisson")]
public class WeatherForecastController : ControllerBase
{
}
4.場景
在上一篇認證授權方案之JwtBearer認證中,我們已經實現了獲取token的方式,這一次,我們實現一個以基於角色場景為例的認證授權。
在原來生成token的方式中,添加多一個聲明角色的Claim,如下:
new Claim(JwtClaimTypes.Role,"admin")
[HttpGet]
public IActionResult GetToken()
{
try
{
//定義發行人issuer
string iss = "JWTBearer.Auth";
//定義受眾人audience
string aud = "api.auth";
//定義許多種的聲明Claim,信息存儲部分,Claims的實體一般包含用戶和一些元數據
IEnumerable<Claim> claims = new Claim[]
{
new Claim(JwtClaimTypes.Id,"1"),
new Claim(JwtClaimTypes.Name,"i3yuan"),
new Claim(JwtClaimTypes.Role,"admin"),
};
//notBefore 生效時間
// long nbf =new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds();
var nbf = DateTime.UtcNow;
//expires //過期時間
// long Exp = new DateTimeOffset(DateTime.Now.AddSeconds(1000)).ToUnixTimeSeconds();
var Exp = DateTime.UtcNow.AddSeconds(1000);
//signingCredentials 簽名憑證
string sign = "q2xiARx$4x3TKqBJ"; //SecurityKey 的長度必須 大於等於 16個字元
var secret = Encoding.UTF8.GetBytes(sign);
var key = new SymmetricSecurityKey(secret);
var signcreds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
//String issuer = default(String), String audience = default(String), IEnumerable<Claim> claims = null, Nullable<DateTime> notBefore = default(Nullable<DateTime>), Nullable<DateTime> expires = default(Nullable<DateTime>), SigningCredentials signingCredentials = null
var jwt = new JwtSecurityToken(issuer: iss, audience: aud, claims:claims,notBefore:nbf,expires:Exp, signingCredentials: signcreds);
var JwtHander = new JwtSecurityTokenHandler();
var token = JwtHander.WriteToken(jwt);
return Ok(new
{
access_token = token,
token_type = "Bearer",
});
}
catch (Exception ex)
{
throw;
}
}
對控制器或控制器內的操作,指定當前用戶必須是其角色才能訪問請求資源,如WeatherForecastController.cs
[ApiController]
[Route("[controller]")]
[Authorize(Roles ="admin")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
}
}
5.運行
5.1. 獲取token
分別獲取role為admin和role為user的情況下頒發的token,只有在角色為admin的情況下才能授權通過。
5.2. 授權資源介面訪問
在role為admin的情況下
在role為user的情況下
由上可知,只有在角色為admin的情況下,才能訪問目標資源進行操作。
6.總結
- 從上一篇的認證到這一篇的授權階段,簡單的介紹了Asp.net Core的認證授權系統,對授權有了初步的認識以及使用,對授權進行劃分為兩種,一種是基於角色的授權,但隨著角色的增加會對處理授權產生限制,不適合表達複雜的授權邏輯。另一種是基於策略的身份驗證,策略包含一系列基於聲明的要求,以及基於可從
HTTP
上下文或外部源註入的其他任何信息的自定義邏輯。這些要求各自與一個或多個處理程式相關聯,這些處理程式負責要求的實際計算。 - 可以發現,asp.net core提供的授權策略是一個非常強大豐富且靈活的認證授權方案,能夠滿足大部分的授權場景。
- 如果有不對的或不理解的地方,希望大家可以多多指正,提出問題,一起討論,不斷學習,共同進步。
- 因此,在後續的篇章中,會繼續探索授權系統,對授權策略的核心進行一步步的詳解。
- 本示例源碼地址
參考文獻文檔