本文的目的是介绍c#–如何从authrizationhandler.net核心获取params的详细情况,特别关注c#获取cpu的相关信息。我们将通过专业的研究、有关数据的分析等多种方式,为您呈现一个
本文的目的是介绍c# – 如何从authrizationhandler .net核心获取params的详细情况,特别关注c#获取cpu的相关信息。我们将通过专业的研究、有关数据的分析等多种方式,为您呈现一个全面的了解c# – 如何从authrizationhandler .net核心获取params的机会,同时也不会遗漏关于ASP.NET Core Authentication and Authorization、asp.net Core 中AuthorizationHandler 实现自定义授权、ASP.NET Web API Claims Authorization with ASP.NET Identity 2.1 Part 5 (by TAISEER)、asp.net – Owin Middleware vs ExceptionHandler vs HttpMessageHandler(DelegatingHandler)的知识。
本文目录一览:- c# – 如何从authrizationhandler .net核心获取params(c#获取cpu)
- ASP.NET Core Authentication and Authorization
- asp.net Core 中AuthorizationHandler 实现自定义授权
- ASP.NET Web API Claims Authorization with ASP.NET Identity 2.1 Part 5 (by TAISEER)
- asp.net – Owin Middleware vs ExceptionHandler vs HttpMessageHandler(DelegatingHandler)
c# – 如何从authrizationhandler .net核心获取params(c#获取cpu)
在旧的.net中,我可以像这样从Httpcontext请求参数中获取参数
var eventId = filterContext.RequestContext.HttpContext.Request.Params["id"];
我不知道如何在.net核心中实现它
enter code here public class HasAdminRoleFromAnySiteRequirement : AuthorizationHandler<HasAdminRoleFromAnySiteRequirement>,IAuthorizationRequirement { public HasAdminRoleFromAnySiteRequirement() { } protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,HasAdminRoleFromAnySiteRequirement requirement) { //need to call get param from controller to used in the validation // something like this //var eventId = filterContext.RequestContext.HttpContext.Request.Params["id"]; // I tried the suggestion below but I can't get the parameter from routedata // var mvcContext = context.Resource as Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext; return Task.Fromresult(0); } }
解决方法
var mvcContext = context.Resource as Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext; if (mvcContext != null) { // examine MVC specific things like routing data. }
如果需要参数值,则在绑定发生之前运行授权属性块.相反,你会转移到控制器内的强制调用.这基本上是resource based authorization,你的参数是一个资源.
您可以将授权服务注入您的控制器;
public class DocumentController : Controller { IAuthorizationService _authorizationService; public DocumentController(IAuthorizationService authorizationService) { _authorizationService = authorizationService; } }
然后稍微改写你的处理程序;
public class DocumentAuthorizationHandler : AuthorizationHandler<MyRequirement,Document> { public override Task HandleRequirementAsync(AuthorizationHandlerContext context,MyRequirement requirement,Document resource) { // Validate the requirement against the resource and identity. return Task.CompletedTask; } }
您可以看到此处理程序获取文档,这可以是您喜欢的任何内容,无论是ID的整数,还是某种类型的视图模型.
然后,您可以在HandleRequirementAsync()方法中访问它.
最后,一旦绑定发生,你就可以从你的控制器中调用它;
if (await authorizationService.AuthorizeAsync( User,document,yourRequirement)) { }
ASP.NET Core Authentication and Authorization
最近把一个Asp .net core 2.0的项目迁移到Asp .net core 3.1,项目启动的时候直接报错:
InvalidOperationException: Endpoint CoreAuthorization.Controllers.HomeController.Index (CoreAuthorization) contains authorization metadata, but a middleware was not found that supports authorization.
Configure your application startup by adding app.UseAuthorization() inside the call to Configure(..) in the application startup code. The call to app.UseAuthorization() must appear between app.UseRouting() and app.UseEndpoints(...).
Microsoft.AspNetCore.Routing.EndpointMiddleware.ThrowMissingAuthMiddlewareException(Endpoint endpoint)
看意思是缺少了一个authorization的中间件,这个项目在Asp.net core 2.0上是没问题的。
startup是这样注册的:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options =>
{
options.LoginPath = "/account/Login";
});
services.AddControllersWithViews();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
//app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
}
查了文档后发现3.0的示例代码多了一个UseAuthorization,改成这样就可以了:
app.UseRouting();
app.UseAuthentication();
//use授权中间件
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
看来Asp .net Core 3.1的认证跟授权又不太一样了,只能继续看文档学习了。
UseAuthentication and UseAuthorization
先说一下Authentication跟Authorization的区别。这两个单词长的十分相似,而且还经常一起出现,很多时候容易搞混了。
- Authentication是认证,明确是你谁,确认是不是合法用户。常用的认证方式有用户名密码认证。
- Authorization是授权,明确你是否有某个权限。当用户需要使用某个功能的时候,系统需要校验用户是否需要这个功能的权限。
所以这两个单词是不同的概念,不同层次的东西。UseAuthorization在asp.net core 2.0中是没有的。在3.0之后微软明确的把授权功能提取到了Authorization中间件里,所以我们需要在UseAuthentication之后再次UseAuthorization。否则,当你使用授权功能比如使用[Authorize]属性的时候系统就会报错。
Authentication(认证)
认证的方案有很多,最常用的就是用户名密码认证,下面演示下基于用户名密码的认证。新建一个MVC项目,添加AccountController:
[HttpPost]
public async Task<IActionResult> Login(
[FromForm]string userName, [FromForm]string password
)
{
//validate username password
...
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, userName),
new Claim(ClaimTypes.Role, "老师")
};
var claimsIdentity = new ClaimsIdentity(
claims, CookieAuthenticationDefaults.AuthenticationScheme);
await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity));
return Redirect("/");
}
public async Task<IActionResult> Logoff()
{
await HttpContext.SignOutAsync();
return Redirect("Login");
}
public IActionResult AccessDenied()
{
return Content("AccessDenied");
}
修改login.cshtml
@{
ViewData["Title"] = "Login Page";
}
<h1>
Login Page
</h1>
<form method="post">
<p>
用户名: <input name="userName" value="administrator" />
</p>
<p>
密码: <input name="password" value="123" />
</p>
<p>
<button>登录</button>
</p>
</form>
从前台传入用户名密码后进行用户名密码校验(示例代码省略了密码校验)。如果合法,则把用户的基本信息存到一个claim list里,并且指定cookie-base的认证存储方案。最后调用SignInAsync把认证信息写到cookie中。根据cookie的特性,接来下所有的http请求都会携带cookie,所以系统可以对接来下用户发起的所有请求进行认证校验。Claim有很多翻译,个人觉得叫“声明”比较好。一单认证成功,用户的认证信息里就会携带一串Claim,其实就是用户的一些信息,你可以存任何你觉得跟用户相关的东西,比如用户名,角色等,当然是常用的信息,不常用的信息建议在需要的时候查库。调用HttpContext.SignOutAsync()方法清除用户登认证信息。
Claims信息我们可以方便的获取到:
@{
ViewData["Title"] = "Home Page";
}
<h2>
CoreAuthorization
</h2>
<p>
@Context.User.FindFirst(System.Security.Claims.ClaimTypes.Name)?.Value
</p>
<p>
角色:
@foreach (var claims in Context.User.Claims.Where(c => c.Type == System.Security.Claims.ClaimTypes.Role))
{
<span> @claims.Value </span>
}
</p>
<p>
<a href="/Student/index">/Student/index</a>
</p>
<p>
<a href="/Teacher/index">/Teacher/Index</a>
</p>
<p>
<a href="/Teacher/Edit">/Student/Edit</a>
</p>
<p>
<a href="/Account/Logoff">退出</a>
</p>
改一下home/Index页面的html,把这些claim信息展示出来。
以上就是一个基于用户名密码以及cookie的认证方案。
Authorization(授权)
有了认证我们还需要授权。刚才我们实现了用户名密码登录认证,但是系统还是没有任何管控,用户可以随意查库任意页面。现实中的系统往往都是某些页面可以随意查看,有些页面则需要认证授权后才可以访问。
AuthorizeAttribute
当我们希望一个页面只有认证后才可以访问,我们可以在相应的Controller或者Action上打上AuthorizeAttribute这个属性。修改HomeController:
[Authorize]
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
}
重新启动网站,如果没有登录,访问home/index的时候网站会跳转到/account/AccessDenied。如果登录后则可以正常访问。AuthorizeAttribute默认授权校验其实是把认证跟授权合为一体了,只要认证过,就认为有授权,这是也是最最简单的授权模式。
基于角色的授权策略
显然上面默认的授权并不能满足我们开发系统的需要。AuthorizeAttribute还内置了基于Role(角色)的授权策略。
登录的时候给认证信息加上角色的声明:
[HttpPost]
public async Task<IActionResult> Login(
[FromForm]string userName,
[FromForm]string password
)
{
//validate username password
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, userName),
new Claim(ClaimTypes.Role, "老师"),
};
var claimsIdentity = new ClaimsIdentity(
claims, CookieAuthenticationDefaults.AuthenticationScheme);
await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity));
return Redirect("/");
}
新建一个TeacherController:
[Authorize(Roles = "老师")]
public class TeacherController : Controller
{
public IActionResult Index()
{
return Content("Teacher index");
}
}
给AuthorizeAttribute的属性设置Roles=老师,表示只有老师角色的用户才可以访问。如果某个功能可以给多个角色访问那么可以给Roles设置多个角色,使用逗号进行分割。
[Authorize(Roles = "老师,校长")]
public class TeacherController : Controller
{
public IActionResult Index()
{
return Content("Teacher index");
}
}
这样认证的用户只要具有老师或者校长其中一个角色就可以访问。
基于策略的授权
上面介绍了内置的基于角色的授权策略。如果现实中需要更复杂的授权方案,我们还可以自定义策略来支持。比如我们下面定义一个策略:编辑功能只能姓王的老师可以访问。
定义一个要求:
public class LastNamRequirement : IAuthorizationRequirement
{
public string LastName { get; set; }
}
IAuthorizationRequirement其实是一个空接口,仅仅用来标记,继承这个接口就是一个要求。这是空接口,所以要求的定义比较宽松,想怎么定义都可以,一般都是根据具体的需求设置一些属性。比如上面的需求,本质上是根据老师的姓来决定是否授权通过,所以把姓作为一个属性暴露出去,以便可以配置不同的姓。
除了要求,我们还需要实现一个AuthorizationHandler:
public class LastNameHandler : AuthorizationHandler<IAuthorizationRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, IAuthorizationRequirement requirement)
{
var lastNameRequirement = requirement as LastNamRequirement;
if (lastNameRequirement == null)
{
return Task.CompletedTask;
}
var isTeacher = context.User.HasClaim((c) =>
{
return c.Type == System.Security.Claims.ClaimTypes.Role && c.Value == "老师";
});
var isWang = context.User.HasClaim((c) =>
{
return c.Type == "LastName" && c.Value == lastNameRequirement.LastName;
});
if (isTeacher && isWang)
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
AuthorizationHandler是一个抽象类,继承它后需要重写其中的HandleRequirementAsync方法。这里才是真正判断是否授权成功的地方。要求(Requirement)跟用户的声明(Claim)信息会被传到这方法里,然后我们根据这些信息进行判断,如果符合授权就调用context.Succeed方法。这里注意如果不符合请谨慎调用context.Failed方法,因为策略之间一般是OR的关系,这个策略不通过,可能有其他策略通过。
在ConfigureServices方法中添加策略跟注册AuthorizationHandler到DI容器中:
services.AddSingleton<IAuthorizationHandler, LastNameHandler>();
services.AddAuthorization(options =>
{
options.AddPolicy("王老师", policy =>
policy.AddRequirements(new LastNamRequirement { LastName = "王" })
);
});
使用AddSingleton生命周期来注册LastNameHandler,这个生命周期并不一定要单例,看情况而定。在AddAuthorization中添加一个策略叫"王老师"。这里有个个人认为比较怪的地方,为什么AuthorizationHandler不是在AddAuthorization方法中配置?而是仅仅注册到容器中就可以开始工作了。如果有一个需求,仅仅是需要自己调用一下自定义的AuthorizationHandler,而并不想它真正参与授权。这样的话就不能使用DI的方式来获取实例了,因为一注册进去就会参与授权的校验了。
在TeacherController下添加一个 Edit Action:
[Authorize(Policy="王老师")]
public IActionResult Edit()
{
return Content("Edit success");
}
给AuthorizeAttribute的Policy设置为“王老师”。
修改Login方法添加一个姓的声明:
[HttpPost]
public async Task<IActionResult> Login(
[FromForm]string userName,
[FromForm]string password
)
{
//validate username password
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, userName),
new Claim(ClaimTypes.Role, "老师"),
new Claim("LastName", "王"),
};
var claimsIdentity = new ClaimsIdentity(
claims, CookieAuthenticationDefaults.AuthenticationScheme);
await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity));
return Redirect("/");
}
运行一下程序,访问一下/teacher/edit,可以看到访问成功了。如果修改Login方法,修改LastName的声明为其他值,则访问会拒绝。
使用泛型Func方法配置策略
如果你的策略比较简单,其实还有个更简单的方法来配置,就是在AddAuthorization方法内直接使用一个Func来配置策略。 使用Func来配置一个女老师的策略:
options.AddPolicy("女老师", policy =>
policy.RequireAssertion((context) =>
{
var isTeacher = context.User.HasClaim((c) =>
{
return c.Type == System.Security.Claims.ClaimTypes.Role && c.Value == "老师";
});
var isFemale = context.User.HasClaim((c) =>
{
return c.Type == "Sex" && c.Value == "女";
});
return isTeacher && isFemale;
}
)
);
总结
- Authentication跟Authorization是两个不同的概念。Authentication是指认证,认证用户的身份;Authorization是授权,判断是否有某个功能的权限。
- Authorization内置了基于角色的授权策略。
- 可以使用自定义AuthorizationHandler跟Func的方式来实现自定义策略。
吐槽
关于认证跟授权微软为我们考虑了很多很多,包括identityserver,基本上能想到的都有了,什么oauth,openid,jwt等等。其实本人是不太喜欢用的。虽然微软都给你写好了,考虑很周到,但是学习跟Trouble shooting都是要成本的。其实使用中间件、过滤器再配合redis等组件,很容易自己实现一套授权认证方案,自由度也更高,有问题修起来也更快。自己实现一下也可以更深入的了解某项的技术,比如jwt是如果工作的,oauth是如何工作的,这样其实更有意义。
原文出处:https://www.cnblogs.com/kklldog/p/auth-in-aspnetcore.html
asp.net Core 中AuthorizationHandler 实现自定义授权
原文: asp.net Core 中AuthorizationHandler 实现自定义授权
前言
ASP.NET Core 中 继承的是AuthorizationHandler ,而ASP.NET Framework 中继承的是AuthorizeAttribute.
它们都是用过重写里面的方法实现过滤请求的。
现在我们实现如何在 ASP.NET Core MVC 实现自定义授权。
关于AuthorizationHandler 详细介绍可以看这里
https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-2.2#authorization-handlers
如何自定义授权
比如我们后台有个博客管理功能,那我们可以新建一个Blog的控制器,比如BlogController
里面有添加,删除,编辑等功能,分别是Add,Delete,Edit
代码如下
public class BlogController : Controller
{
public IActionResult Index()
{
return View();
}
/// <summary>
/// 博客添加页面
/// </summary>
/// <returns></returns>
public IActionResult Add()
{
return View();
}
/// <summary>
/// 博客列表页面
/// </summary>
public IActionResult List()
{
return View();
}
/// <summary>
/// 博客编辑页面
/// </summary>
public IActionResult Edit()
{
return View();
}
}
如果有打印可以起个名字叫 public IActionResult Print()
自定义就是做个控制界面做勾选功能,用户根据自身业务选择。
以此类推,在ASP.NET 框架下默认路由就是Controller和Action,除非你修改默认路由,当然了你修改默认路由你的权限逻辑也得变。
实现过滤器
AuthorizationHandler 参数里面有个IAuthorizationRequirement要我们去填充,根据我们自己业务自己选择定义数据。
public class PermissionRequirement : IAuthorizationRequirement
{
/// <summary>
/// 无权限action
/// </summary>
public string DeniedAction { get; set; } = "/Home/visitDeny";
/// <summary>
/// 认证授权类型
/// </summary>
public string ClaimType { internal get; set; }
/// <summary>
/// 默认登录页面
/// </summary>
public string LoginPath { get; set; } = "/Home/Login";
/// <summary>
/// 过期时间
/// </summary>
public TimeSpan Expiration { get; set; }
/// <summary>
/// 构造
/// </summary>
/// <param name="deniedAction"></param>
/// <param name="claimType"></param>
/// <param name="expiration"></param>
public PermissionRequirement(string deniedAction, string claimType, TimeSpan expiration)
{
ClaimType = claimType;
DeniedAction = deniedAction;
Expiration = expiration;
}
}
第一个参数集合
public class PermissionItem
{
/// <summary>
/// 用户或角色或其他凭据名称
/// </summary>
public virtual string Role { get; set; }
/// <summary>
/// 配置的Controller名称
/// </summary>
public virtual string controllerName { get; set; }
/// <summary>
/// 配置的Action名称
/// </summary>
public virtual string actionName { get; set; }
}
Startup 里面,添加一个授权策略,PermissionRequirement 放进去,然后注入
////权限要求参数
var permissionRequirement = new PermissionRequirement(
"/Home/visitDeny",// 拒绝授权的跳转地址
ClaimTypes.Name,//基于用户名的授权
expiration: TimeSpan.FromSeconds(60 * 5)//接口的过期时间
);
#endregion
//【授权】
services.AddAuthorization(options =>
{
options.AddPolicy("Permission", policy => policy.Requirements.Add(permissionRequirement));
});
// 注入权限处理器
services.AddTransient<IAuthorizationHandler, PermissionHandler>();
控制器里面加上标示
[Authorize("Permission")]
public class BlogController : Controller
{
}
登录页面授权
[HttpPost]
public async Task<IActionResult> Login(LoginViewModel model)
{
if (ModelState.IsValid)
{
if (model.textUser == null)
{
ModelState.AddModelError("", "请输入账号.");
return View(model);
}
if (model.textPassword == null)
{
ModelState.AddModelError("", "请输入密码.");
return View(model);
}
if (model.textUser == "admin" && model.textPassword == "123")
{
#region 传统的登录
//只判断是否登录 通过[Authorize] 小项目中只有一个管理员 只要账号和密码对就行
var claimIdentity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
claimIdentity.AddClaim(new Claim(ClaimTypes.Name, model.textUser));
var claimsPrincipal = new ClaimsPrincipal(claimIdentity);
//await HttpContext.SignInAsync(claimsPrincipal);
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, claimsPrincipal);
#endregion
//下面代码是演示的,实际项目要从根据用户名或者角色从数据库读取出来 配置到 List<PermissionItem>里面
//这里我用的是用户名判断的,根据自己的业务自己处理
//测试的时候 可以 删除一条记录试试,或者添加一条
List<PermissionItem> lsperm = new List<PermissionItem>();
lsperm.Add(new PermissionItem() { Role = model.textUser, controllerName = "Blog", actionName = "Add" });//添加博客页面的权限
lsperm.Add(new PermissionItem() { Role = model.textUser, controllerName = "Blog", actionName = "Edit" });//编辑博客页面的权限
lsperm.Add(new PermissionItem() { Role = model.textUser, controllerName = "Blog", actionName = "List" });//查看博客页面的权限
string perData = JsonConvert.SerializeObject(lsperm);
await _cacheService.SetStringAsync("perm" + model.textUser, perData);
return RedirectToAction("Index", "Home");
}
}
return View(model);
}
List<PermissionItem> 我用Redis存储的,大家根据实际情况存储。
权限判断
public class PermissionHandler : AuthorizationHandler<PermissionRequirement>
{
public IAuthenticationSchemeProvider Schemes;
readonly IDistributedCache _cacheService;
/// <summary>
/// 构造函数注入
/// </summary>
public PermissionHandler(IAuthenticationSchemeProvider schemes, IDistributedCache cacheService)
{
Schemes = schemes;
_cacheService = cacheService;
}
// 重载异步处理程序
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
{
//从AuthorizationHandlerContext转成HttpContext,以便取出表求信息
AuthorizationFilterContext filterContext = context.Resource as AuthorizationFilterContext;
HttpContext httpContext = filterContext.HttpContext;
AuthenticateResult result = await httpContext.AuthenticateAsync(Schemes.GetDefaultAuthenticateSchemeAsync().Result.Name);
//如果没登录result.Succeeded为false
if (result.Succeeded)
{
httpContext.User = result.Principal;
//当前访问的Controller
string controllerName = filterContext.RouteData.Values["Controller"].ToString();//通过ActionContext类的RouteData属性获取Controller的名称:Home
//当前访问的Action
string actionName = filterContext.RouteData.Values["Action"].ToString();//通过ActionContext类的RouteData属性获取Action的名称:Index
string name = httpContext.User.Claims.SingleOrDefault(s => s.Type == ClaimTypes.Name)?.Value;
string perData = await _cacheService.GetStringAsync("perm" + name);
List<PermissionItem> lst = JsonConvert.DeserializeObject<List<PermissionItem>>(perData);
if (lst.Where(w => w.controllerName == controllerName && w.actionName == actionName).Count() > 0)
{
//如果在配置的权限表里正常走
context.Succeed(requirement);
}
else
{
//不在权限配置表里 做错误提示
//如果是AJAX请求 (包含了VUE等 的ajax)
string requestType = filterContext.HttpContext.Request.Headers["X-Requested-With"];
if (!string.IsNullOrEmpty(requestType) && requestType.Equals("XMLHttpRequest", StringComparison.CurrentCultureIgnoreCase))
{
//ajax 的错误返回
//filterContext.Result = new StatusCodeResult(499); //自定义错误号 ajax请求错误 可以用来错没有权限判断 也可以不写 用默认的
context.Fail();
}
else
{
//普通页面错误提示 就是跳转一个页面
//httpContext.Response.Redirect("/Home/visitDeny");//第一种方式跳转
filterContext.Result = new RedirectToActionResult("visitDeny", "Home", null);//第二种方式跳转
context.Fail();
}
}
}
else
{
context.Fail();
}
}
}
至此我们实现定义授权判断。实际业务上每个人可以根据自己的情况做处理。
ASP.NET Web API Claims Authorization with ASP.NET Identity 2.1 Part 5 (by TAISEER)
https://www.cnblogs.com/KimmyLee/p/6430474.html
https://www.cnblogs.com/rocketRobin/p/9077523.html
http://bitoftech.net/2015/03/31/asp-net-web-api-claims-authorization-with-asp-net-identity-2-1/
ASP.NET Web API Claims Authorization with ASP.NET Identity 2.1
In the previous post we have implemented a finer grained way to control authorization based on the Roles assigned for the authenticated user, this was done by assigning users to a predefined Roles in our system and then attributing the protected controllers or actions by the [Authorize(Roles = “Role(s) Name”)]attribute.
Using Roles Based Authorization for controlling user access will be efficient in scenarios where your Roles do not change too much and the users permissions do not change frequently.
In some applications controlling user access on system resources is more complicated, and having users assigned to certain Roles is not enough for managing user access efficiently, you need more dynamic way to to control access based on certain information related to the authenticated user, this will lead us to control user access using Claims, or in another word using Claims Based Authorization.
But before we dig into the implementation of Claims Based Authorization we need to understand what Claims are!
Note: It is not mandatory to use Claims for controlling user access, if you are happy with Roles Based Authorization and you have limited number of Roles then you can stick to this.
What is a Claim?
Claim is a statement about the user makes about itself, it can be user name, first name, last name, gender, phone, the roles user assigned to, etc… Yes the Roles we have been looking at are transformed to Claims at the end, and as we saw in the previous post; in ASP.NET Identity those Roles have their own manager (ApplicationRoleManager) and set of APIs to manage them, yet you can consider them as a Claim of type Role.
As we saw before, any authenticated user will receive a JSON Web Token (JWT) which contains a set of claims inside it, what we’ll do now is to create a helper end point which returns the claims encoded in the JWT for an authenticated user.
To do this we will create a new controller named “ClaimsController” which will contain a single method responsible to unpack the claims in the JWT and return them, to do this add new controller named “ClaimsController” under folder Controllers and paste the code below:
[RoutePrefix("api/claims")]
public class ClaimsController : BaseApiController
{
[Authorize]
[Route("")]
public IHttpActionResult GetClaims()
{
var identity = User.Identity as ClaimsIdentity;
var claims = from c in identity.Claims
select new
{
subject = c.Subject.Name,
type = c.Type,
value = c.Value
};
return Ok(claims);
}
}
The code we have implemented above is straight forward, we are getting the Identity of the authenticated user by calling “User.Identity” which returns “ClaimsIdentity” object, then we are iterating over the IEnumerable Claims property and return three properties which they are (Subject, Type, and Value).
To execute this endpoint we need to issue HTTP GET request to the end point “http://localhost/api/claims” and do not forget to pass a valid JWT in the Authorization header, the response for this end point will contain the below JSON object:
[
{"subject": "Hamza","type": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier","value": "cd93945e-fe2c-49c1-b2bb-138a2dd52928"},{"subject": "Hamza","type": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name","value": "Hamza"},{"subject": "Hamza","type": "http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider","value": "ASP.NET Identity"},{"subject": "Hamza","type": "AspNet.Identity.SecurityStamp","value": "a77594e2-ffa0-41bd-a048-7398c01c8948"},{"subject": "Hamza","type": "iss","value": "http://localhost:59822"},{"subject": "Hamza","type": "aud","value": "414e1927a3884f68abc79f7283837fd1"},{"subject": "Hamza","type": "exp","value": "1427744352"},{"subject": "Hamza","type": "nbf","value": "1427657952"}]
As you noticed from the response above, all the claims contain three properties, and those properties represents the below:
- Subject: Represents the identity which those claims belongs to, usually the value for the subject will contain the unique identifier for the user in the system (Username or Email).
- Type: Represents the type of the information contained in the claim.
- Value: Represents the claim value (information) about this claim.
Now to have better understanding of what type of those claims mean let’s take a look the table below:
SUBJECT | TYPE | VALUE | NOTES |
---|---|---|---|
Hamza | nameidentifier | cd93945e-fe2c-49c1-b2bb-138a2dd52928 | Unique User Id generated from Identity System |
Hamza | name | Hamza | Unique Username |
Hamza | identityprovider | ASP.NET Identity | How user has been authenticated using ASP.NET Identity |
Hamza | SecurityStamp | a77594e2-ffa0-41bd-a048-7398c01c8948 | Unique Id which stays the same until any security related attribute change, i.e. change user password |
Hamza | iss | http://localhost:59822 | Issuer of the Access Token (Authz Server) |
Hamza | aud | 414e1927a3884f68abc79f7283837fd1 | For which system this token is generated |
Hamza | exp | 1427744352 | Expiry time for this access token (Epoch) |
Hamza | nbf | 1427657952 | When this token is issued (Epoch) |
After we have briefly described what claims are, we want to see how we can use them to manage user assess, in this post I will demonstrate three ways of using the claims as the below:
- Assigning claims to the user on the fly based on user information.
- Creating custom Claims Authorization attribute.
- Managing user claims by using the “ApplicationUserManager” APIs.
Method 1: Assigning claims to the user on the fly
Method 2: Creating custom Claims Authorization attribute
Method 3: Managing user claims by using the “ApplicationUserManager” APIs
The last method we want to explore here is to use the “ApplicationUserManager” claims related API to manage user claims and store them in ASP.NET Identity related tables “AspNetUserClaims”.
In the previous two methods we’ve created claims for the user on the fly, but in method 3 we will see how we can add/remove claims for a certain user.
The “ApplicationUserManager” class comes with a set of predefined APIs which makes dealing and managing claims simple, the APIs that we’ll use in this post are listed in the table below:
METHOD NAME | USAGE |
---|---|
AddClaimAsync(id, claim) | Create a new claim for specified user id |
RemoveClaimAsync(id, claim) | Remove claim from specified user if claim type and value match |
GetClaimsAsync(id) | Return IEnumerable of claims based on specified user id |
To use those APIs let’s add 2 new methods to the “AccountsController”, the first method “AssignClaimsToUser” will be responsible to add new claims for specified user, and the second method “RemoveClaimsFromUser” will remove claims from a specified user as the code below:
[Authorize(Roles = "Admin")]
[Route("user/{id:guid}/assignclaims")]
[HttpPut]
public async Task<IHttpActionResult> AssignClaimsToUser([FromUri] string id, [FromBody] List<ClaimBindingModel> claimsToAssign) {
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var appUser = await this.AppUserManager.FindByIdAsync(id);
if (appUser == null)
{
return NotFound();
}
foreach (ClaimBindingModel claimModel in claimsToAssign)
{
if (appUser.Claims.Any(c => c.ClaimType == claimModel.Type)) {
await this.AppUserManager.RemoveClaimAsync(id, ExtendedClaimsProvider.CreateClaim(claimModel.Type, claimModel.Value));
}
await this.AppUserManager.AddClaimAsync(id, ExtendedClaimsProvider.CreateClaim(claimModel.Type, claimModel.Value));
}
return Ok();
}
[Authorize(Roles = "Admin")]
[Route("user/{id:guid}/removeclaims")]
[HttpPut]
public async Task<IHttpActionResult> RemoveClaimsFromUser([FromUri] string id, [FromBody] List<ClaimBindingModel> claimsToRemove)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var appUser = await this.AppUserManager.FindByIdAsync(id);
if (appUser == null)
{
return NotFound();
}
foreach (ClaimBindingModel claimModel in claimsToRemove)
{
if (appUser.Claims.Any(c => c.ClaimType == claimModel.Type))
{
await this.AppUserManager.RemoveClaimAsync(id, ExtendedClaimsProvider.CreateClaim(claimModel.Type, claimModel.Value));
}
}
return Ok();
}
The implementation for both methods is very identical, as you noticed we are only allowing users in “Admin” role to access those endpoints, then we are specifying the UserId and a list of the claims that will be add or removed for this user.
Then we are making sure that user specified exists in our system before trying to do any operation on the user.
In case we are adding a new claim for the user, we will check if the user has the same claim type before trying to add it, add if it exists before we’ll remove this claim and add it again with the new claim value.
The same applies when we try to remove a claim from the user, notice that methods “AddClaimAsync” and “RemoveClaimAsync” will save the claims permanently in our SQL data-store in table “AspNetUserClaims”.
Do not forget to add the “ClaimBindingModel” under folder “Models” which acts as our POCO class when we are sending the claims from our front-end application, the class will contain the code below:
public class ClaimBindingModel
{
[Required]
[Display(Name = "Claim Type")]
public string Type { get; set; }
[Required]
[Display(Name = "Claim Value")]
public string Value { get; set; }
}
There is no extra steps needed in order to pull those claims from the SQL data-store when establishing the user identity, thanks for the method “CreateIdentityAsync” which is responsible to pull all the claims for the user. We have already implemented this and it can be checked by visiting the highlighted LOC.
To test those methods all you need to do is to issue HTTP PUT request to the URI: “http://localhost:59822/api/accounts/user/{UserId}/assignclaims” and “http://localhost:59822/api/accounts/user/{UserId}/removeclaims” as the request images below:
That’s it for now folks about implementing Authorization using Claims.
asp.net – Owin Middleware vs ExceptionHandler vs HttpMessageHandler(DelegatingHandler)
> Owin中间件
> HttpMessageHandler(或DelegatingHandler)
> ExceptionHandler
我想要做的是开发和提供一个不变格式的json数据的web api,意味着如果实际的数据是
{"Id":1,"UserName":"abc","Email":"abc@xyz.com"}
然后我喜欢提供json
{__d:{"Id":1,"Email":"abc@xyz.com"},code:200,somekey: "somevalue"}
为此,我尝试使用自定义ActionFilterattribute,但我觉得(仍然不能确认),如果代码遇到异常,这样做不能提供类似的格式数据
请建议我最好的方向.
这是我的简短的自定义属性代码片段.还建议我是自定义属性是好的目的
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class,Inherited = true,AllowMultiple = false)] public class ResponsenormalizationAttribute : ActionFilterattribute { public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext) { base.OnActionExecuted(actionExecutedContext); var response = actionExecutedContext.Response; object contentValue; if (response.TryGetContentValue(out contentValue)) { var nval = new { data=contentValue,status = 200 }; var newResponse = new HttpResponseMessage { Content = new ObjectContent(nval.GetType(),nval,new JsonMediaTypeFormatter()) }; newResponse.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); actionContext.Response = newResponse; } } }
解决方法
编写一个继承自DelegatingHandler的自定义处理程序:
public class ApiResponseHandler : DelegatingHandler { protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,CancellationToken cancellationToken) { var response = await base.SendAsync(request,cancellationToken); return buildresponse(request,response); } private static HttpResponseMessage buildresponse(HttpRequestMessage request,HttpResponseMessage response) { object content; string errorMessage = null; if (response.TryGetContentValue(out content) && !response.IsSuccessstatusCode) { HttpError error = content as HttpError; if (error != null) { content = null; errorMessage = error.Message; } } var newResponse = request.CreateResponse(response.StatusCode,new ApiResponse((int)response.StatusCode,content,errorMessage)); foreach (var header in response.Headers) { newResponse.Headers.Add(header.Key,header.Value); } return newResponse; } } //ApiResponse is your constant json response public class ApiResponse { public ApiResponse(int statusCode,object content,string errorMsg) { Code = statusCode; Content = content; Error = errorMsg; Id = Guid.NewGuid().ToString(); } public string Error { get; set; } //your actual data is mapped to the Content property public object Content { get; set; } public int Code { get; private set; } public string Id { get; set; } }
在WebApiConfig.cs中注册处理程序:
public static void Register(HttpConfiguration config) { // Web API routes config.MapHttpAttributeRoutes(); ... config.MessageHandlers.Add(new ApiResponseHandler()); config.Routes.MapHttpRoute( name: "DefaultApi",routeTemplate: "{controller}/{id}",defaults: new { id = RouteParameter.Optional } ); ... }
我在SO中发布了一个类似的答案,但这是在.NET Core中,并被实现为一个OWIN中间件(因为DelegatingHandler在.NET Core中). How can I wrap Web API responses(in .net core) for consistency?
今天关于c# – 如何从authrizationhandler .net核心获取params和c#获取cpu的分享就到这里,希望大家有所收获,若想了解更多关于ASP.NET Core Authentication and Authorization、asp.net Core 中AuthorizationHandler 实现自定义授权、ASP.NET Web API Claims Authorization with ASP.NET Identity 2.1 Part 5 (by TAISEER)、asp.net – Owin Middleware vs ExceptionHandler vs HttpMessageHandler(DelegatingHandler)等相关知识,可以在本站进行查询。
本文标签: