Yetkilendirme ve yetkilendirme hataları için AuthorizeAttribute neden giriş sayfasına yönlendiriyor?


265

ASP.NET MVC'de bir denetleyici yöntemini şu şekilde işaretleyebilirsiniz AuthorizeAttribute:

[Authorize(Roles = "CanDeleteTags")]
public void Delete(string tagName)
{
    // ...
}

Bu, şu anda oturum açmış olan kullanıcı "CanDeleteTags" rolünde değilse, denetleyici yönteminin hiçbir zaman çağrılmayacağı anlamına gelir.

Ne yazık ki, başarısızlıklarından dolayı AuthorizeAttributegetiri HttpUnauthorizedResulthep HTTP durum kodu 401. döndürür Bu oturum açma sayfasına bir yeniden yönlendirme neden olur.

Kullanıcı oturum açmadıysa, bu çok mantıklıdır. Ancak, kullanıcı zaten giriş yapmışsa ancak gerekli rolde değilse, giriş sayfasına geri göndermek kafa karıştırıcıdır.

AuthorizeAttributeKimlik doğrulama ve yetkilendirmeyi birleştiriyor gibi görünüyor .

Bu ASP.NET MVC bir gözetim biraz gibi görünüyor, ya da bir şey eksik mi?

Ben DemandRoleAttributeikisini ayıran bir yemek zorunda kaldı . Kullanıcı kimliği doğrulanmadığında, HTTP 401'i döndürür ve giriş sayfasına gönderir. Kullanıcı oturum açtığında, ancak gerekli rolde olmadığında, bunun NotAuthorizedResultyerine bir dosya oluşturulur . Şu anda bu bir hata sayfasına yönlendiriyor.

Elbette bunu yapmak zorunda değildim mi?


10
Mükemmel bir soru ve katılıyorum, bir HTTP Yetkili değil durumu atmak olmalıdır.
Pure.Krome

3
Çözümünü seviyorum Roger. Yapmasan bile.
Jon Davis

Oturum Açma sayfam, önceden yetkilendirilmişse kullanıcıyı ReturnUrl'e yönlendirmek için bir onay işaretine sahiptir. 302 yönlendirmesi olan sonsuz bir döngü oluşturmayı başardım: D woot.
juhan_h

1
Check out bu .
Jogi

Roger, çözümünüz hakkında iyi bir makale - red-gate.com/simple-talk/dotnet/asp-net/… Çözümünüzü temiz bir şekilde yapmanın tek yolu gibi görünüyor
Craig

Yanıtlar:


305

İlk geliştirildiğinde, System.Web.Mvc.AuthorizeAttribute doğru olanı yapıyordu - HTTP spesifikasyonunun eski revizyonları hem "yetkisiz" hem de "kimliği doğrulanmamış" için 401 durum kodunu kullandı.

Orijinal spesifikasyondan:

İstek zaten Yetkilendirme kimlik bilgilerini içeriyorsa, 401 yanıtı bu kimlik bilgileri için yetkinin reddedildiğini gösterir.

Aslında, karışıklığı orada görebilirsiniz - "kimlik doğrulama" anlamına geldiğinde "yetkilendirme" kelimesini kullanır. Ancak günlük uygulamada, kullanıcının kimliği doğrulandığında ancak yetkilendirilmediğinde 403 Yasak'ı döndürmek daha mantıklıdır. Kullanıcının, onlara erişim sağlayacak ikinci bir kimlik bilgisine sahip olması olası değildir - kötü kullanıcı deneyimi.

Çoğu işletim sistemini düşünün - erişim izniniz olmayan bir dosyayı okumaya çalıştığınızda, bir giriş ekranı gösterilmez!

Neyse ki, belirsizlikler gidermek için HTTP özellikleri güncellendi (Haziran 2014).

"Hiper Metin Aktarım Protokolü (HTTP / 1.1): Kimlik Doğrulama" (RFC 7235):

401 (Yetkisiz) durum kodu, hedef kaynak için geçerli kimlik doğrulama bilgilerinden yoksun olduğu için isteğin uygulanmadığını gösterir.

"Köprü Metni Aktarım Protokolü (HTTP / 1.1): Anlambilim ve İçerik" (RFC 7231):

403 (Yasak) durum kodu, sunucunun isteği anladığını, ancak yetkilendirmeyi reddettiğini gösterir.

İlginçtir ki, ASP.NET MVC 1 piyasaya sürüldüğünde AuthorizeAttribute davranışı doğruydu. Şimdi, davranış yanlış - HTTP / 1.1 belirtimi düzeltildi.

ASP.NET'in giriş sayfası yönlendirmelerini değiştirmeye çalışmak yerine, sorunu kaynağında düzeltmek daha kolaydır. Web sitenizin varsayılan ad alanında aynı ada ( AuthorizeAttribute) sahip yeni bir özellik oluşturabilirsiniz (bu çok önemlidir), derleyici MVC'nin standart adı yerine otomatik olarak alacaktır. Elbette, bu yaklaşımı tercih ederseniz, özelliğe her zaman yeni bir ad verebilirsiniz.

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class AuthorizeAttribute : System.Web.Mvc.AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(System.Web.Mvc.AuthorizationContext filterContext)
    {
        if (filterContext.HttpContext.Request.IsAuthenticated)
        {
            filterContext.Result = new System.Web.Mvc.HttpStatusCodeResult((int)System.Net.HttpStatusCode.Forbidden);
        }
        else
        {
            base.HandleUnauthorizedRequest(filterContext);
        }
    }
}

52
+1 Çok iyi bir yaklaşım. Küçük bir öneri: kontrol etmek yerine, yerleşik null kontroller ile gelen filterContext.HttpContext.User.Identity.IsAuthenticatedkontrol edebilirsiniz filterContext.HttpContext.Request.IsAuthenticated. Bkz. Stackoverflow.com/questions/1379566/…
Daniel Liuzzi

> Web sitenizin varsayılan ad alanında aynı ada (AuthorizeAttribute) sahip yeni bir özellik oluşturabilirsiniz, daha sonra derleyici otomatik olarak MVC'nin standart adı yerine alacaktır. Bu bir hatayla sonuçlanır: 'Authorize' türü veya ad alanı bulunamadı (bir yönerge veya derleme başvurusu eksik mi?) Her ikisi de System.Web.Mvc kullanarak; ve özel AuthorizeAttribute sınıfımın ad alanına denetleyicide başvurulur. Bunu çözmek için [MyNamepace.Authorize] kullanmak zorunda kaldım
stormwild

2
@DePeter spec bir yönlendirme hakkında hiçbir şey söylemez, bu yüzden yönlendirme neden daha iyi bir çözümdür? Bu tek başına çözmek için bir kesmek olmadan ajax isteklerini öldürür.
Adam Tuliper - MSFT

1
Bu açıkça bir davranışsal hata olduğu için MS Connect'te oturum açılmalıdır. Teşekkürler.
Tony Wall

BTW, neden giriş sayfasına yönlendiriliyoruz ? Neden sadece aynı istek içinde bir 401 kodu ve giriş sayfası çıkarmıyorsunuz?
SandRock

25

Bunu Giriş Sayfası_Yol işlevinize ekleyin:

// User was redirected here because of authorization section
if (User.Identity != null && User.Identity.IsAuthenticated)
    Response.Redirect("Unauthorized.aspx");

Kullanıcı oraya yeniden yönlendirildiğinde, ancak önceden giriş yaptığında, yetkisiz sayfayı gösterir. Eğer giriş yapmazlarsa, giriş yapar ve giriş sayfasını gösterir.


18
Page_Load bir web formları mojo
Chance

2
@Chance - bunu FormsAuthencation'ın çağırmak üzere ayarlandığı denetleyici için varsayılan ActionMethod'da yapın.
Pure.Krome

MVC için böyle bir şey olmalı ama bu aslında gerçekten iyi çalışıyor if (User.Identity != null && User.Identity.IsAuthenticated) return RedirectToRoute("Unauthorized");nerede Yetkisiz tanımlı güzergah adıdır.
Moses Machua

Yani bir kaynak istersiniz, bir giriş sayfasına yönlendirilirsiniz ve tekrar bir 403 sayfasına yönlendirilirsiniz ? Bana kötü geliyor. Bir yönlendirmeye bile tahammül edemiyorum. IMO bu şey zaten çok kötü inşa edilmiş.
SandRock

3
Çözümünüze göre, daha önce giriş yaptıysanız ve URL'yi yazarak Giriş sayfasına giderseniz ... bu sizi Yetkisiz sayfaya yönlendirir. ki bu doğru değil.
Rajshekar Reddy

4

Her zaman bunun mantıklı olduğunu düşündüm. Giriş yaptıysanız ve sahip olmadığınız bir rolü gerektiren bir sayfaya basmaya çalışırsanız, rolüne sahip bir kullanıcıyla giriş yapmanızı isteyen giriş ekranına yönlendirilirsiniz.

Oturum açma sayfasına, kullanıcının zaten kimlik doğrulaması yapılıp yapılmadığını kontrol eden bir mantık ekleyebilirsiniz. Neden oraya tekrar vurulduğunu açıklayan samimi bir mesaj ekleyebilirsiniz.


4
Çoğu insanın belirli bir web uygulaması için birden fazla kimliğe sahip olma eğiliminde olmadığını düşünüyorum. Eğer yaparlarsa, "şu anki kimliğimde mojo yok, diğeri olarak tekrar giriş yapacağım" diye düşünecek kadar akıllılar.
Roger Lipscombe

Giriş sayfasında bir şey görüntülemeyle ilgili diğer noktanız iyi olsa da. Teşekkürler.
Roger Lipscombe

4

Ne yazık ki, ASP.NET form kimlik doğrulamasının varsayılan davranışıyla ilgileniyorsunuz. Burada tartışılan (denemedim) bir geçici çözüm var:

http://www.codeproject.com/KB/aspnet/Custon401Page.aspx

(MVC'ye özgü değildir)

Çoğu durumda en iyi çözüm, kullanıcının oraya gitmeye çalışmadan önce yetkisiz kaynaklara erişimi kısıtlamak olduğunu düşünüyorum. Bu yetkisiz sayfaya yönlendirebilecek bağlantıyı veya düğmeyi kaldırarak / grileştirerek.

Yetkisiz bir kullanıcının nereye yönlendirileceğini belirlemek için özellikte ek bir parametrenin olması muhtemelen iyi olur. Ama bu arada AuthorizeAttribute'a güvenlik ağı olarak bakıyorum.


Ben de yetkilendirme dayalı bağlantı kaldırma planlıyoruz (Ben burada bir yerde bu konuda bir soru gördüm), bu yüzden daha sonra bir HtmlHelper uzantısı yöntemi kodlayacağım.
Roger Lipscombe

1
Yine de kullanıcının doğrudan URL'ye gitmesini önlemek zorundayım, bu özellik tümüyle ilgili. Custom 401 çözümünden çok memnun değilim (biraz küresel görünüyor), bu yüzden NotAuthorizedResult'umu RedirectToRouteResult üzerinde modellemeyi deneyeceğim ...
Roger Lipscombe

0

Bunu Global.ascx dosyanızın Application_EndRequest işleyicisinde deneyin

if (HttpContext.Current.Response.Status.StartsWith("302") && HttpContext.Current.Request.Url.ToString().Contains("/<restricted_path>/"))
{
    HttpContext.Current.Response.ClearContent();
    Response.Redirect("~/AccessDenied.aspx");
}

0

Aspnetcore 2.0 kullanıyorsanız, bunu kullanın:

using System;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;

namespace Core
{
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
    public class AuthorizeApiAttribute : Microsoft.AspNetCore.Authorization.AuthorizeAttribute, IAuthorizationFilter
    {
        public void OnAuthorization(AuthorizationFilterContext context)
        {
            var user = context.HttpContext.User;

            if (!user.Identity.IsAuthenticated)
            {
                context.Result = new UnauthorizedResult();
                return;
            }
        }
    }
}

0

Benim durumumda "HTTP belirtimi hem" yetkisiz "hem de" kimliği doğrulanmamış "durum kodu 401 kullanıldı. ShadowChaser'ın dediği gibi.

Bu çözüm benim için çalışıyor:

if (User != null &&  User.Identity.IsAuthenticated && Response.StatusCode == 401)
{
    //Do whatever

    //In my case redirect to error page
    Response.RedirectToRoute("Default", new { controller = "Home", action = "ErrorUnauthorized" });
}
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.