ASP.NET Core'da nasıl özel bir AuthorizeAttribute oluşturursunuz?


428

ASP.NET Core'da özel bir yetkilendirme özelliği yapmaya çalışıyorum. Önceki sürümlerde geçersiz kılmak mümkündü bool AuthorizeCore(HttpContextBase httpContext). Ama bu artık mevcut değil AuthorizeAttribute.

Özel bir AuthorizeAttribute yapmak için mevcut yaklaşım nedir?

Ne yapmaya çalışıyorum: Üstbilgi Yetkilendirmesi'nde bir oturum kimliği alıyorum. Bu kimlikten belirli bir işlemin geçerli olup olmadığını bileceğim.


Bunu nasıl yapacağımdan emin değilim, ama MVC açık kaynak. Github deposunu çekebilir ve IAuthorizationFilter uygulamalarını arayabilirsiniz. Bugün vaktim varsa, seni arayacağım ve gerçek bir cevap göndereceğim, ama söz vermiyorum. github repo: github.com/aspnet/Mvc
bopapa_1979

Tamam, zaman aşımına uğradı , ancak aspnet / Güvenlik deposunda AuthorizeAttribute kullanan MVC Repo'da AuthorizationPolicy kullanımlarını burada bulabilirsiniz: github.com/aspnet/Security . Alternatif olarak, önem verdiğiniz güvenlik öğelerinin bulunduğu ad alanı için MVC deposuna bakın (Microsoft.AspNet.Authorization). Üzgünüm daha fazla yardımcı olamam. İyi şanslar!
bopapa_1979

Yanıtlar:


446

ASP.Net Core ekibi tarafından önerilen yaklaşım, burada tamamen belgelenen yeni politika tasarımını kullanmaktır . Yeni yaklaşımın arkasındaki temel fikir, bir "politika" belirtmek için yeni [Yetkilendir] özelliğini kullanmaktır (örneğin [Authorize( Policy = "YouNeedToBe18ToDoThis")], politikanın, uygulamanın bir kod bloğunu yürütmek için uygulamanın Startup.cs dosyasında kayıtlı olması (ör. Kullanıcının yaş talebinde bulunmasını sağlamak) yaş 18 veya üzeri).

