AuthorizeAttributeが認証および承認の失敗のためにログインページにリダイレクトするのはなぜですか?
-
04-07-2019 - |
質問
ASP.NET MVCでは、次のように AuthorizeAttribute
を使用してコントローラーメソッドをマークアップできます。
[Authorize(Roles = "CanDeleteTags")]
public void Delete(string tagName)
{
// ...
}
これは、現在ログインしているユーザーが" CanDeleteTags"にない場合、役割、コントローラーメソッドは呼び出されません。
残念ながら、失敗の場合、 AuthorizeAttribute
は HttpUnauthorizedResult
を返し、常にHTTPステータスコード401を返します。これにより、ログインページへのリダイレクトが発生します。
ユーザーがログインしていない場合、これは理にかなっています。ただし、ユーザーがすでにログインしているが、必要な役割になっていない場合は、ログインページにユーザーを送り返すのが混乱します。
AuthorizeAttribute
は認証と承認を制限しているようです。
これは、ASP.NET MVCで少し見落としているように見えますか、それとも何か不足していますか?
2つを分離する DemandRoleAttribute
を作成する必要がありました。ユーザーが認証されない場合、HTTP 401を返し、ログインページに送信します。ユーザーがログインしているが、必要な役割になっていない場合、代わりに NotAuthorizedResult
を作成します。現在、これはエラーページにリダイレクトされます。
本当にこれをする必要はなかったのですか?
解決
最初に開発されたとき、System.Web.Mvc.AuthorizeAttributeは正しいことをしていた- HTTP仕様の古いリビジョンでは、「無許可」と「無許可」の両方にステータスコード401が使用されていました。および「認証されていない」。
元の仕様から:
リクエストにすでに認証資格情報が含まれていた場合、401応答は、それらの資格情報の認証が拒否されたことを示します。
実際、混乱がすぐにわかります。「承認」という言葉を使用しています。 「認証」を意味する場合。ただし、日常業務では、ユーザーが認証されているが許可されていない場合は、403 Forbiddenを返す方が合理的です。ユーザーがアクセスを許可する2番目の資格情報セットを持っている可能性は低い-周りのユーザーエクスペリエンスが悪い。
ほとんどのオペレーティングシステムを考慮してください-アクセス権限のないファイルを読み取ろうとすると、ログイン画面は表示されません!
ありがたいことに、あいまいさを排除するためにHTTP仕様が更新されました(2014年6月)。
「Hyper Text Transport Protocol(HTTP / 1.1):Authentication」から(RFC 7235):
401(Unauthorized)ステータスコードは、ターゲットリソースの有効な認証資格情報がないため、リクエストが適用されていないことを示しています。
「Hypertext Transfer Protocol(HTTP / 1.1):Semantics and Content」から; (RFC 7231):
403(禁止)ステータスコードは、サーバーがリクエストを理解したが、承認を拒否したことを示します。
興味深いことに、ASP.NET MVC 1がリリースされた時点では、AuthorizeAttributeの動作は正しいものでした。現在、動作は正しくありません-HTTP / 1.1仕様が修正されました。
ASP.NETのログインページリダイレクトを変更するよりも、ソースで問題を修正する方が簡単です。同じ名前( AuthorizeAttribute
)で新しい属性を Webサイトのデフォルト名前空間に作成できます(これは非常に重要です)、コンパイラはMVCの標準ではなく自動的にそれを選択します1。もちろん、そのようなアプローチを取りたい場合は、いつでも属性に新しい名前を付けることができます。
[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);
}
}
}
他のヒント
これをLogin Page_Load関数に追加します:
// User was redirected here because of authorization section
if (User.Identity != null && User.Identity.IsAuthenticated)
Response.Redirect("Unauthorized.aspx");
ユーザーがそこにリダイレクトされているが、すでにログインしている場合、不正なページが表示されます。彼らがログインしていない場合は、ログインページが表示されます。
これは理にかなっているといつも思っていました。ログインしていて、自分が持っていないロールを必要とするページにアクセスしようとすると、ログイン画面に転送され、そのロールを持っているユーザーでログインするよう求められます。
ユーザーがすでに認証されているかどうかを確認するためのロジックをログインページに追加できます。なぜ彼らが再び戻ってきたのかを説明するフレンドリーなメッセージを追加することができます。
残念ながら、ASP.NETフォーム認証のデフォルトの動作を扱っています。ここで説明されている回避策があります(試していません):
http://www.codeproject.com/KB/aspnet/Custon401Page.aspx
(MVCに固有ではありません)
ほとんどの場合、最善の解決策は、ユーザーがアクセスしようとする前に不正なリソースへのアクセスを制限することだと思います。この無許可のページに移動する可能性のあるリンクまたはボタンを削除/グレーアウトすることにより。
おそらく、許可されていないユーザーをどこにリダイレクトするかを指定するために、属性に追加のパラメーターがあればいいでしょう。しかし、当面は、AuthorizeAttributeをセーフティネットと見なします。
Global.asaxファイルのApplication_EndRequestハンドラーでこれを試してください
if (HttpContext.Current.Response.Status.StartsWith("302") && HttpContext.Current.Request.Url.ToString().Contains("/<restricted_path>/"))
{
HttpContext.Current.Response.ClearContent();
Response.Redirect("~/AccessDenied.aspx");
}
aspnetcore 2.0を使用している場合、これを使用します:
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;
}
}
}
}
私の場合、問題は&quot; HTTP仕様が&quot; unauthorized&quot;とおよび「認証されていない」」。 ShadowChaserが言ったように。
この解決策は私に役立ちます:
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" });
}