امن سازی برنامههای ASP.NET Core توسط IdentityServer 4x - قسمت هشتم- تعریف سطوح دسترسی پیچیده
نویسنده: وحید نصیری
تاریخ: ۱۳۹۷/۰۶/۱۸ ۱۵:۲۵
آدرس: www.dntips.ir
| مطالب | ۳۶۹۴ |
| نویسندگان | ۲۷۶ |
| گروههای مطالب | ۱۰۲۴ |
| نقشههای راه | ۱۱۹ |
| دورهها | ۱۴ |
| اشتراکها | ۱۷۹۱۴ |
namespace DNT.IDP
{
public static class Config
{
public static List<TestUser> GetUsers()
{
return new List<TestUser>
{
new TestUser
{
Username = "User 1",
// ...
Claims = new List<Claim>
{
// ...
new Claim("subscriptionlevel", "PayingUser"),
new Claim("country", "ir")
}
},
new TestUser
{
Username = "User 2",
// ...
Claims = new List<Claim>
{
// ...
new Claim("subscriptionlevel", "FreeUser"),
new Claim("country", "be")
}
}
};
} namespace DNT.IDP
{
public static class Config
{
// identity-related resources (scopes)
public static IEnumerable<IdentityResource> GetIdentityResources()
{
return new List<IdentityResource>
{
// ...
new IdentityResource(
name: "country",
displayName: "The country you're living in",
claimTypes: new List<string> { "country" }),
new IdentityResource(
name: "subscriptionlevel",
displayName: "Your subscription level",
claimTypes: new List<string> { "subscriptionlevel" })
};
} namespace DNT.IDP
{
public static class Config
{
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientName = "Image Gallery",
// ...
AllowedScopes =
{
// ...
"country",
"subscriptionlevel"
}
// ...
}
};
}
} options.Scope.Add("subscriptionlevel");
options.Scope.Add("country"); options.ClaimActions.MapUniqueJsonKey(claimType: "role", jsonKey: "role"); options.ClaimActions.MapUniqueJsonKey(claimType: "subscriptionlevel", jsonKey: "subscriptionlevel"); options.ClaimActions.MapUniqueJsonKey(claimType: "country", jsonKey: "country");
namespace ImageGallery.MvcClient.WebApp
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthorization(options =>
{
options.AddPolicy(
name: "CanOrderFrame",
configurePolicy: policyBuilder =>
{
policyBuilder.RequireAuthenticatedUser();
policyBuilder.RequireClaim(claimType: "country", requiredValues: "ir");
policyBuilder.RequireClaim(claimType: "subscriptionlevel", requiredValues: "PayingUser");
});
}); @if(User.IsInRole("PayingUser"))
{
<li><a asp-area="" asp-controller="Gallery" asp-action="AddImage">Add an image</a></li>
<li><a asp-area="" asp-controller="Gallery" asp-action="OrderFrame">Order a framed picture</a></li>
} @using Microsoft.AspNetCore.Authorization @inject IAuthorizationService AuthorizationService
@if (User.IsInRole("PayingUser"))
{
<li><a asp-area="" asp-controller="Gallery" asp-action="AddImage">Add an image</a></li>
}
@if ((await AuthorizationService.AuthorizeAsync(User, "CanOrderFrame")).Succeeded)
{
<li><a asp-area="" asp-controller="Gallery" asp-action="OrderFrame">Order a framed picture</a></li>
} namespace ImageGallery.MvcClient.WebApp.Controllers
{
[Authorize]
public class GalleryController : Controller
{
[Authorize(Policy = "CanOrderFrame")]
public async Task<IActionResult> OrderFrame()
{ using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;
namespace ImageGallery.WebApi.Services
{
public class MustOwnImageRequirement : IAuthorizationRequirement
{
}
public class MustOwnImageHandler : AuthorizationHandler<MustOwnImageRequirement>
{
private readonly IImagesService _imagesService;
private readonly ILogger<MustOwnImageHandler> _logger;
public MustOwnImageHandler(
IImagesService imagesService,
ILogger<MustOwnImageHandler> logger)
{
_imagesService = imagesService;
_logger = logger;
}
protected override async Task HandleRequirementAsync(
AuthorizationHandlerContext context, MustOwnImageRequirement requirement)
{
var filterContext = context.Resource as AuthorizationFilterContext;
if (filterContext == null)
{
context.Fail();
return;
}
var imageId = filterContext.RouteData.Values["id"].ToString();
if (!Guid.TryParse(imageId, out Guid imageIdAsGuid))
{
_logger.LogError($"`{imageId}` is not a Guid.");
context.Fail();
return;
}
var subClaim = context.User.Claims.FirstOrDefault(c => c.Type == "sub");
if (subClaim == null)
{
_logger.LogError($"User.Claims don't have the `sub` claim.");
context.Fail();
return;
}
var ownerId = subClaim.Value;
if (!await _imagesService.IsImageOwnerAsync(imageIdAsGuid, ownerId))
{
_logger.LogError($"`{ownerId}` is not the owner of `{imageIdAsGuid}` image.");
context.Fail();
return;
}
// all checks out
context.Succeed(requirement);
}
}
} <Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="2.1.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Abstractions" Version="2.1.1.0" />
</ItemGroup>
</Project> public class MustOwnImageRequirement : IAuthorizationRequirement
{
} namespace ImageGallery.WebApi.WebApp
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthorization(authorizationOptions =>
{
authorizationOptions.AddPolicy(
name: "MustOwnImage",
configurePolicy: policyBuilder =>
{
policyBuilder.RequireAuthenticatedUser();
policyBuilder.AddRequirements(new MustOwnImageRequirement());
});
});
services.AddScoped<IAuthorizationHandler, MustOwnImageHandler>(); [Authorize(Policy ="MustOwnImage")]
namespace ImageGallery.WebApi.WebApp.Controllers
{
[Route("api/images")]
[Authorize]
public class ImagesController : Controller
{
[HttpGet("{id}", Name = "GetImage")]
[Authorize("MustOwnImage")]
public async Task<IActionResult> GetImage(Guid id)
{
}
[HttpDelete("{id}")]
[Authorize("MustOwnImage")]
public async Task<IActionResult> DeleteImage(Guid id)
{
}
[HttpPut("{id}")]
[Authorize("MustOwnImage")]
public async Task<IActionResult> UpdateImage(Guid id, [FromBody] ImageForUpdateModel imageForUpdate)
{
}
}
} public class AppUserRole : IdentityUserRole<int>
{
public virtual AppUser User { get; set; }
public virtual AppRole Role { get; set; }
public DateTimeOffset? ExpirationDate { get; set; }
}