0

我正在将我们的 asp.net MVC 应用程序与 SAML2 身份验证集成。并使用Kentor.AuthServices作为模块,如kentor.AuthServices 配置中所述

一切正常。但下一步是仅针对指定范围的页面添加第二个服务提供者(配置为在服务器端使用另一个身份验证机制)的使用。

首先,如何通过 web.config 配置它以添加具有不同entityId的第二个 SP(不是第一个 SP 范围内的第二个 IdP) 。

其次,如何以编程方式切换到第二个 SP?我假设它应该发生在方法Application_BeginRequest的global.asax文件中,但是如何?

4

2 回答 2

0

自答。

这是针对 MVC 或 HttpModule 包的多个 SP 的解决方法,切换基于指定的 URL 范围。在我的情况下,不同的 SP 实现不同数量的安全因素。

首先,实现自定义 IOptions 和 CookieHandler,能够切换到正确的实例。在 web.config 文件中,必须定义两个 kentor.authServices 部分。在我的情况下,只有“entityId”属性不同。

    public class CustomOptions : IOptions
    {
        private IOptions options1Factor;
        private IOptions options2Factor;
        private Func<bool> _checkIsSecure;

        public CustomOptions(Func<bool> checkIsSecure)
        {
            _checkIsSecure = checkIsSecure;

            AddOption(out options2Factor, "kentor.authServices1");
            AddOption(out options1Factor, "kentor.authServices");
        }

        private void AddOption(out IOptions options, string sectionName)
        {
            var sp = new SPOptions((KentorAuthServicesSection)ConfigurationManager.GetSection(sectionName));
            options = new Options(sp);
            KentorAuthServicesSection.Current.IdentityProviders.RegisterIdentityProviders(options);
            KentorAuthServicesSection.Current.Federations.RegisterFederations(options);
        }

        public SPOptions SPOptions
        {
            get
            {
                if (_checkIsSecure())
                    return options2Factor.SPOptions;

                return options1Factor.SPOptions;
            }
        }

        public IdentityProviderDictionary IdentityProviders
        {
            get
            {
                if (_checkIsSecure())
                    return options2Factor.IdentityProviders;

                return options1Factor.IdentityProviders;
            }

        }

        public KentorAuthServicesNotifications Notifications
        {
            get
            {
                if (_checkIsSecure())
                    return options2Factor.Notifications;

                return options1Factor.Notifications;
            }
        }
    }

    public class CustomCookieHandler : CookieHandler
    {
        private Func<bool> _checkIsSecure;
        private CookieHandler _originalCookieHandler1Factor;
        private CookieHandler _originalCookieHandler2Factor;

        public CustomCookieHandler(Func<bool> checkIsSecure)
        {
            _checkIsSecure = checkIsSecure;
            _originalCookieHandler1Factor = new ChunkedCookieHandler()
            {
                Name = "commonAuth",
                RequireSsl = false
            };

            _originalCookieHandler2Factor = new ChunkedCookieHandler()
            {
                Name = "securedAuth",
                RequireSsl = false
            };
        }

        public override string MatchCookiePath(Uri baseUri, Uri targetUri)
        {
            if (_checkIsSecure())
                return _originalCookieHandler2Factor.MatchCookiePath(baseUri, targetUri);

            return _originalCookieHandler1Factor.MatchCookiePath(baseUri, targetUri);
        }

        protected override void DeleteCore(string name, string path, string domain, HttpContext context)
        {
            if (_checkIsSecure())
                _originalCookieHandler2Factor.Delete();
            else
                _originalCookieHandler1Factor.Delete();
        }

        protected override byte[] ReadCore(string name, HttpContext context)
        {
            if (_checkIsSecure())
                return _originalCookieHandler2Factor.Read();

            return _originalCookieHandler1Factor.Read();
        }

        protected override void WriteCore(byte[] value, string name, string path, string domain, DateTime expirationTime, bool secure, bool httpOnly, HttpContext context)
        {
            if (_checkIsSecure())
                _originalCookieHandler2Factor.Write(value, true, expirationTime); 
            else
                _originalCookieHandler1Factor.Write(value, true, expirationTime);
        }
    }

在 Global.asax 文件中将静态属性设置为自定义实现。无需更多修改。

    protected void Application_Start()
    {
        FederatedAuthentication.FederationConfiguration.CookieHandler = new CustomCookieHandler(CheckIsSecure);
        Kentor.AuthServices.Mvc.AuthServicesController.Options = new CustomOptions(CheckIsSecure);
    }

    private bool CheckIsSecure()
    {
        if (HttpContext.Current == null)
            return false;

        var mainHost = "http://host.local"; // host url 
        var sp = new [] { "/Home/Secure" }; // array of URLs which must be secured with other SP
        var request = HttpContext.Current.Request;
        var isSecured = sp.Any(x => x.Equals(request.Path, StringComparison.InvariantCultureIgnoreCase));

        if (!isSecured && request.Path.Equals("/AuthServices/SignIn", StringComparison.InvariantCultureIgnoreCase))
        {
            var returnUrl = request.QueryString["ReturnUrl"];
            isSecured = !string.IsNullOrEmpty(returnUrl) &&
                        sp.Any(x => x.Equals(returnUrl, StringComparison.InvariantCultureIgnoreCase));
        }

        if (!isSecured && request.Path.Equals("/AuthServices/Acs", StringComparison.InvariantCultureIgnoreCase))
        {
            var _r = new HttpRequestWrapper(request).ToHttpRequestData();
            isSecured = _r != null && _r.StoredRequestState != null && _r.StoredRequestState.ReturnUrl != null
                        && sp.Any(x => x.Equals(_r.StoredRequestState.ReturnUrl.ToString(),
                            StringComparison.InvariantCultureIgnoreCase));
        }

        if (!isSecured && !string.IsNullOrEmpty(request.Headers["Referer"]))
        {
            var referer = request.Headers["Referer"];
            isSecured = sp
                .Select(x => string.Format("{0}/{1}", mainHost.TrimEnd('/'), x.TrimStart('/')))
                .Any(x => x.Equals(referer, StringComparison.InvariantCultureIgnoreCase));
        }

        return isSecured;
    }
于 2017-09-28T15:21:40.993 回答
0

在同一个应用程序中使用两个不同的 SP 实例是一种非常罕见的情况。但是如果你真的确定你需要它,它是可以实现的。

您将不得不使用 Kentor.AuthServices.Owin 包并在代码中进行配置 - web.config 不会这样做。注册中间件的两个实例。每个人都有自己的配置,包括自己的 SP EntityID。还要确保更改其中ModulePath至少一个的,以便它们获得不同的端点地址。

要挑战任一身份验证,请在挑战中设置正确的身份验证方案(通常在ChallengeResult从控制器返回的)

于 2017-09-22T07:20:29.837 回答