Politika tasarımı çerçeveye harika bir ektir ve ASP.Net Güvenlik Çekirdeği ekibinin tanıtımı için övgüyle karşılanması gerekir. Bununla birlikte, tüm durumlar için uygun değildir. Bu yaklaşımın eksikliği, belirli bir kontrolörün veya eylemin belirli bir talep türü gerektirdiğini iddia etmenin en yaygın ihtiyacı için uygun bir çözüm sağlayamamasıdır. Bir uygulamanın ayrı ayrı REST kaynakları ("CanCreateOrder", "CanReadOrder", "CanUpdateOrder", "CanDeleteOrder" vb.) Üzerinde CRUD işlemlerini yöneten yüzlerce ayrı izne sahip olması durumunda, yeni yaklaşım ya tekrarlayan bire bir gerektirir bir politika adı ile bir talep adı arasında bir eşleme (ör.options.AddPolicy("CanUpdateOrder", policy => policy.RequireClaim(MyClaimTypes.Permission, "CanUpdateOrder));) veya çalışma sırasında bu kayıtları gerçekleştirmek için bazı kodlar yazabilirsiniz (örn. tüm talep türlerini bir veritabanından okuyun ve yukarıda belirtilen çağrıyı bir döngüde gerçekleştirin). Vakaların çoğunda bu yaklaşımla ilgili sorun, gereksiz ek yük olmasıdır.

ASP.Net Core Security ekibi asla kendi çözümünüzü yaratmanızı önermese de, bazı durumlarda bu, başlamak için en ihtiyatlı seçenek olabilir.

Aşağıda, belirli bir denetleyici veya eylem için bir talep gereksinimini ifade etmenin basit bir yolunu sağlamak üzere IAuthorizationFilter kullanan bir uygulamadır:

public class ClaimRequirementAttribute : TypeFilterAttribute
{
    public ClaimRequirementAttribute(string claimType, string claimValue) : base(typeof(ClaimRequirementFilter))
    {
        Arguments = new object[] {new Claim(claimType, claimValue) };
    }
}

public class ClaimRequirementFilter : IAuthorizationFilter
{
    readonly Claim _claim;

    public ClaimRequirementFilter(Claim claim)
    {
        _claim = claim;
    }

    public void OnAuthorization(AuthorizationFilterContext context)
    {
        var hasClaim = context.HttpContext.User.Claims.Any(c => c.Type == _claim.Type && c.Value == _claim.Value);
        if (!hasClaim)
        {
            context.Result = new ForbidResult();
        }
    }
}


[Route("api/resource")]
public class MyController : Controller
{
    [ClaimRequirement(MyClaimTypes.Permission, "CanReadResource")]
    [HttpGet]
    public IActionResult GetResource()
    {
        return Ok();
    }
}

78
Bu DOĞRU CEVAP olarak işaretlenmelidir. Burada Microsoft'taki kullanıcıların geliştiricilerin geri bildirimlerini nasıl değerlendirdiğini görüyorsunuz. Bunun etrafında "kapalı fikirli" olmasının nedenini anlamıyorum, çünkü her biri için bir politikayı kodlamak zorunda olan farklı izinlerin bir miriadına sahip olmak çok yaygın bir durum. Bunu çok uzun zamandır arıyordum ... (Bu soruyu neredeyse iki yıl önce, vNext'in hala bir bahis olduğu zaman sordum: stackoverflow.com/questions/32181400/… ama hala orada kaldık)
Vi100

3
Bu iyi şeyler. Web API'sında kimlik doğrulama ara katman yazılımımız var ancak yetkilendirme izinlerinde role göre taneli güvenlik var; yani [MyAuthorize (MyClaimTypes.Permission, MyClaimValueTypes.Write, MyPermission.Employee)] gibi bir nitelik atmak zorunda kaldığınızda çok iyi görünüyor.
Mariano Peinador

4
@Derek Greer: Bu en iyi cevap. Ancak, Eylem Filtresini Yetkilendirme işleminden sonra çalışan bir ActionFilter uygularsınız. Eylem Filtresini uygulamak ve Yetkilendirmek için yine de var mı?
Jacob Phan

6
@JacobPhan Doğru, bu IAuthorizationFilter arayüzü kullanılarak daha iyi uygulanacaktır. Kodu, değişiklikleri yansıtacak şekilde güncelledim.
Derek Greer

3
böylece new ForbidResult()çalışma yapmaz (neden olur istisna / 500) ilişkili bir yetkilendirme şeması olmadığı için. Bu dava için ne kullanırım?
Sinaestetik

252

Ben asp.net güvenlik görevlisiyim. Öncelikle, bunların hiçbirinin henüz müzik mağazası örneği veya birim testleri dışında belgelenmediği için özür dilerim ve bunların tümü açıkta kalan API'lar açısından rafine ediliyor. Ayrıntılı dokümantasyon burada .

Özel yetkilendirme özellikleri yazmanızı istemiyoruz. Bunu yapmanız gerekiyorsa yanlış bir şey yaptık. Bunun yerine, yetkilendirme gereksinimleri yazmalısınız .

Yetkilendirme kimlikler üzerinde hareket eder. Kimlikler kimlik doğrulaması ile oluşturulur.

Yorumlarda başlıktaki bir oturum kimliğini kontrol etmek istediğinizi söylersiniz. Oturum kimliğiniz kimlik için temel oluşturur. AuthorizeÖzniteliği kullanmak istiyorsanız, o üstbilgiyi almak ve kimlik doğrulamasına dönüştürmek için bir kimlik doğrulama ara katmanı yazarsınız ClaimsPrincipal. Daha sonra bir yetkilendirme gereksinimi içinde bunu kontrol edersiniz. Yetkilendirme gereksinimleri istediğiniz kadar karmaşık olabilir; örneğin, şu anki kimlikle ilgili doğum tarihi talebinde bulunan ve kullanıcının 18 yaşından büyük olması durumunda yetkilendirecek olan;

public class Over18Requirement : AuthorizationHandler<Over18Requirement>, IAuthorizationRequirement
{
        public override void Handle(AuthorizationHandlerContext context, Over18Requirement requirement)
        {
            if (!context.User.HasClaim(c => c.Type == ClaimTypes.DateOfBirth))
            {
                context.Fail();
                return;
            }

            var dateOfBirth = Convert.ToDateTime(context.User.FindFirst(c => c.Type == ClaimTypes.DateOfBirth).Value);
            int age = DateTime.Today.Year - dateOfBirth.Year;
            if (dateOfBirth > DateTime.Today.AddYears(-age))
            {
                age--;
            }

            if (age >= 18)
            {
                context.Succeed(requirement);
            }
            else
            {
                context.Fail();
            }
        }
    }
}

Sonra ConfigureServices()işlevinizde onu bağlarsınız

services.AddAuthorization(options =>
{
    options.AddPolicy("Over18", 
        policy => policy.Requirements.Add(new Authorization.Over18Requirement()));
});

Ve son olarak, bir denetleyiciye veya eylem yöntemine uygulayın.

[Authorize(Policy = "Over18")]

84
Acaba ... bununla nasıl hassas bir erişim kontrolü uygulanır? Diyelim ki ManageStoreMüzik Mağazası Gereksinimi örneği. Örnekte olduğu gibi, bunu yapmanın yalnızca "izin ver ya da hiçbir şey" yolu yoktur. O zaman olası her permütasyon için yeni bir politika yaratmamız gerekiyor mu? örn. "Kullanıcılar / Okuma", "Kullanıcılar / Oluşturma", "Kullanıcılar / AssignRole", "Kullanıcılar / Sil", ayrıntılı hak talepleri istiyorsak? Bir [ClaimsAutzorization("User", "Read", "Create", "Delete", "Assign")]özellikten ziyade iddiaları yönetmek için çalışmasını ve politikaların bolluğunu sağlamak için hemen hemen kurulum işi gibi görünüyor mu?
Tseng

84
Tüm bunların özel bir yetkilendirme yöntemi uygulamaktan daha karmaşık olduğunu söylemek zorundayım. Ben yetki yapılmasını istiyorum biliyorum Ben sadece gitmek ve MVC 5 yazmak olabilir, MVC 6 onlar aslında "şey" kendini uygulamak yerine daha karmaşık olan "bitmiş" kodu bir sürü ekleyin. Beni kodları yazmak yerine bir şeyler bulmaya çalışan bir sayfanın önünde otururken, Microsoft'un (veya No-Sql) dışında RDBMS kullanan insanlar için de büyük bir acı veriyor.
Felype

17
Benim açımdan, bu tüm senaryoları çözmez. MVC 6'dan önce, kendi "İzin Sistemimi" uygulamak için özel bir Yetkilendirme Özelliği kullandım. Yetkilendirme özniteliğini tüm eylemlere ekleyebilir ve gerekli belirli bir izni (Numaralama Değeri olarak) iletebilirim. İznin kendisi DB içindeki gruplara / kullanıcılara eşlendi. Yani, bunu politikalarla ele almanın bir yolunu göremiyorum !?
Gerwald

43
Bu yorumlardaki diğer pek çok kişi gibi ben de yetkilendirme için öznitelikleri kullanmanın Web API 2'de neyin mümkün olduğu konusunda büyük ölçüde kısırlaştırıldığını hayal kırıklığına uğrattım. temel yetkilendirme algoritmasını bildirmek için öznitelik yapıcı parametreleri. Eskiden beyin gibi basit bir şey yapardı [CustomAuthorize(Operator.And, Permission.GetUser, Permission.ModifyUser)]. Sadece yapıcı parametrelerini değiştirerek sonsuz sayıda tek bir özel öznitelik kullanabilirsiniz.
NathanAldenSr

61
Ben de kendi kendini ilan "Lead ASP.NET güvenlik adam" aslında IAuthorizeData.Policyçerçeve içinde ele almak yerine, bu açık gözetim üstesinden gelmek için sihirli dizeleri (anlamını hack ) ve özel politika sağlayıcıları kullanmanızı önerdi şaşırdım . Kendi uygulamalarımızı yaratmamamız gerektiğini düşündüm? Birçoğumuzu sıfırdan yeniden uygulamak dışında bir seçeneğimiz kalmadı (tekrar) ve bu sefer Web API'sinin eski Authorizeözelliğinden bile yararlanmadan . Şimdi bunu eylem filtresi veya ara katman yazılımı seviyesinde yapmalıyız.
NathanAldenSr

104

ASP.NET Core 2 ile tekrar devralabilir AuthorizeAttribute, sadece uygulamanız IAuthorizationFilter(veya IAsyncAuthorizationFilter) yapmanız gerekir :

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class CustomAuthorizeAttribute : AuthorizeAttribute, IAuthorizationFilter
{
    private readonly string _someFilterParameter;

    public CustomAuthorizeAttribute(string someFilterParameter)
    {
        _someFilterParameter = someFilterParameter;
    }

    public void OnAuthorization(AuthorizationFilterContext context)
    {
        var user = context.HttpContext.User;

        if (!user.Identity.IsAuthenticated)
        {
            // it isn't needed to set unauthorized result 
            // as the base class already requires the user to be authenticated
            // this also makes redirect to a login page work properly
            // context.Result = new UnauthorizedResult();
            return;
        }

        // you can also use registered services
        var someService = context.HttpContext.RequestServices.GetService<ISomeService>();

        var isAuthorized = someService.IsUserAuthorized(user.Identity.Name, _someFilterParameter);
        if (!isAuthorized)
        {
            context.Result = new StatusCodeResult((int)System.Net.HttpStatusCode.Forbidden);
            return;
        }
    }
}

4
Yalnızca bu için kullanabileceği Yani inkar yetkilendirme değil hibe bunu?
MEMark

1
@MEMark tarafından verilmesi , başka bir yetkilendirme özelliği ağır basan demek?
gius

2
AFAIK, varsayılan olarak erişime izin verilir, bu nedenle açıkça reddetmeniz gerekir (örneğin, bir AuthorizeAttribute ekleyerek). Daha fazla ayrıntı için bu soruyu kontrol edin: stackoverflow.com/questions/17272422/…
gius

16
Ayrıca, önerilen örnekte birinin AuthorizeAttribute öğesinden devralmak zorunda olmadığını unutmayın. Şunlar arasından devralabilir Özelliği ve IAuthorizationFilter . Bu şekilde, standart olmayan bir kimlik doğrulama mekanizması kullanılırsa aşağıdaki istisna alınmaz: InvalidOperationException: authenticationScheme belirtilmedi ve DefaultChallengeScheme bulunamadı.
Anatolyevich

13
Senin eğer Not OnAuthorizationuygulaması bir zaman uyumsuz yöntemini beklemek üzere ihtiyacı var, uygulamalıdır IAsyncAuthorizationFilteryerine IAuthorizationFilteraksi takdirde filtre senkronize çalıştırır ve denetleyici eylem filtresi sonucu ne olursa olsun çalıştırır.
Codemunkie

34

Derek Greer BÜYÜK cevap dayanarak, enums ile yaptım.

İşte benim kod bir örnek:

public enum PermissionItem
{
    User,
    Product,
    Contact,
    Review,
    Client
}

public enum PermissionAction
{
    Read,
    Create,
}


public class AuthorizeAttribute : TypeFilterAttribute
{
    public AuthorizeAttribute(PermissionItem item, PermissionAction action)
    : base(typeof(AuthorizeActionFilter))
    {
        Arguments = new object[] { item, action };
    }
}

public class AuthorizeActionFilter : IAuthorizationFilter
{
    private readonly PermissionItem _item;
    private readonly PermissionAction _action;
    public AuthorizeActionFilter(PermissionItem item, PermissionAction action)
    {
        _item = item;
        _action = action;
    }
    public void OnAuthorization(AuthorizationFilterContext context)
    {
        bool isAuthorized = MumboJumboFunction(context.HttpContext.User, _item, _action); // :)

        if (!isAuthorized)
        {
            context.Result = new ForbidResult();
        }
    }
}

public class UserController : BaseController
{
    private readonly DbContext _context;

    public UserController( DbContext context) :
        base()
    {
        _logger = logger;
    }

    [Authorize(PermissionItem.User, PermissionAction.Read)]
    public async Task<IActionResult> Index()
    {
        return View(await _context.User.ToListAsync());
    }
}

1
Bunun için teşekkürler. Ben biraz farklı bir uygulaması ile bu yayını ve doğrulama için bir istek yarattı stackoverflow.com/questions/49551047/...
Anton Swanevelder


31

Denetleyicilerinizde ve Eylemlerinizde özel öznitelikler bulabilecek olan AuthorizationHandler'ınızı oluşturabilir ve bunları HandleRequirementAsync yöntemine iletebilirsiniz.

public abstract class AttributeAuthorizationHandler<TRequirement, TAttribute> : AuthorizationHandler<TRequirement> where TRequirement : IAuthorizationRequirement where TAttribute : Attribute
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, TRequirement requirement)
    {
        var attributes = new List<TAttribute>();

        var action = (context.Resource as AuthorizationFilterContext)?.ActionDescriptor as ControllerActionDescriptor;
        if (action != null)
        {
            attributes.AddRange(GetAttributes(action.ControllerTypeInfo.UnderlyingSystemType));
            attributes.AddRange(GetAttributes(action.MethodInfo));
        }

        return HandleRequirementAsync(context, requirement, attributes);
    }

    protected abstract Task HandleRequirementAsync(AuthorizationHandlerContext context, TRequirement requirement, IEnumerable<TAttribute> attributes);

    private static IEnumerable<TAttribute> GetAttributes(MemberInfo memberInfo)
    {
        return memberInfo.GetCustomAttributes(typeof(TAttribute), false).Cast<TAttribute>();
    }
}

