Asp.net mvc 在ADAL OpenIDConnect中首次成功登录MVC.NET 5 OW后,第二次登录会导致无限重定向循环

Asp.net mvc 在ADAL OpenIDConnect中首次成功登录MVC.NET 5 OW后,第二次登录会导致无限重定向循环,asp.net-mvc,azure,asp.net-mvc-5,owin,openid-connect,Asp.net Mvc,Azure,Asp.net Mvc 5,Owin,Openid Connect,第一篇帖子,所以要温柔!:) 我正在为Office 365开发一个MVC.NET 5 web应用程序,并且正在使用OpenIDConnect框架。我已经设置了OWIN(3)和ADAL(2)以及我的Azure广告应用程序。没有用户操作的登录,主控制器附加了[Authorize]属性,强制立即登录重定向到Azure AD。我没有在任何Authorize属性中使用角色 问题:我可以成功登录到我的应用程序-一次!第一次登录后,我关闭浏览器(或在另一台机器上打开新浏览器),然后再次点击应用程序。它将我重定

第一篇帖子,所以要温柔!:)

我正在为Office 365开发一个MVC.NET 5 web应用程序,并且正在使用OpenIDConnect框架。我已经设置了OWIN(3)和ADAL(2)以及我的Azure广告应用程序。没有用户操作的登录,主控制器附加了[Authorize]属性,强制立即登录重定向到Azure AD。我没有在任何Authorize属性中使用角色

问题:我可以成功登录到我的应用程序-一次!第一次登录后,我关闭浏览器(或在另一台机器上打开新浏览器),然后再次点击应用程序。它将我重定向到我登录的Azure广告登录屏幕,然后它在应用程序和Azure之间不断重定向,直到我得到臭名昭著的400个长标题。看看饼干店,我发现里面全是即兴的。我检查缓存(Vittorio的EFADALCache配方,尽管发现此问题时我正在使用TokenCache.DefaultShared),它有数百行缓存数据(成功登录时仅生成一行)

我可以通过输出窗口看到重定向发生时,每次往返都会生成一个新的访问和刷新令牌:

Microsoft.IdentityModel.Clients.ActiveDirectory Verbose: 1 : 31/07/2015 12:31:52: 15ad306e-e26d-4827-98dc-dea75853788a - AcquireTokenByAuthorizationCodeHandler: Resource value in the token response was used for storing tokens in the cache
iisexpress.exe Information: 0 : 31/07/2015 12:31:52: 15ad306e-e26d-4827-98dc-dea75853788a - AcquireTokenByAuthorizationCodeHandler: Resource value in the token response was used for storing tokens in the cache
Microsoft.IdentityModel.Clients.ActiveDirectory Information: 2 : 31/07/2015 12:31:52:  - TokenCache: Deserialized 1 items to token cache.
iisexpress.exe Information: 0 : 31/07/2015 12:31:52:  - TokenCache: Deserialized 1 items to token cache.
Microsoft.IdentityModel.Clients.ActiveDirectory Verbose: 1 : 31/07/2015 12:31:52: 15ad306e-e26d-4827-98dc-dea75853788a - TokenCache: Storing token in the cache...
iisexpress.exe Information: 0 : 31/07/2015 12:31:52: 15ad306e-e26d-4827-98dc-dea75853788a - TokenCache: Storing token in the cache...
Microsoft.IdentityModel.Clients.ActiveDirectory Verbose: 1 : 31/07/2015 12:31:52: 15ad306e-e26d-4827-98dc-dea75853788a - TokenCache: An item was stored in the cache
iisexpress.exe Information: 0 : 31/07/2015 12:31:52: 15ad306e-e26d-4827-98dc-dea75853788a - TokenCache: An item was stored in the cache
Microsoft.IdentityModel.Clients.ActiveDirectory Information: 2 : 31/07/2015 12:31:52: 15ad306e-e26d-4827-98dc-dea75853788a - AcquireTokenHandlerBase: === Token Acquisition finished successfully. An access token was retuned:
    Access Token Hash: PN5HoBHPlhhHIf1lxZhEWb4B4Hli69UKgcle0w7ssvo=
    Refresh Token Hash: 3xmypXCO6MIMS9qUV+37uPD4kPip9WDH6Ex29GdWL88=
    Expiration Time: 31/07/2015 13:31:51 +00:00
    User Hash: GAWUtY8c4EKcJnsHrO6NOzwcQDMW64z5BNOvVIl1vAI=
