내 ASP.NET MVC 기반 사이트의 일부 페이지에 HTTPS를 사용하려면 어떻게해야합니까?
Steve Sanderson은 Preview 4에서 DRY 방식으로이 작업을 수행하는 방법에 대한 꽤 좋은 튜토리얼을 제공합니다.
http://blog.codeville.net/2008/08/05/adding-httpsssl-support-to-aspnet-mvc-routing/
Preview 5에 더 나은 / 업데이트 된 방법이 있습니까?,
내 ASP.NET MVC 기반 사이트의 일부 페이지에 HTTPS를 사용하려면 어떻게해야합니까?
Steve Sanderson은 Preview 4에서 DRY 방식으로이 작업을 수행하는 방법에 대한 꽤 좋은 튜토리얼을 제공합니다.
http://blog.codeville.net/2008/08/05/adding-httpsssl-support-to-aspnet-mvc-routing/
Preview 5에 더 나은 / 업데이트 된 방법이 있습니까?,
답변:
당신이 사용하는 경우 ASP.NET MVC 2 미리보기 2 이상을 , 당신은 지금 간단하게 사용할 수 있습니다 :
[RequireHttps]
public ActionResult Login()
{
return View();
}
으로 Amadiere 쓴 [RequireHttps]를위한 MVC 2에서 잘 작동 입력 HTTPS를. 하지만 일부 에만 HTTPS를 사용하려는 경우 말한 것처럼 페이지 MVC 2는 사용자를 사랑하지 않습니다. 일단 사용자를 HTTPS로 전환하면 수동으로 리디렉션 할 때까지 거기에 머물러 있습니다.
내가 사용한 접근 방식은 다른 사용자 지정 특성 인 [ExitHttpsIfNotRequired]를 사용하는 것입니다. 컨트롤러 또는 작업에 연결되면 다음과 같은 경우 HTTP로 리디렉션됩니다.
여기에 게시하기에는 너무 크지 만 여기에서 코드 와 몇 가지 추가 세부 정보를 볼 수 있습니다 .
다음은 Dan Wahlin의 최근 게시물입니다.
http://weblogs.asp.net/dwahlin/archive/2009/08/25/requiring-ssl-for-asp-net-mvc-controllers.aspx
그는 ActionFilter 속성을 사용합니다.
일부 ActionLink 확장 : http://www.squaredroot.com/post/2008/06/11/MVC-and-SSL.aspx 또는 https : // http://forums.asp.net으로 리디렉션되는 컨트롤러 작업 속성 /p/1260198/2358380.aspx#2358380
속성 지향 개발 접근 방식의 팬이 아닌 사람들을 위해 도움이 될 수있는 코드는 다음과 같습니다.
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);
}
}
속성을 피하는 데는 몇 가지 이유가 있으며 그 중 하나는 모든 보안 페이지 목록을 보려면 솔루션의 모든 컨트롤러를 건너 뛰어야하기 때문입니다.
나는이 질문을 건너서 내 솔루션이 누군가를 도울 수 있기를 바랍니다.
몇 가지 문제가 있습니다.- "계정"의 "로그온"과 같은 특정 작업을 보호해야합니다. 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의 수동 버전을 만들었습니다. http : // urls로 다시 리디렉션 할 수 있도록 원본 RequireSsl 소스 코드를 수정했습니다. 또한 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() { }
}
이제 각 cpntroller / action에서 SSL이 필요합니다.이 속성을 ConnectionProtocol.Https로 설정하기 만하면됩니다.
이제 URL로 이동하겠습니다. URL 라우팅 엔진에 문제가 거의 없습니다. http://blog.stevensanderson.com/2008/08/05/adding-httpsssl-support-to-aspnet-mvc-routing/ 에서 자세한 내용을 확인할 수 있습니다 . 이 게시물에서 제안한 솔루션은 이론적으로는 좋지만 오래되었고 접근 방식이 마음에 들지 않습니다.
내 솔루션은 다음과 같습니다. 기본 "Route"클래스의 하위 클래스를 만듭니다.
공용 클래스 AbsoluteUrlRoute : Route {#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
}
이 버전의 "Route"클래스는 절대 URL을 생성합니다. 여기에 블로그 게시물 작성자 제안이 뒤 따르는 트릭은 DataToken을 사용하여 스키마를 지정하는 것입니다 (끝의 예 :)).
이제 예를 들어 "Account / LogOn"경로에 대한 URL을 생성하면 "/ http://example.com/Account/LogOn "이 표시됩니다. 이는 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";
}
다음은 2009 년 1 월 Pablo M. Cibrano 가 HttpModule 및 확장 메서드를 포함한 몇 가지 기술을 모은 블로그 게시물 입니다.
다음 은 ActionFilter를 사용하는 Adam Salvo 의 블로그 게시물 입니다.
이것은 반드시 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의 답변 에서 언급했듯이)를 사용하려면 각 컨트롤러에서 속성 스타일을 사용하는 대신 (또는 모든 컨트롤러에서 상속 할 BaseController를 만드는 대신) Startup.cs에이를 추가 할 수 있습니다.
Startup.cs- 레지스터 필터 :
public void ConfigureServices(IServiceCollection services)
{
// TODO: Register other services
services.AddMvc(options =>
{
options.Filters.Add(typeof(RequireHttpsAttribute));
});
}
위의 접근 방식에 대한 디자인 결정에 대한 자세한 내용 은 RequireHttpsAttribute에 의해 처리되지 않도록 localhost 요청을 제외하는 방법에 대한 유사한 질문에 대한 내 답변을 참조하십시오 .
또는 Global.asax.cs에 필터를 추가합니다.
GlobalFilters.Filters.Add (new RequireHttpsAttribute ());
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
namespace xxxxxxxx
{
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
GlobalFilters.Filters.Add(new RequireHttpsAttribute());
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
}