Ardından, denetleyicilerinizde veya eylemlerinizde ihtiyacınız olan tüm özel özellikler için kullanabilirsiniz. Örneğin izin gereksinimleri eklemek için. Özel özelliğinizi oluşturmanız yeterlidir.

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class PermissionAttribute : AuthorizeAttribute
{
    public string Name { get; }

    public PermissionAttribute(string name) : base("Permission")
    {
        Name = name;
    }
}

Ardından Politikanıza eklemek için bir Gereksinim oluşturun

public class PermissionAuthorizationRequirement : IAuthorizationRequirement
{
    //Add any custom requirement properties if you have them
}

Ardından, daha önce oluşturduğumuz AttributeAuthorizationHandler'ı devralarak özel özelliğiniz için AuthorizationHandler'ı oluşturun. Denetleyici ve Eyleminizden biriken HandleRequirementsAsync yöntemindeki tüm özel nitelikleriniz için bir IEnumerable geçirilir.

public class PermissionAuthorizationHandler : AttributeAuthorizationHandler<PermissionAuthorizationRequirement, PermissionAttribute>
{
    protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionAuthorizationRequirement requirement, IEnumerable<PermissionAttribute> attributes)
    {
        foreach (var permissionAttribute in attributes)
        {
            if (!await AuthorizeAsync(context.User, permissionAttribute.Name))
            {
                return;
            }
        }

        context.Succeed(requirement);
    }

    private Task<bool> AuthorizeAsync(ClaimsPrincipal user, string permission)
    {
        //Implement your custom user permission logic here
    }
}

