Tôi đã lướt qua câu hỏi này và hy vọng giải pháp của tôi có thể giúp ích cho ai đó.
Chúng tôi gặp một số vấn đề: - Chúng tôi cần bảo mật các hành động cụ thể, ví dụ: "Đăng nhập" trong "Tài khoản". Chúng ta có thể sử dụng bản dựng trong thuộc tính RequestHttps, điều này thật tuyệt - nhưng nó sẽ chuyển hướng chúng ta trở lại bằng https: //. - Chúng ta nên làm cho các liên kết, biểu mẫu và những thứ như vậy "nhận biết SSL".
Nói chung, giải pháp của tôi cho phép chỉ định các tuyến sẽ sử dụng url tuyệt đối, ngoài khả năng chỉ định giao thức. Bạn có thể sử dụng phương pháp này để chỉ định giao thức "https".
Vì vậy, trước tiên tôi đã tạo một enum ConnectionProtocol:
public enum ConnectionProtocol
{
Ignore,
Http,
Https
}
Bây giờ, tôi đã tạo phiên bản cuộn thủ công của RequestSsl. Tôi đã sửa đổi mã nguồn RequestSsl ban đầu để cho phép chuyển hướng trở lại http: // urls. Ngoài ra, tôi đã đặt một trường cho phép chúng tôi xác định xem chúng tôi có nên yêu cầu SSL hay không (tôi đang sử dụng nó với bộ xử lý trước DEBUG).
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public sealed class RequireHttpsAttribute : FilterAttribute, IAuthorizationFilter
{
public RequireHttpsAttribute()
{
Protocol = ConnectionProtocol.Ignore;
}
public ConnectionProtocol Protocol { get; set; }
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");
}
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)
{
if (!String.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException("The requested resource can only be accessed via SSL.");
}
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.");
}
string url = "http://" + filterContext.HttpContext.Request.Url.Host + filterContext.HttpContext.Request.RawUrl;
filterContext.Result = new RedirectResult(url);
}
}
Bây giờ, RequestSsl này sẽ thực hiện cơ sở sau dựa trên giá trị thuộc tính Yêu cầu của bạn: - Bỏ qua: Sẽ không làm gì cả. - Http: Sẽ buộc chuyển hướng sang giao thức http. - Https: Sẽ buộc chuyển hướng sang giao thức https.
Bạn nên tạo bộ điều khiển cơ sở của riêng mình và đặt thuộc tính này thành Http.
[RequireSsl(Requirement = ConnectionProtocol.Http)]
public class MyController : Controller
{
public MyController() { }
}
Bây giờ, trong mỗi cpntroller / action mà bạn muốn yêu cầu SSL - chỉ cần đặt thuộc tính này với ConnectionProtocol.Https.
Bây giờ, hãy chuyển đến URL: Chúng tôi gặp một số vấn đề với công cụ định tuyến url. Bạn có thể đọc thêm về chúng tại http://blog.stevensanderson.com/2008/08/05/adding-httpsssl-support-to-aspnet-mvc-routing/ . Giải pháp được đề xuất trong bài đăng này về mặt lý thuyết là tốt, nhưng đã cũ và tôi không thích cách tiếp cận.
Giải pháp của tôi là như sau: Tạo một lớp con của lớp "Tuyến đường" cơ bản:
public class AbsoluteUrlRoute: Route {#region ctor
public AbsoluteUrlRoute(string url, IRouteHandler routeHandler)
: base(url, routeHandler)
{
}
public AbsoluteUrlRoute(string url, RouteValueDictionary defaults, IRouteHandler routeHandler)
: base(url, defaults, routeHandler)
{
}
public AbsoluteUrlRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints,
IRouteHandler routeHandler)
: base(url, defaults, constraints, routeHandler)
{
}
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
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
}
Phiên bản này của lớp "Tuyến đường" sẽ tạo url tuyệt đối. Mẹo ở đây, tiếp theo là gợi ý của tác giả bài đăng trên blog, là sử dụng DataToken để chỉ định lược đồ (ví dụ ở cuối :)).
Bây giờ, nếu chúng tôi tạo một url, ví dụ: đối với tuyến đường "Account / LogOn", chúng tôi sẽ lấy "/ http://example.com/Account/LogOn " - đó là vì UrlRoutingModule xem tất cả các url là tương đối. Chúng tôi có thể khắc phục điều đó bằng cách sử dụng HttpModule tùy chỉnh:
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);
}
}
}
}
Vì mô-đun này đang ghi đè việc triển khai cơ sở của UrlRoutingModule, chúng tôi nên xóa httpModule cơ sở và đăng ký của chúng tôi trong web.config. Vì vậy, trong "system.web" đặt:
<httpModules>
<remove name="UrlRoutingModule-4.0" />
<add name="UrlRoutingModule-4.0" type="MyApp.Web.Mvc.Routing.AbsoluteUrlRoutingModule" />
</httpModules>
Đó là nó :).
Để đăng ký một lộ trình tuyệt đối / giao thức được tuân theo, bạn nên làm:
routes.Add(new AbsoluteUrlRoute("Account/LogOn", new MvcRouteHandler())
{
Defaults = new RouteValueDictionary(new {controller = "Account", action = "LogOn", area = ""}),
DataTokens = new RouteValueDictionary(new {scheme = "https"})
});
Sẽ thích nghe phản hồi + cải tiến của bạn. Hy vọng nó có thể giúp đỡ! :)
Chỉnh sửa: Tôi quên bao gồm phương thức mở rộng IsCurrentConnectionSecured () (quá nhiều đoạn mã: P). Đây là một phương thức mở rộng thường sử dụng Request.IsSecuredConnection. Tuy nhiên, cách tiếp cận này sẽ không hoạt động khi sử dụng cân bằng tải - vì vậy phương pháp này có thể bỏ qua điều này (lấy từ nopCommerce).
public static bool IsCurrentConnectionSecured(this HttpRequestBase request)
{
return request != null && request.IsSecureConnection;
}