当问题发生时,我的OpenIdConnectAuthenticationOptions中的AuthorizationCodeReceived通知将被命中,因此我知道Azure认为登录成功(否则将不会重定向回应用):

在发现问题后,我将Authorized属性替换为我自己的Auth属性,继承自AuthorizeAttribute,这样我就可以尝试进入Authorized代码并查看发生了什么。我根据MVC5的源代码版本5构建了一个PDB文件,但所发生的一切就是它跳回到我自己的代码中:(尽管如此,我已经覆盖了我可以覆盖的内容,并且发现,filterContext.HttpContext.User.Identity.IsAuthenticated为false,这是有意义的,因为这会导致重定向回Azure登录

因此,我知道:

  • Azure正在接受我的登录并返回相关令牌
  • 在第二次登录时,在进行身份验证之前,filterContext.HttpContext.User.Identity.IsAuthenticated返回false
  • 我的Azure应用程序配置很好,否则根本无法进行身份验证
我认为:

  • MVC身份设置中存在错误。Azure工作正常,或者根本无法进行身份验证
  • 这不是cookie问题,因为如果您在另一台机器上执行第二次登录,就会出现问题
很抱歉,这有点冗长,但有这么多的无限重定向问题,我需要解释为什么我的情况是不同的

我要寻找的(如果不是答案的话!)是如何进一步调试的正确方向

感谢你能给予的任何帮助


Andy

我没有确切描述问题,但在基于OpenId连接的登录过程中,我在我的开发机器上也有一个重定向循环

在我的例子中,这是cookies的一个简单错误。我是通过HTTP访问受保护的URL。请确保您是通过HTTPS访问依赖方的受保护URL

一旦您通过身份验证,身份验证cookie将仅通过HTTPS发送,这意味着当您通过HTTP访问受保护的URL时,浏览器不会随请求发送身份验证cookie,因此服务器会将您视为未经身份验证。此时,服务器会将您重定向到身份验证服务器(您已在其中登录).Auth服务器会将您重定向回原始url,从而确保重定向循环


在您的部署中不应出现这种情况,因为如果您具有身份验证等功能,则应始终在应用程序中使用所有SSL。这降低了会话劫持的风险。

已经为任何感兴趣的人找到了答案。这是Katana中的一个已知错误,Katana cookie manager和ASP.NET cookie manager冲突并覆盖这是彼此的cookies。详细信息和解决方法如下:

下面显示的SystemWebCookieManager现在可以在Nuget包中找到

添加CodePlex死亡时的代码:

//stick this in public void ConfigureAuth(IAppBuilder app)
  app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                // ...
                CookieManager = new SystemWebCookieManager()
            });

//And create this class elsewhere:
public class SystemWebCookieManager : ICookieManager
    {
        public string GetRequestCookie(IOwinContext context, string key)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            var webContext = context.Get<HttpContextBase>(typeof(HttpContextBase).FullName);
            var cookie = webContext.Request.Cookies[key];
            return cookie == null ? null : cookie.Value;
        }

        public void AppendResponseCookie(IOwinContext context, string key, string value, CookieOptions options)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }
            if (options == null)
            {
                throw new ArgumentNullException("options");
            }

            var webContext = context.Get<HttpContextBase>(typeof(HttpContextBase).FullName);

            bool domainHasValue = !string.IsNullOrEmpty(options.Domain);
            bool pathHasValue = !string.IsNullOrEmpty(options.Path);
            bool expiresHasValue = options.Expires.HasValue;

            var cookie = new HttpCookie(key, value);
            if (domainHasValue)
            {
                cookie.Domain = options.Domain;
            }
            if (pathHasValue)
            {
                cookie.Path = options.Path;
            }
            if (expiresHasValue)
            {
                cookie.Expires = options.Expires.Value;
            }
            if (options.Secure)
            {
                cookie.Secure = true;
            }
            if (options.HttpOnly)
            {
                cookie.HttpOnly = true;
            }

            webContext.Response.AppendCookie(cookie);
        }

        public void DeleteCookie(IOwinContext context, string key, CookieOptions options)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }
            if (options == null)
            {
                throw new ArgumentNullException("options");
            }

            AppendResponseCookie(
                context,
                key,
                string.Empty,
                new CookieOptions
                {
                    Path = options.Path,
                    Domain = options.Domain,
                    Expires = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc),
                });
        }
    }