Son olarak, Startup.cs ConfigureServices yönteminizde, özel AuthorizationHandler'ınızı hizmetlere ekleyin ve Politikanızı ekleyin.

        services.AddSingleton<IAuthorizationHandler, PermissionAuthorizationHandler>();

        services.AddAuthorization(options =>
        {
            options.AddPolicy("Permission", policyBuilder =>
            {
                policyBuilder.Requirements.Add(new PermissionAuthorizationRequirement());
            });
        });

Artık Denetleyicilerinizi ve İşlemlerinizi özel özelliğinizle dekore edebilirsiniz.

[Permission("AccessCustomers")]
public class CustomersController
{
    [Permission("AddCustomer")]
    IActionResult AddCustomer([FromBody] Customer customer)
    {
        //Add customer
    }
}

1
Bu ASAP'a bir göz atacağım.
NathanAldenSr

5
Bu oldukça devralınmış ... Ben bir parametre alır basit bir AuthorizationFilterAttribute kullanarak aynı çözüldü. Bunun için düşünmenize gerek yok, "resmi" çözümden daha da artistik görünüyor (oldukça zayıf buluyorum).
Vi100

2
@ Vi100 ASP.NET Core'da AuthorizationFilters hakkında fazla bilgi bulamadım. Resmi dokümantasyon sayfası şu anda bu konu üzerinde çalıştıklarını söylüyor. docs.microsoft.com/tr-tr/aspnet/core/security/authorization/…
Shawn

