Các trang SSL trong ASP.NET MVC


80

Làm cách nào để sử dụng HTTPS cho một số trang trong trang web dựa trên ASP.NET MVC của tôi?

Steve Sanderson có một hướng dẫn khá hay về cách thực hiện điều này theo cách KHÔ trên Bản xem trước 4 tại:

http://blog.codeville.net/2008/08/05/adding-httpsssl-support-to-aspnet-mvc-routing/

Có cách nào tốt hơn / được cập nhật với Xem trước 5 không ?,


3
Cái này rất có niên đại. Đối với MVC4 và cao hơn, nhìn thấy bài đăng blog của tôi blogs.msdn.com/b/rickandy/archive/2012/03/23/...
RickAndMSFT

Câu trả lời:


92

Nếu bạn đang sử dụng ASP.NET MVC 2 Preview 2 trở lên , bây giờ bạn có thể chỉ cần sử dụng:

[RequireHttps]
public ActionResult Login()
{
   return View();
}

Tuy nhiên, tham số thứ tự đáng chú ý, như đã đề cập ở đây .


23
Bạn cũng có thể làm điều này ở cấp độ bộ điều khiển. Tốt hơn, nếu bạn muốn toàn bộ ứng dụng là SSL, bạn có thể tạo bộ điều khiển cơ sở, mở rộng nó cho tất cả bộ điều khiển và áp dụng thuộc tính ở đó.
tro999,

22
Ngoài ra, bạn có thể thêm nó là một bộ lọc toàn cục MVC3 trong Global.asax GlobalFilters.Filters.Add (mới RequestHttpsAttribute ());
GraemeMiller

2
Không có nhà phát triển nào được đảm bảo sẽ sử dụng bộ điều khiển có nguồn gốc của bạn. Bạn có thể thực hiện một cuộc gọi để buộc HTTPS - xem bài đăng blog của tôi blogs.msdn.com/b/rickandy/archive/2012/03/23/...
RickAndMSFT

17

MVCFutures có thuộc tính 'RequestSSL'.

(cảm ơn Adam đã chỉ ra điều đó trong bài đăng blog cập nhật của bạn)

Chỉ cần áp dụng nó vào phương thức hành động của bạn, với 'Redirect = true' nếu bạn muốn yêu cầu http: // tự động trở thành https: //:

    [RequireSsl(Redirect = true)]

Xem thêm: ASP.NET MVC RequestHttps in Production Only


Tôi có phải phân lớp nó để xử lý các yêu cầu localhost không?
Mr Rogers

một cách là tạo chứng chỉ cho máy cục bộ của bạn và sử dụng chứng chỉ đó. Tôi nghĩ rằng để vô hiệu hóa hoàn toàn nó cho localhost, bạn thực sự sẽ cần phân lớp hoặc sao chép mã. không chắc chắn về cách tiếp cận đề nghị là
Simon_Weaver

1
Có vẻ như nó đã được niêm phong nên tôi cần sao chép mã. Bummer. Chứng chỉ cho máy cục bộ sẽ chỉ hoạt động trong IIS, chứ không phải máy chủ web dev.
Mr Rogers

@mr rogers - hãy xem điều này: stackoverflow.com/questions/1639707/…
Simon_Weaver

Đang cập nhật này để MVC4 + xem blog bài viết của tôi blogs.msdn.com/b/rickandy/archive/2012/03/23/...
RickAndMSFT

9

Như Amadiere đã viết , [RequestHttps] hoạt động tốt trong MVC 2 để vào HTTPS. Nhưng nếu bạn chỉ muốn sử dụng HTTPS cho một số trang như bạn đã nói, MVC 2 không mang lại cho bạn bất kỳ tình yêu nào - một khi nó chuyển người dùng sang HTTPS, họ sẽ bị mắc kẹt ở đó cho đến khi bạn chuyển hướng họ theo cách thủ công.

Cách tiếp cận mà tôi đã sử dụng là sử dụng một thuộc tính tùy chỉnh khác, [ExitHttpsIfNotRequired]. Khi được gắn vào bộ điều khiển hoặc hành động, hành động này sẽ chuyển hướng đến HTTP nếu:

  1. Yêu cầu là HTTPS
  2. Thuộc tính [RequestHttps] không được áp dụng cho hành động (hoặc bộ điều khiển)
  3. Yêu cầu là GET (chuyển hướng ĐĂNG sẽ dẫn đến tất cả các loại rắc rối).

Nó hơi quá lớn để đăng ở đây, nhưng bạn có thể xem mã ở đây cùng với một số chi tiết bổ sung.


AllowAnonymous sửa lỗi đó. Đối với MVC4 và cao hơn, nhìn thấy bài đăng blog của tôi blogs.msdn.com/b/rickandy/archive/2012/03/23/...
RickAndMSFT

8

Đây là một bài đăng gần đây của Dan Wahlin về điều này:

http://weblogs.asp.net/dwahlin/archive/2009/08/25/requiring-ssl-for-asp-net-mvc-controllers.aspx

Anh ấy sử dụng Thuộc tính ActionFilter.


2
Đây có vẻ là cách tốt nhất vào lúc này.
royco

1 năm sau khi cuộc gọi isLocal giúp tôi giải quyết một vấn đề mà đã trở thành một thực tại đau @@@
Heisenberg

1
Trên đây là ngày, Đối MVC4 và cao hơn, nhìn thấy bài đăng blog của tôi blogs.msdn.com/b/rickandy/archive/2012/03/23/...
RickAndMSFT


3

Đối với những người không yêu thích phương pháp tiếp cận phát triển theo hướng thuộc tính, đây là một đoạn mã có thể giúp:

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);
    }
}

Có một số lý do để tránh các thuộc tính và một trong số đó là nếu bạn muốn xem danh sách tất cả các trang được bảo mật, bạn sẽ phải chuyển qua tất cả các bộ điều khiển trong giải pháp.


Tôi nghĩ rằng hầu hết mọi người sẽ không đồng ý với bạn một ngày này, mặc dù cung cấp một cách thay thế luôn luôn là hữu ích ...
Serj Sagan

2

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:

/// <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
}

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).

/* 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);
    }
}

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

    /// <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
}

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>
  <!-- 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>

Đó 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).

    /// <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";
    }




0

MVC 6 (ASP.NET Core 1.0) đang hoạt động hơi khác với Startup.cs.

Để sử dụng RequestHttpsAttribute (như được đề cập trong câu trả lời của Amadiere) trên tất cả các trang, bạn có thể thêm cái này trong Startup.cs thay vì sử dụng kiểu thuộc tính trên mỗi bộ điều khiển (hoặc thay vì tạo BaseController cho tất cả bộ điều khiển của bạn kế thừa).

Startup.cs - bộ lọc đăng ký:

public void ConfigureServices(IServiceCollection services)
{
    // TODO: Register other services

    services.AddMvc(options =>
    {
        options.Filters.Add(typeof(RequireHttpsAttribute));
    });
}

Để biết thêm thông tin về các quyết định thiết kế cho cách tiếp cận trên, hãy xem câu trả lời của tôi cho câu hỏi tương tự về cách loại trừ các yêu cầu localhost khỏi được xử lý bởi RequestHttpsAttribute .


0

Hoặc thêm bộ lọc vào Global.asax.cs

GlobalFilters.Filters.Add (mới RequestHttpsAttribute ());

Lớp RequestHttpsAttribute

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);
        }
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.