//将其粘贴到public void ConfigureAuth(IAppBuilder应用程序)中
app.UseCookieAuthentication(新的CookieAuthenticationOptions
{
// ...
CookieManager=新系统WebCookieManager()
});
//并在其他位置创建此类:
公共类SystemWebCookieManager:ICookieManager
{
公共字符串GetRequestCookie(IOwinContext上下文,字符串键)
{
if(上下文==null)
{
抛出新的ArgumentNullException(“上下文”);
}
var webContext=context.Get(typeof(HttpContextBase.FullName);
var cookie=webContext.Request.Cookies[key];
返回cookie==null?null:cookie.Value;
}
public void AppendResponseCookie(IOwinContext上下文、字符串键、字符串值、CookieOptions)
{
if(上下文==null)
{
抛出新的ArgumentNullException(“上下文”);
}
如果(选项==null)
{
抛出新的ArgumentNullException(“选项”);
}
var webContext=context.Get(typeof(HttpContextBase.FullName);
bool domainHasValue=!string.IsNullOrEmpty(options.Domain);
bool pathHasValue=!string.IsNullOrEmpty(options.Path);
bool expiresHasValue=options.Expires.HasValue;
var cookie=新的HttpCookie(键,值);
if(domainHasValue)
{
cookie.Domain=options.Domain;
}
if(路径值)
{
饼干,帕特
//stick this in public void ConfigureAuth(IAppBuilder app)
  app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                // ...
                CookieManager = new SystemWebCookieManager()
            });

//And create this class elsewhere:
public class SystemWebCookieManager : ICookieManager
    {
        public string GetRequestCookie(IOwinContext context, string key)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            var webContext = context.Get<HttpContextBase>(typeof(HttpContextBase).FullName);
            var cookie = webContext.Request.Cookies[key];
            return cookie == null ? null : cookie.Value;
        }

        public void AppendResponseCookie(IOwinContext context, string key, string value, CookieOptions options)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }
            if (options == null)
            {
                throw new ArgumentNullException("options");
            }

            var webContext = context.Get<HttpContextBase>(typeof(HttpContextBase).FullName);

            bool domainHasValue = !string.IsNullOrEmpty(options.Domain);
            bool pathHasValue = !string.IsNullOrEmpty(options.Path);
            bool expiresHasValue = options.Expires.HasValue;

            var cookie = new HttpCookie(key, value);
            if (domainHasValue)
            {
                cookie.Domain = options.Domain;
            }
            if (pathHasValue)
            {
                cookie.Path = options.Path;
            }
            if (expiresHasValue)
            {
                cookie.Expires = options.Expires.Value;
            }
            if (options.Secure)
            {
                cookie.Secure = true;
            }
            if (options.HttpOnly)
            {
                cookie.HttpOnly = true;
            }

            webContext.Response.AppendCookie(cookie);
        }

        public void DeleteCookie(IOwinContext context, string key, CookieOptions options)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }
            if (options == null)
            {
                throw new ArgumentNullException("options");
            }

            AppendResponseCookie(
                context,
                key,
                string.Empty,
                new CookieOptions
                {
                    Path = options.Path,
                    Domain = options.Domain,
                    Expires = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc),
                });
        }
    }
  protected void Session_Start(object sender, EventArgs e)
        {
            // event is raised each time a new session is created     
        }

  protected void Session_End(object sender, EventArgs e)
        {
            // event is raised when a session is abandoned or expires

        }
  app.UseCookieAuthentication(new CookieAuthenticationOptions
  {
      AuthenticationType = "Cookies",
      CookieManager = new Microsoft.Owin.Host.SystemWeb.SystemWebChunkingCookieManager()
  });
protected void Session_Start(object sender, EventArgs e)
    {
        // event is raised each time a new session is created     
    }



protected void Session_End(object sender, EventArgs e)
    {
        // event is raised when a session is abandoned or expires

    }
  app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = "Cookies",
            CookieManager = new Microsoft.Owin.Host.SystemWeb.SystemWebChunkingCookieManager()
        });