4
@ Vi100 Lütfen çözümünüzü paylaşabilir misiniz, bunu başarmanın daha basit bir yolu varsa bilmek isterim.
Shawn

2
Yukarıdaki UnderlyingSystemType kullanımı not etmek bir şey derlemek değil, ama kaldırma işe yarıyor gibi görünüyor.
teatime

25

Özel bir AuthorizeAttribute yapmak için mevcut yaklaşım nedir

Kolay: kendinizinkini yaratmayın AuthorizeAttribute.

Saf yetkilendirme senaryoları için (yalnızca belirli kullanıcılara erişimi kısıtlamak gibi) önerilen yaklaşım yeni yetkilendirme bloğunu kullanmaktır: https://github.com/aspnet/MusicStore/blob/1c0aeb08bb1ebd846726232226279bbe001782e1/samples/MusicStore/Startup.cs# -L92

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.Configure<AuthorizationOptions>(options =>
        {
            options.AddPolicy("ManageStore", policy => policy.RequireClaim("Action", "ManageStore"));
        });
    }
}

public class StoreController : Controller
{
    [Authorize(Policy = "ManageStore"), HttpGet]
    public async Task<IActionResult> Manage() { ... }
}

Kimlik doğrulama için en iyi ara katman yazılımı düzeyinde ele alınır.

Tam olarak ne elde etmeye çalışıyorsunuz?


1
Üstbilgi Yetkilendirmesi'nde bir oturum kimliği alıyorum. Bu kimlikten belirli bir işlemin geçerli olup olmadığını bileceğim.
jltrem

1
O zaman bu bir yetkilendirme sorunu değildir. Sanırım "oturum kimliği" aslında arayanın kimliğini içeren bir belirteç: bu kesinlikle ara katman düzeyinde yapılmalıdır.
Kévin Chalet

3
Kimlik doğrulama değildir (kullanıcının kim olduğunu belirler), ancak yetkilendirmedir (kullanıcının bir kaynağa erişip erişemeyeceğini belirleme). Peki bunu çözmeye bakmamı nereden öneriyorsun?
jltrem

3
@jltrem, kabul ettiniz, bahsettiğiniz şey kimlik doğrulama değil yetkilendirmedir.
bopapa_1979

2
@ Nokta ben değilim. Bu bilgi için başka bir sistemi sorgularım. Bu sistem, kullanıcının kimliğini doğrular (belirler) ve yetkilendirir (bana o kullanıcının neye erişebileceğini söyler). Şu anda diğer sistem oturumu doğrulamak için her denetleyici eylem bir yöntem çağırarak çalışmak kesmek var. Bunun otomatik olarak bir özellik üzerinden olmasını istiyorum.
jltrem

4

Herhangi biri geçerli güvenlik uygulamalarını kullanarak yetkilendirme aşamasında bir taşıyıcı jetonunu doğrulamak istiyorsa,

bunu Başlangıç ​​/ Yapılandırma Hizmetlerinize ekleyin

    services.AddSingleton<IAuthorizationHandler, BearerAuthorizationHandler>();
    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer();

    services.AddAuthorization(options => options.AddPolicy("Bearer",
        policy => policy.AddRequirements(new BearerRequirement())
        )
    );

ve bu kod tabanınızda,

public class BearerRequirement : IAuthorizationRequirement
{
    public async Task<bool> IsTokenValid(SomeValidationContext context, string token)
    {
        // here you can check if the token received is valid 
        return true;
    }
}

public class BearerAuthorizationHandler : AuthorizationHandler<BearerRequirement> 
{

    public BearerAuthorizationHandler(SomeValidationContext thatYouCanInject)
    {
       ...
    }

    protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, BearerRequirement requirement)
    {
        var authFilterCtx = (Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext)context.Resource;
        string authHeader = authFilterCtx.HttpContext.Request.Headers["Authorization"];
        if (authHeader != null && authHeader.Contains("Bearer"))
        {
            var token = authHeader.Replace("Bearer ", string.Empty);
            if (await requirement.IsTokenValid(thatYouCanInject, token))
            {
                context.Succeed(requirement);
            }
        }
    }
}

Kod ulaşmazsa context.Succeed(...)yine de başarısız olur (401).

