ASP.NET MVCの下のSSLページ
-
03-07-2019 - |
質問
ASP.NET MVCベースのサイトの一部のページでHTTPSを使用するにはどうすればよいですか
Steve Sandersonは、プレビュー4でDRYの方法でこれを行う方法に関する非常に優れたチュートリアルを提供しています。
http:// blog .codeville.net / 2008/08/05 / adding-httpsssl-support-to-aspnet-mvc-routing /
プレビュー5には、より良い/更新された方法がありますか?
解決
ASP.NET MVC 2 Preview 2以降を使用している場合、次のように使用できるようになりました:
[RequireHttps]
public ActionResult Login()
{
return View();
}
ただし、ここに記載。
他のヒント
MVCFutures には「RequireSSL」属性があります。
(更新されたブログ投稿でそれを指摘してくれてありがとう。 )
アクションメソッドに適用するだけで、http://リクエストが自動的にhttps://になるようにするには、「Redirect = true」を使用します。
[RequireSsl(Redirect = true)]
Amadiereが書いたように、[RequireHttps]は素晴らしいHTTPSの入力のためのMVC 2。しかし、あなたが言ったように一部ページにのみHTTPSを使用したい場合、MVC 2はあなたに何の愛も与えません-ユーザーをHTTPSに切り替えると、手動でリダイレクトするまでそこに留まります。
私が使用したアプローチは、別のカスタム属性[ExitHttpsIfNotRequired]を使用することです。次の場合、コントローラーまたはアクションに接続すると、HTTPにリダイレクトされます。
- リクエストはHTTPSでした
- [RequireHttps]属性がアクション(またはコントローラー)に適用されませんでした
- リクエストはGETでした(POSTをリダイレクトすると、あらゆる種類のトラブルが発生します)。
ここに投稿するには大きすぎますが、ここにあるコードと追加の詳細。
Dan Wahlinの最近の投稿は次のとおりです。
http://weblogs.asp.net/dwahlin/archive/2009/08/25/requiring-ssl-for-asp-net-mvc-controllers.aspx
彼はActionFilter属性を使用します。
属性指向の開発アプローチのファンではない人のために、役立つコードを以下に示します。
public static readonly string[] SecurePages = new[] { "login", "join" };
protected void Application_AuthorizeRequest(object sender, EventArgs e)
{
var pageName = RequestHelper.GetPageNameOrDefault();
if (!HttpContext.Current.Request.IsSecureConnection
&& (HttpContext.Current.Request.IsAuthenticated || SecurePages.Contains(pageName)))
{
Response.Redirect("https://" + Request.ServerVariables["HTTP_HOST"] + HttpContext.Current.Request.RawUrl);
}
if (HttpContext.Current.Request.IsSecureConnection
&& !HttpContext.Current.Request.IsAuthenticated
&& !SecurePages.Contains(pageName))
{
Response.Redirect("http://" + Request.ServerVariables["HTTP_HOST"] + HttpContext.Current.Request.RawUrl);
}
}
属性を回避する理由はいくつかありますが、その1つは、セキュリティで保護されたすべてのページのリストを確認したい場合、ソリューション内のすべてのコントローラーをジャンプする必要があることです。
この質問に答えて、私の解決策が誰かに役立つことを願っています。
いくつかの問題がありました: -特定のアクション、たとえば" LogOn"を保護する必要があります。 「アカウント」で。 RequireHttps属性でビルドを使用できますが、これは素晴らしいことですが、https://でリダイレクトされます。 -リンク、フォーム、およびそのような「SSL対応」を作成する必要があります。
通常、私のソリューションでは、プロトコルを指定する機能に加えて、絶対URLを使用するルートを指定できます。このアプローチを使用して、「https」を指定できます。プロトコル。
したがって、最初にConnectionProtocol列挙型を作成しました:
/// <summary>
/// Enum representing the available secure connection requirements
/// </summary>
public enum ConnectionProtocol
{
/// <summary>
/// No secure connection requirement
/// </summary>
Ignore,
/// <summary>
/// No secure connection should be used, use standard http request.
/// </summary>
Http,
/// <summary>
/// The connection should be secured using SSL (https protocol).
/// </summary>
Https
}
今、RequireSslの手巻きバージョンを作成しました。元のRequireSslソースコードを変更して、http:// URLにリダイレクトできるようにしました。さらに、SSLを必要とするかどうかを決定できるフィールドを配置しました(DEBUGプリプロセッサで使用しています)。
/* Note:
* This is hand-rolled version of the original System.Web.Mvc.RequireHttpsAttribute.
* This version contains three improvements:
* - Allows to redirect back into http:// addresses, based on the <see cref="SecureConnectionRequirement" /> Requirement property.
* - Allows to turn the protocol scheme redirection off based on given condition.
* - Using Request.IsCurrentConnectionSecured() extension method, which contains fix for load-balanced servers.
*/
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public sealed class RequireHttpsAttribute : FilterAttribute, IAuthorizationFilter
{
public RequireHttpsAttribute()
{
Protocol = ConnectionProtocol.Ignore;
}
/// <summary>
/// Gets or sets the secure connection required protocol scheme level
/// </summary>
public ConnectionProtocol Protocol { get; set; }
/// <summary>
/// Gets the value that indicates if secure connections are been allowed
/// </summary>
public bool SecureConnectionsAllowed
{
get
{
#if DEBUG
return false;
#else
return true;
#endif
}
}
public void OnAuthorization(System.Web.Mvc.AuthorizationContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
/* Are we allowed to use secure connections? */
if (!SecureConnectionsAllowed)
return;
switch (Protocol)
{
case ConnectionProtocol.Https:
if (!filterContext.HttpContext.Request.IsCurrentConnectionSecured())
{
HandleNonHttpsRequest(filterContext);
}
break;
case ConnectionProtocol.Http:
if (filterContext.HttpContext.Request.IsCurrentConnectionSecured())
{
HandleNonHttpRequest(filterContext);
}
break;
}
}
private void HandleNonHttpsRequest(AuthorizationContext filterContext)
{
// only redirect for GET requests, otherwise the browser might not propagate the verb and request
// body correctly.
if (!String.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException("The requested resource can only be accessed via SSL.");
}
// redirect to HTTPS version of page
string url = "https://" + filterContext.HttpContext.Request.Url.Host + filterContext.HttpContext.Request.RawUrl;
filterContext.Result = new RedirectResult(url);
}
private void HandleNonHttpRequest(AuthorizationContext filterContext)
{
if (!String.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException("The requested resource can only be accessed without SSL.");
}
// redirect to HTTP version of page
string url = "http://" + filterContext.HttpContext.Request.Url.Host + filterContext.HttpContext.Request.RawUrl;
filterContext.Result = new RedirectResult(url);
}
}
現在、このRequireSslは、Requirements属性値に基づいて次のことを行います。 -無視:何もしません。 -HTTP:HTTPプロトコルへのリダイレクトを強制します。 -Https:httpsプロトコルへのリダイレクトを強制します。
独自のベースコントローラーを作成し、この属性をHttpに設定する必要があります。
[RequireSsl(Requirement = ConnectionProtocol.Http)]
public class MyController : Controller
{
public MyController() { }
}
今、SSLを要求する各cpntroller / actionで、この属性をConnectionProtocol.Httpsで設定するだけです。
これでURLに移行できるようになりました:URLルーティングエンジンに関する問題はほとんどありませんでした。それらの詳細については、 httpをご覧ください。 ://blog.stevensanderson.com/2008/08/05/adding-httpsssl-support-to-aspnet-mvc-routing/ 。この投稿で提案されている解決策は理論的には優れていますが、古いものであり、私はこのアプローチが嫌いです。
私の解決策は次のとおりです。 基本的な「ルート」のサブクラスを作成しますクラス:
パブリッククラスAbsoluteUrlRoute:ルート { #region ctor
/// <summary>
/// Initializes a new instance of the System.Web.Routing.Route class, by using
/// the specified URL pattern and handler class.
/// </summary>
/// <param name="url">The URL pattern for the route.</param>
/// <param name="routeHandler">The object that processes requests for the route.</param>
public AbsoluteUrlRoute(string url, IRouteHandler routeHandler)
: base(url, routeHandler)
{
}
/// <summary>
/// Initializes a new instance of the System.Web.Routing.Route class, by using
/// the specified URL pattern and handler class.
/// </summary>
/// <param name="url">The URL pattern for the route.</param>
/// <param name="defaults">The values to use for any parameters that are missing in the URL.</param>
/// <param name="routeHandler">The object that processes requests for the route.</param>
public AbsoluteUrlRoute(string url, RouteValueDictionary defaults, IRouteHandler routeHandler)
: base(url, defaults, routeHandler)
{
}
/// <summary>
/// Initializes a new instance of the System.Web.Routing.Route class, by using
/// the specified URL pattern and handler class.
/// </summary>
/// <param name="url">The URL pattern for the route.</param>
/// <param name="defaults">The values to use for any parameters that are missing in the URL.</param>
/// <param name="constraints">A regular expression that specifies valid values for a URL parameter.</param>
/// <param name="routeHandler">The object that processes requests for the route.</param>
public AbsoluteUrlRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints,
IRouteHandler routeHandler)
: base(url, defaults, constraints, routeHandler)
{
}
/// <summary>
/// Initializes a new instance of the System.Web.Routing.Route class, by using
/// the specified URL pattern and handler class.
/// </summary>
/// <param name="url">The URL pattern for the route.</param>
/// <param name="defaults">The values to use for any parameters that are missing in the URL.</param>
/// <param name="constraints">A regular expression that specifies valid values for a URL parameter.</param>
/// <param name="dataTokens">Custom values that are passed to the route handler, but which are not used
/// to determine whether the route matches a specific URL pattern. These values
/// are passed to the route handler, where they can be used for processing the
/// request.</param>
/// <param name="routeHandler">The object that processes requests for the route.</param>
public AbsoluteUrlRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints,
RouteValueDictionary dataTokens, IRouteHandler routeHandler)
: base(url, defaults, constraints, dataTokens, routeHandler)
{
}
#endregion
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
var virtualPath = base.GetVirtualPath(requestContext, values);
if (virtualPath != null)
{
var scheme = "http";
if (this.DataTokens != null && (string)this.DataTokens["scheme"] != string.Empty)
{
scheme = (string) this.DataTokens["scheme"];
}
virtualPath.VirtualPath = MakeAbsoluteUrl(requestContext, virtualPath.VirtualPath, scheme);
return virtualPath;
}
return null;
}
#region Helpers
/// <summary>
/// Creates an absolute url
/// </summary>
/// <param name="requestContext">The request context</param>
/// <param name="virtualPath">The initial virtual relative path</param>
/// <param name="scheme">The protocol scheme</param>
/// <returns>The absolute URL</returns>
private string MakeAbsoluteUrl(RequestContext requestContext, string virtualPath, string scheme)
{
return string.Format("{0}://{1}{2}{3}{4}",
scheme,
requestContext.HttpContext.Request.Url.Host,
requestContext.HttpContext.Request.ApplicationPath,
requestContext.HttpContext.Request.ApplicationPath.EndsWith("/") ? "" : "/",
virtualPath);
}
#endregion
}
&quot;ルート&quot;のこのバージョンクラスは絶対URLを作成します。ここでのコツは、ブログ投稿者の提案に続いて、DataTokenを使用してスキームを指定することです(最後の例:))。
ここで、たとえばルート&quot; Account / LogOn&quot;のようなURLを生成する場合&quot; / http://example.com/Account/LogOn &quot; -これは、UrlRoutingModuleがすべてのURLを相対として認識しているためです。カスタムHttpModuleを使用して修正できます:
public class AbsoluteUrlRoutingModule : UrlRoutingModule
{
protected override void Init(System.Web.HttpApplication application)
{
application.PostMapRequestHandler += application_PostMapRequestHandler;
base.Init(application);
}
protected void application_PostMapRequestHandler(object sender, EventArgs e)
{
var wrapper = new AbsoluteUrlAwareHttpContextWrapper(((HttpApplication)sender).Context);
}
public override void PostResolveRequestCache(HttpContextBase context)
{
base.PostResolveRequestCache(new AbsoluteUrlAwareHttpContextWrapper(HttpContext.Current));
}
private class AbsoluteUrlAwareHttpContextWrapper : HttpContextWrapper
{
private readonly HttpContext _context;
private HttpResponseBase _response = null;
public AbsoluteUrlAwareHttpContextWrapper(HttpContext context)
: base(context)
{
this._context = context;
}
public override HttpResponseBase Response
{
get
{
return _response ??
(_response =
new AbsoluteUrlAwareHttpResponseWrapper(_context.Response));
}
}
private class AbsoluteUrlAwareHttpResponseWrapper : HttpResponseWrapper
{
public AbsoluteUrlAwareHttpResponseWrapper(HttpResponse response)
: base(response)
{
}
public override string ApplyAppPathModifier(string virtualPath)
{
int length = virtualPath.Length;
if (length > 7 && virtualPath.Substring(0, 7) == "/http:/")
return virtualPath.Substring(1);
else if (length > 8 && virtualPath.Substring(0, 8) == "/https:/")
return virtualPath.Substring(1);
return base.ApplyAppPathModifier(virtualPath);
}
}
}
}
このモジュールはUrlRoutingModuleの基本実装をオーバーライドするため、基本httpModuleを削除してweb.configに登録する必要があります。したがって、「system.web」の下で設定:
<httpModules>
<!-- Removing the default UrlRoutingModule and inserting our own absolute url routing module -->
<remove name="UrlRoutingModule-4.0" />
<add name="UrlRoutingModule-4.0" type="MyApp.Web.Mvc.Routing.AbsoluteUrlRoutingModule" />
</httpModules>
それで:)。
絶対/プロトコルに従うルートを登録するには、以下を実行する必要があります:
routes.Add(new AbsoluteUrlRoute("Account/LogOn", new MvcRouteHandler())
{
Defaults = new RouteValueDictionary(new {controller = "Account", action = "LogOn", area = ""}),
DataTokens = new RouteValueDictionary(new {scheme = "https"})
});
フィードバック+改善点をお楽しみください。それが役立つことを願っています! :)
編集: IsCurrentConnectionSecured()拡張メソッドを含めるのを忘れました(スニペットが多すぎます:P)。これは、通常Request.IsSecuredConnectionを使用する拡張メソッドです。ただし、ロードバランシングを使用している場合、このアプローチは機能しません。したがって、このメソッドはこれをバイパスできます(nopCommerceから取得)。
/// <summary>
/// Gets a value indicating whether current connection is secured
/// </summary>
/// <param name="request">The base request context</param>
/// <returns>true - secured, false - not secured</returns>
/// <remarks><![CDATA[ This method checks whether or not the connection is secured.
/// There's a standard Request.IsSecureConnection attribute, but it won't be loaded correctly in case of load-balancer.
/// See: <a href="http://nopcommerce.codeplex.com/SourceControl/changeset/view/16de4a113aa9#src/Libraries/Nop.Core/WebHelper.cs">nopCommerce WebHelper IsCurrentConnectionSecured()</a>]]></remarks>
public static bool IsCurrentConnectionSecured(this HttpRequestBase request)
{
return request != null && request.IsSecureConnection;
// when your hosting uses a load balancer on their server then the Request.IsSecureConnection is never got set to true, use the statement below
// just uncomment it
//return request != null && request.ServerVariables["HTTP_CLUSTER_HTTPS"] == "on";
}
Pablo M. Cibranoによる2009年1月のブログ投稿。HttpModuleや拡張メソッドを含むいくつかのテクニックをまとめています。
こちらは Adam Salvoによるブログ投稿でActionFilterを使用しています。
これは必ずしもMVC固有ではありませんが、このソリューションはASP.NET WebFormsとMVCの両方で機能します。
http://www.codeproject.com/KB/web-security /WebPageSecurity_v2.aspx
私はこれを数年間使用しており、web.configファイルによる懸念と管理の分離が好きです。
MVC 6 (ASP.NET Core 1.0)は、Startup.csとわずかに異なる動作をします。
すべてのページでRequireHttpsAttribute(Amadiereによる answer を参照)を使用するには、これをStartup.csに追加できます。各コントローラーで属性スタイルを使用する代わりに(またはすべてのコントローラーから継承するBaseControllerを作成する代わりに)。
Startup.cs -フィルターの登録:
public void ConfigureServices(IServiceCollection services)
{
// TODO: Register other services
services.AddMvc(options =>
{
options.Filters.Add(typeof(RequireHttpsAttribute));
});
}
上記のアプローチの設計決定の詳細については、 localhostリクエストの処理からlocalhostリクエストを除外する方法に関する同様の質問に対する私の回答を参照してください。 RequireHttpsAttribute 。