Ve sonra kontrol cihazlarınızda

 [Authorize(Policy = "Bearer", AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]

JwtBearer ara katman yazılımı zaten bununla ilgileniyorsa neden jetonun kendi doğrulamasını yapmayı seçtiniz? Ayrıca bir kimlik doğrulama / belirteç doğrulama / süre sonu hatası için WWW-Authenticate yanıt başlığına doğru içeriği koyar. Kimlik doğrulama kanalına erişmek istiyorsanız, AddJwtBearer seçeneklerinde (OnAuthenticationFailed, OnChallenge, OnMessageReceived ve OnTokenValidated) dokunabileceğiniz belirli olaylar vardır.
Darren Lewis

Bu gördüğüm diğer tüm çözümlerden son derece basittir. Özellikle basit api anahtar kullanım durumları için. Bir güncelleme: 3.1 için AuthorizationFilterContext'e yayın, uç nokta yönlendirme öğeleri nedeniyle artık geçerli değil. HttpContextAccessor aracılığıyla bağlamı yakalamanız gerekir.
JasonCoder

2

Modern yol AuthenticationHandlers

startup.cs içinde

services.AddAuthentication("BasicAuthentication").AddScheme<AuthenticationSchemeOptions, BasicAuthenticationHandler>("BasicAuthentication", null);

public class BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
    {
        private readonly IUserService _userService;

        public BasicAuthenticationHandler(
            IOptionsMonitor<AuthenticationSchemeOptions> options,
            ILoggerFactory logger,
            UrlEncoder encoder,
            ISystemClock clock,
            IUserService userService)
            : base(options, logger, encoder, clock)
        {
            _userService = userService;
        }

        protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
        {
            if (!Request.Headers.ContainsKey("Authorization"))
                return AuthenticateResult.Fail("Missing Authorization Header");

            User user = null;
            try
            {
                var authHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
                var credentialBytes = Convert.FromBase64String(authHeader.Parameter);
                var credentials = Encoding.UTF8.GetString(credentialBytes).Split(new[] { ':' }, 2);
                var username = credentials[0];
                var password = credentials[1];
                user = await _userService.Authenticate(username, password);
            }
            catch
            {
                return AuthenticateResult.Fail("Invalid Authorization Header");
            }

            if (user == null)
                return AuthenticateResult.Fail("Invalid User-name or Password");

            var claims = new[] {
                new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
                new Claim(ClaimTypes.Name, user.Username),
            };
            var identity = new ClaimsIdentity(claims, Scheme.Name);
            var principal = new ClaimsPrincipal(identity);
            var ticket = new AuthenticationTicket(principal, Scheme.Name);

            return AuthenticateResult.Success(ticket);
        }
    }

IUserService, kullanıcı adınız ve şifrenizin olduğu yerde yaptığınız bir hizmettir. temel olarak, taleplerinizi eşlemek için kullandığınız bir kullanıcı sınıfını döndürür.

var claims = new[] {
                new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
                new Claim(ClaimTypes.Name, user.Username),
            }; 

Daha sonra bu talepleri ve ona eşlediğiniz verileri sorgulayabilirsiniz, bunlardan birkaçı, ClaimTypes sınıfına bir göz atın

bunu bir uzantı yönteminde kullanabilir ve eşlemelerden herhangi birini alabilirsiniz

public int? GetUserId()
{
   if (context.User.Identity.IsAuthenticated)
    {
       var id=context.User.FindFirst(ClaimTypes.NameIdentifier);
       if (!(id is null) && int.TryParse(id.Value, out var userId))
            return userId;
     }
      return new Nullable<int>();
 }

Bu yeni yol, bence daha iyi

public class BasicAuthenticationAttribute : AuthorizationFilterAttribute
{
    public override void OnAuthorization(HttpActionContext actionContext)
    {
        if (actionContext.Request.Headers.Authorization != null)
        {
            var authToken = actionContext.Request.Headers.Authorization.Parameter;
            // decoding authToken we get decode value in 'Username:Password' format
            var decodeauthToken = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(authToken));
            // spliting decodeauthToken using ':'
            var arrUserNameandPassword = decodeauthToken.Split(':');
            // at 0th postion of array we get username and at 1st we get password
            if (IsAuthorizedUser(arrUserNameandPassword[0], arrUserNameandPassword[1]))
            {
                // setting current principle
                Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity(arrUserNameandPassword[0]), null);
            }
            else
            {
                actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
            }
        }
        else
        {
            actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
        }
    }

    public static bool IsAuthorizedUser(string Username, string Password)
    {
        // In this method we can handle our database logic here...
        return Username.Equals("test") && Password == "test";
    }
}

Bu parlak cevap sadece bir cazibe gibi çalışıyor! Bunun için teşekkür ederim ve bloglar, belgeler ve Temel kimlik doğrulama artı Rol yetkilendirme için yığın üzerinden arama altı saat gibi sonra bulduğum en iyi cevap olduğu için, oy vermenizi diliyorum.
Piotr Śródka

@ PiotrŚródka, bekliyoruz, cevabın biraz "basitleştirilmiş" olduğunu unutmayın, kötü niyetli bir kullanıcı sadece bir dizinde güzel bir son oynayarak hizmetinizi çökmesine çalışabilir olarak metinde ':' olup olmadığını test edin aralık istisnası. her zaman olduğu gibi harici kaynaklar tarafından size verilenleri test edin
Walter Vehoeven

2

Bu yazı itibariyle bunun asp.net core 2 ve üstü IClaimsTransformation arayüzü ile yapılabileceğine inanıyorum. Buraya gönderilecek kadar paylaşılabilir bir kavram kanıtı uyguladım.

public class PrivilegesToClaimsTransformer : IClaimsTransformation
{
    private readonly IPrivilegeProvider privilegeProvider;
    public const string DidItClaim = "http://foo.bar/privileges/resolved";

    public PrivilegesToClaimsTransformer(IPrivilegeProvider privilegeProvider)
    {
        this.privilegeProvider = privilegeProvider;
    }

    public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
    {
        if (principal.Identity is ClaimsIdentity claimer)
        {
            if (claimer.HasClaim(DidItClaim, bool.TrueString))
            {
                return principal;
            }

            var privileges = await this.privilegeProvider.GetPrivileges( ... );
            claimer.AddClaim(new Claim(DidItClaim, bool.TrueString));

            foreach (var privilegeAsRole in privileges)
            {
                claimer.AddClaim(new Claim(ClaimTypes.Role /*"http://schemas.microsoft.com/ws/2008/06/identity/claims/role" */, privilegeAsRole));
            }
        }

        return principal;
    }
}

Bunu Denetleyicinizde kullanmak [Authorize(Roles="whatever")]için yöntemlerinize uygun bir yöntem ekleyin .

[HttpGet]
[Route("poc")]
[Authorize(Roles = "plugh,blast")]
public JsonResult PocAuthorization()
{
    var result = Json(new
    {
        when = DateTime.UtcNow,
    });

    result.StatusCode = (int)HttpStatusCode.OK;

    return result;
}

Bizim durumumuzda her talep JWT olan bir Yetkilendirme başlığı içerir. Bu prototip ve önümüzdeki hafta üretim sistemimizde buna çok yakın bir şey yapacağımıza inanıyorum.

Gelecekteki seçmenler, oy verirken yazma tarihini düşünün. Bugün itibariyle, bu works on my machine.™ Muhtemelen uygulamanızda daha fazla hata işleme ve oturum açma isteyeceksiniz.


ConfigureServices ne olacak? Bir şey eklemek gerekiyor mu?
Daniel

Başka bir yerde tartışıldığı gibi, evet.
Para İadesi Yok İade Yok

1

Bizim app yetkilendirme için. Yetkilendirme özelliğinde geçirilen parametrelere dayalı bir hizmeti çağırmak zorunda kaldık.

Örneğin, giriş yapmış doktorun hasta randevularını görüntüleyip görüntüleyemeyeceğini kontrol etmek istiyorsak, özel yetkilendirme niteliğini sağlamak için "View_Appointment" iletilir ve DB hizmetinde doğru olup olmadığını ve sonuçlara dayanarak onaylayacağız. Bu senaryonun kodu şöyledir:

    public class PatientAuthorizeAttribute : TypeFilterAttribute
    {
    public PatientAuthorizeAttribute(params PatientAccessRights[] right) : base(typeof(AuthFilter)) //PatientAccessRights is an enum
    {
        Arguments = new object[] { right };
    }

    private class AuthFilter : IActionFilter
    {
        PatientAccessRights[] right;

        IAuthService authService;

        public AuthFilter(IAuthService authService, PatientAccessRights[] right)
        {
            this.right = right;
            this.authService = authService;
        }

        public void OnActionExecuted(ActionExecutedContext context)
        {
        }

        public void OnActionExecuting(ActionExecutingContext context)
        {
            var allparameters = context.ActionArguments.Values;
            if (allparameters.Count() == 1)
            {
                var param = allparameters.First();
                if (typeof(IPatientRequest).IsAssignableFrom(param.GetType()))
                {
                    IPatientRequest patientRequestInfo = (IPatientRequest)param;
                    PatientAccessRequest userAccessRequest = new PatientAccessRequest();
                    userAccessRequest.Rights = right;
                    userAccessRequest.MemberID = patientRequestInfo.PatientID;
                    var result = authService.CheckUserPatientAccess(userAccessRequest).Result; //this calls DB service to check from DB
                    if (result.Status == ReturnType.Failure)
                    {
                        //TODO: return apirepsonse
                        context.Result = new StatusCodeResult((int)System.Net.HttpStatusCode.Forbidden);
                    }
                }
                else
                {
                    throw new AppSystemException("PatientAuthorizeAttribute not supported");
                }
            }
            else
            {
                throw new AppSystemException("PatientAuthorizeAttribute not supported");
            }
        }
    }
}

Ve API eyleminde bunu şu şekilde kullanıyoruz:

    [PatientAuthorize(PatientAccessRights.PATIENT_VIEW_APPOINTMENTS)] //this is enum, we can pass multiple
    [HttpPost]
    public SomeReturnType ViewAppointments()
    {

    }

1
SignalR.SignalR Hub'larında Hub yöntemleri için aynı özniteliği kullanmak istediğinizde IActionFilter'ın bir sorun olacağını lütfen unutmayın IAuthorizationFilter
ilkerkaran

Bilgi için teşekkürler. Şu anda benim uygulamada SignalR kullanmıyorum, bu yüzden onunla test etmedi.
Abdullah

Sanırım yine de başlık yetkilendirme girişini kullanmak zorunda kalacaksınız, uygulama farklı olacak
Walter Vehoeven

0

Kabul edilen cevap ( https://stackoverflow.com/a/41348219/4974715 ) "CanReadResource" bir hak talebi olarak kullanıldığından gerçekçi bir şekilde sürdürülemez veya uygun değildir (ancak esasen gerçekte bir politika, IMO olmalıdır). Yanıttaki yaklaşım, kullanıldığı şekilde uygun değildir, çünkü bir eylem yöntemi birçok farklı talep kurulumu gerektiriyorsa, o zaman bu cevapla tekrar tekrar böyle bir şey yazmanız gerekir ...

[ClaimRequirement(MyClaimTypes.Permission, "CanReadResource")] 
[ClaimRequirement(MyClaimTypes.AnotherPermision, "AnotherClaimVaue")]
//and etc. on a single action.

Bunun ne kadar kodlama alacağını hayal edin. İdeal olarak, "CanReadResource" bir kullanıcının bir kaynağı okuyabilip okuyamayacağını belirlemek için birçok iddia kullanan bir politika olması gerekir.

Yaptığım şey, bir numaralandırma olarak politikalarımı oluşturmak ve sonra döngü ve böylece gibi gereksinimleri ayarlamak ...

services.AddAuthorization(authorizationOptions =>
        {
            foreach (var policyString in Enum.GetNames(typeof(Enumerations.Security.Policy)))
            {
                authorizationOptions.AddPolicy(
                    policyString,
                    authorizationPolicyBuilder => authorizationPolicyBuilder.Requirements.Add(new DefaultAuthorizationRequirement((Enumerations.Security.Policy)Enum.Parse(typeof(Enumerations.Security.Policy), policyWrtString), DateTime.UtcNow)));

      /* Note that thisn does not stop you from 
          configuring policies directly against a username, claims, roles, etc. You can do the usual.
     */
            }
        }); 

DefaultAuthorizationRequirement sınıfı ...

public class DefaultAuthorizationRequirement : IAuthorizationRequirement
{
    public Enumerations.Security.Policy Policy {get; set;} //This is a mere enumeration whose code is not shown.
    public DateTime DateTimeOfSetup {get; set;} //Just in case you have to know when the app started up. And you may want to log out a user if their profile was modified after this date-time, etc.
}

public class DefaultAuthorizationHandler : AuthorizationHandler<DefaultAuthorizationRequirement>
{
    private IAServiceToUse _aServiceToUse;

    public DefaultAuthorizationHandler(
        IAServiceToUse aServiceToUse
        )
    {
        _aServiceToUse = aServiceToUse;
    }

    protected async override Task HandleRequirementAsync(AuthorizationHandlerContext context, DefaultAuthorizationRequirement requirement)
    {
        /*Here, you can quickly check a data source or Web API or etc. 
           to know the latest date-time of the user's profile modification...
        */
        if (_aServiceToUse.GetDateTimeOfLatestUserProfileModication > requirement.DateTimeOfSetup)
        {
            context.Fail(); /*Because any modifications to user information, 
            e.g. if the user used another browser or if by Admin modification, 
            the claims of the user in this session cannot be guaranteed to be reliable.
            */
            return;
        }

        bool shouldSucceed = false; //This should first be false, because context.Succeed(...) has to only be called if the requirement specifically succeeds.

        bool shouldFail = false; /*This should first be false, because context.Fail() 
        doesn't have to be called if there's no security breach.
        */

        // You can do anything.
        await doAnythingAsync();

       /*You can get the user's claims... 
          ALSO, note that if you have a way to priorly map users or users with certain claims 
          to particular policies, add those policies as claims of the user for the sake of ease. 
          BUT policies that require dynamic code (e.g. checking for age range) would have to be 
          coded in the switch-case below to determine stuff.
       */

        var claims = context.User.Claims;

        // You can, of course, get the policy that was hit...
        var policy = requirement.Policy

        //You can use a switch case to determine what policy to deal with here...
        switch (policy)
        {
            case Enumerations.Security.Policy.CanReadResource:
                 /*Do stuff with the claims and change the 
                     value of shouldSucceed and/or shouldFail.
                */
                 break;
            case Enumerations.Security.Policy.AnotherPolicy:
                 /*Do stuff with the claims and change the 
                    value of shouldSucceed and/or shouldFail.
                 */
                 break;
                // Other policies too.

            default:
                 throw new NotImplementedException();
        }

        /* Note that the following conditions are 
            so because failure and success in a requirement handler 
            are not mutually exclusive. They demand certainty.
        */

        if (shouldFail)
        {
            context.Fail(); /*Check the docs on this method to 
            see its implications.
            */
        }                

        if (shouldSucceed)
        {
            context.Succeed(requirement); 
        } 
     }
}

Yukarıdaki kodun, bir kullanıcının veri deponuzdaki bir politika ile önceden eşlenmesini de sağlayabileceğini unutmayın. Bu nedenle, kullanıcı için hak talepleri oluştururken, temel olarak doğrudan veya dolaylı olarak kullanıcıya önceden eşlenmiş politikaları alırsınız (örneğin, kullanıcının belirli bir hak talebi değeri olduğu ve bu hak bedelinin tanımlanmış ve bir politika ile eşlenmiş olması gibi) bu hak talebi değerine sahip kullanıcılar için otomatik eşleme sağladığını) ve politikaları hak talebi olarak listeleyerek yetkilendirme işleyicisinde, kullanıcının taleplerinin gereksinim içerip içermediğini kontrol edebilirsiniz. iddialar. Bu, bir politika gereksinimini karşılamanın statik bir yoludur, örneğin "Ad" gereksinimi doğada oldukça statiktir. Yani,

[Authorize(Policy = nameof(Enumerations.Security.Policy.ViewRecord))] 

Dinamik bir gereksinim yaş aralığını vb. Denetlemeyle ilgili olabilir ve bu gereksinimleri kullanan politikalar kullanıcılarla önceden eşleştirilemez.

Dinamik politika talepleri kontrolünün bir örneği (örneğin, bir kullanıcının 18 yaşından büyük olup olmadığını kontrol etmek için) zaten @blowdart ( https://stackoverflow.com/a/31465227/4974715 ) tarafından verilen yanıtta bulunmaktadır .

Not: Bunu telefonuma yazdım. Herhangi bir yazım hatası ve biçimlendirme eksikliğini affedin.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.