为什么当从外部源(即 Excel、Word 等...)单击链接时,cookie 无法识别

发布于 2024-08-29 11:42:04 字数 237 浏览 2 评论 0原文

我注意到,当从 Web 浏览器外部(例如从 Excel 或 Word)单击链接时,即使该链接在同一浏览器窗口的新选项卡中打开,我的会话 cookie 最初也无法识别。

浏览器最终会识别出它的 cookie,但我很困惑为什么来自 Excel 或 Word 的初始链接不起作用。为了使其更具挑战性,在 Outlook 中单击链接效果很好。

有谁知道为什么会发生这种情况?我正在使用 Zend Framework 和 PHP 5.3。

I noticed that when a link is clicked externally from the web browser, such as from Excel or Word, that my session cookie is initially unrecognized, even if the link opens up in a new tab of the same browser window.

The browser ends up recognizing its cookie eventually, but I am puzzled as to why that initial link from Excel or Word doesn't work. To make it even more challenging, clicking a link works fine from Outlook.

Does anybody know why this might be happening? I'm using the Zend Framework with PHP 5.3.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(18

行至春深 2024-09-05 11:42:04

这是因为 MS Office 使用 Hlink.dll 组件来查找链接是否是 Office 文档或其他内容。 MS Office 希望在不借助外部浏览器(使用 IE6 的 Hlink.dll 组件)的情况下打开文档内链接的文档。

如果会话 cookie 保护网站,Hlink 自然会被重定向到登录页面并到达 HTML 页面,但无法“理解”它在外部浏览器中打开它。请注意,它打开的不是原始 URL(预期行为),而是重定向的结果,即使它是 302 重定向。

Microsoft 在不受支持的组件 (Hlink.dll) 中存在该错误,但他们将其交给我们,而不是识别该错误 (试图让我们相信这是我们使用的 SSO 系统(即会话 cookie)的缺陷)并拒绝升级它。它提供关闭 MS Office 查找功能的解决方法:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\
  Office\9.0\Common\Internet\ForceShellExecute:DWORD=1

或者向我们提供服务器端的解决方法,以避免 HTTP 重定向并更改为 Javascript 重定向或 META REFRESH 重定向(即让 Hlink 获取原始 URL 上的文本/html 页面并使其运行外部浏览器来处理它)。

This is because MS Office is using Hlink.dll component to lookup if the link is Office document or something else. MS Office expect to open the document linked within documents without the aid of external browser (using Hlink.dll component of IE6).

If session cookie protects website Hlink naturally is being redirected to login page and having reached HTML page and not able to "understand" it opens it in external browser. Note that it opens not original URL (expected behavior) but the result of redirect, even if it was 302 redirect.

Microsoft has that bug in unsupported component (Hlink.dll), instead of recognizing the bug they turn it over to our head (trying to convince us that it is flaw of SSO system we use, i.e. session cookies) and refuses to upgrade it. It offers workaround that turns off the lookup functionality of MS Office:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\
  Office\9.0\Common\Internet\ForceShellExecute:DWORD=1

Or offer us to workaround serverside, to avoid HTTP redirects and change into Javascript redirects or META REFRESH redirects (i.e. to have Hlink get text/html page on original URL and make it run external browser to handle it).

堇色安年 2024-09-05 11:42:04

服务器端这对我在 IIS 中有用(使用重写规则)

<rule name="WordBypass" enabled="true" stopProcessing="true">
    <match url=".*" />
    <conditions>
        <add input="{HTTP_USER_AGENT}" pattern="Word|Excel|PowerPoint|ms-office" />
    </conditions>
    <action type="CustomResponse" statusCode="200" statusReason="Refresh" statusDescription="Refresh" />
</rule>

Server side this worked for me in IIS (using a rewrite rule)

<rule name="WordBypass" enabled="true" stopProcessing="true">
    <match url=".*" />
    <conditions>
        <add input="{HTTP_USER_AGENT}" pattern="Word|Excel|PowerPoint|ms-office" />
    </conditions>
    <action type="CustomResponse" statusCode="200" statusReason="Refresh" statusDescription="Refresh" />
</rule>
烟织青萝梦 2024-09-05 11:42:04

我们遇到了同样的问题,并编写了一个开源 gem 来帮助那些使用 Rails 的人:https://github.com/spilliton/fix_microsoft_links

不过,您可以使用我们在任何框架上使用的相同方法:

  1. 检测用户代理是否来自 Microsoft 产品
  2. 使用元刷新标记渲染空白 html 页面,这将导致浏览器使用正确的 cookie 刷新页面

示例代码如下: https://github.com/spilliton/fix_microsoft_links/blob/master/lib/fix_microsoft_links.rb< /a>

We had this same problem and wrote an open source gem to help those using rails: https://github.com/spilliton/fix_microsoft_links

You can use the same approach we used on any framework though:

  1. Detect if the user agent is from a Microsoft product
  2. Render a blank html page with a meta refresh tag that will cause the browser to refresh the page with the correct cookies

Example code here: https://github.com/spilliton/fix_microsoft_links/blob/master/lib/fix_microsoft_links.rb

孤君无依 2024-09-05 11:42:04

PHP 解决方案:

这会阻止 MS 产品识别重定向。因此,MS 从所需的链接启动浏览器。

if (isset($_SERVER['HTTP_USER_AGENT']))
{
    $http_user_agent = $_SERVER['HTTP_USER_AGENT']; 
    if (preg_match('/Word|Excel|PowerPoint|ms-office/i', $http_user_agent)) 
    {
        // Prevent MS office products detecting the upcoming re-direct .. forces them to launch the browser to this link
        die();
    }
}

..在此代码后重定向

PHP solution:

This prevents the MS product recognising the redirect. MS therefore launches a browser from the required link.

if (isset($_SERVER['HTTP_USER_AGENT']))
{
    $http_user_agent = $_SERVER['HTTP_USER_AGENT']; 
    if (preg_match('/Word|Excel|PowerPoint|ms-office/i', $http_user_agent)) 
    {
        // Prevent MS office products detecting the upcoming re-direct .. forces them to launch the browser to this link
        die();
    }
}

.. redirect after this code

绮筵 2024-09-05 11:42:04

VB.NET 的修复:

Dim userAgent As String = System.Web.HttpContext.Current.Request.UserAgent

If userAgent.Contains("Word") Or userAgent.Contains("Excel") Or userAgent.Contains("PowerPoint") Or userAgent.Contains("ms-office") Then
       System.Web.HttpContext.Current.Response.Clear()
       System.Web.HttpContext.Current.Response.Write("<html><head><meta http-equiv='refresh' content='0'/></head><body></body></html>")
       System.Web.HttpContext.Current.Response.End()
End If

它基本上强制浏览器刷新页面,因此请求带有浏览器的用户代理和所有正确的 cookie。

Fix for VB.NET:

Dim userAgent As String = System.Web.HttpContext.Current.Request.UserAgent

If userAgent.Contains("Word") Or userAgent.Contains("Excel") Or userAgent.Contains("PowerPoint") Or userAgent.Contains("ms-office") Then
       System.Web.HttpContext.Current.Response.Clear()
       System.Web.HttpContext.Current.Response.Write("<html><head><meta http-equiv='refresh' content='0'/></head><body></body></html>")
       System.Web.HttpContext.Current.Response.End()
End If

It basically forces the browser to refresh the page, so the request comes in with the user agent of the browser and all the correct cookies.

月牙弯弯 2024-09-05 11:42:04

这是基于上面 spilliton 的答案的 C# ASP.NET 解决方案。在 Global.asax.cs 中,添加以下内容:

    private static string MSUserAgentsRegex = @"[^\w](Word|Excel|PowerPoint|ms-office)([^\w]|\z)";
    protected void Application_OnPostAuthenticateRequest(object sender, EventArgs e)
    {
        if (System.Text.RegularExpressions.Regex.IsMatch(Request.UserAgent, MSUserAgentsRegex))
        {
            Response.Write("<html><head><meta http-equiv='refresh' content='0'/></head><body></body></html>");
            Response.End();
        }
    }

Here is a solution for C# ASP.NET based on spilliton's answer above. In Global.asax.cs, add the following:

    private static string MSUserAgentsRegex = @"[^\w](Word|Excel|PowerPoint|ms-office)([^\w]|\z)";
    protected void Application_OnPostAuthenticateRequest(object sender, EventArgs e)
    {
        if (System.Text.RegularExpressions.Regex.IsMatch(Request.UserAgent, MSUserAgentsRegex))
        {
            Response.Write("<html><head><meta http-equiv='refresh' content='0'/></head><body></body></html>");
            Response.End();
        }
    }
别靠近我心 2024-09-05 11:42:04

以下是使用 dotnet core 中间件进行修复的示例:

public class MicrosoftOfficeLinksHandlingMiddleware
{
    private static readonly Regex MsUserAgentsRegex = new Regex(@"[^\w](Word|Excel|PowerPoint|ms-office)([^\w]|\z)");
    private readonly RequestDelegate _next;

    public MicrosoftOfficeLinksHandlingMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        string userAgent = context.Request.Headers["User-Agent"].FirstOrDefault();

        if (userAgent != null && MsUserAgentsRegex.IsMatch(userAgent))
        {
            // just return an empty response to the office agent
            return;
        }

        await _next(context);
    }
}

Here is an example of the fix using a dotnet core middleware:

public class MicrosoftOfficeLinksHandlingMiddleware
{
    private static readonly Regex MsUserAgentsRegex = new Regex(@"[^\w](Word|Excel|PowerPoint|ms-office)([^\w]|\z)");
    private readonly RequestDelegate _next;

    public MicrosoftOfficeLinksHandlingMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        string userAgent = context.Request.Headers["User-Agent"].FirstOrDefault();

        if (userAgent != null && MsUserAgentsRegex.IsMatch(userAgent))
        {
            // just return an empty response to the office agent
            return;
        }

        await _next(context);
    }
}
尘曦 2024-09-05 11:42:04

1.从excel/word指向http://example.com/from_excel.php

2.在“ from_excel.php”重定向到您使用会话的页面

<script>document.location.href = "http://example.com/page_with_session.php"; </script>

1.From excel/word point to http://example.com/from_excel.php

2.In "from_excel.php" redirect to page where you use session

<script>document.location.href = "http://example.com/page_with_session.php"; </script>
你没皮卡萌 2024-09-05 11:42:04

以下是如何通过过滤器使用 Java 和 Spring 解决此问题:

/**
 * To see why this is necessary, check out this page:
 * https://support.microsoft.com/en-gb/help/899927.
 */
public class MicrosoftFilter extends OncePerRequestFilter {
  @Override
  protected void doFilterInternal(final HttpServletRequest request,
      final HttpServletResponse response,
      final FilterChain filterChain) throws ServletException, IOException {
    //Serve up a blank page to anything with a Microsoft Office user agent, forcing it to open the
    //URL in a browser instead of trying to pre-fetch it, getting redirected to SSO, and losing
    //the path of the original link.
    if (!request.getHeader("User-Agent").contains("ms-office")) {
      filterChain.doFilter(request, response);
    }
  }
}

/**
 * Security configuration.
 */
@Configuration
public class SecurityConfiguration {
  @Bean
  public FilterRegistrationBean microsoftFilterRegistrationBean() {
    FilterRegistrationBean<MicrosoftFilter> registrationBean = new FilterRegistrationBean<>();
    registrationBean.setFilter(new MicrosoftFilter());
    registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
    return registrationBean;
  }
}

Here is how to workaround this with Java and Spring via a Filter:

/**
 * To see why this is necessary, check out this page:
 * https://support.microsoft.com/en-gb/help/899927.
 */
public class MicrosoftFilter extends OncePerRequestFilter {
  @Override
  protected void doFilterInternal(final HttpServletRequest request,
      final HttpServletResponse response,
      final FilterChain filterChain) throws ServletException, IOException {
    //Serve up a blank page to anything with a Microsoft Office user agent, forcing it to open the
    //URL in a browser instead of trying to pre-fetch it, getting redirected to SSO, and losing
    //the path of the original link.
    if (!request.getHeader("User-Agent").contains("ms-office")) {
      filterChain.doFilter(request, response);
    }
  }
}

/**
 * Security configuration.
 */
@Configuration
public class SecurityConfiguration {
  @Bean
  public FilterRegistrationBean microsoftFilterRegistrationBean() {
    FilterRegistrationBean<MicrosoftFilter> registrationBean = new FilterRegistrationBean<>();
    registrationBean.setFilter(new MicrosoftFilter());
    registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
    return registrationBean;
  }
}
无戏配角 2024-09-05 11:42:04

我们发现一个问题,当单击 MS Word 中的 URL 时,会打开两个 Chrome 选项卡,并且要打开的页面具有 JavaScript 重定向: window.location.href=blabla

通过从服务器端调试,我们 发现确认除了 Chrome 之外,还有从 Office 应用发送的请求。这太奇怪了。

但无论如何,通过检查请求标头“User-Agent”并向 Office 应用程序返回一个空页面,我们的两个选项卡问题得到了解决。这绝对是正确的做法!

We are seeing a problem that TWO Chrome tabs are opened when clicking an URL from MS Word, and the page to open has JavaScript redirection: window.location.href=blabla

By debugging from the servers side, we confirmed that there are requests sent from Office app, besides Chrome. This is so wierd.

But anyway, by checking the request header "User-Agent", and returning an empty page to Office apps, our TWO tabs issue got resolved. That's definitely the right thing to do!

乜一 2024-09-05 11:42:04

NGINX 解决方案如下:

if ($http_user_agent ~* Word|Excel|PowerPoint|ms-office) {
    return 200 '<html><head><meta http-equiv="refresh" content="0"/></head><body></body></html>';
}

您可以将其放在 serverlocation 块中。
就像魅力一样起作用。

NGINX solution below:

if ($http_user_agent ~* Word|Excel|PowerPoint|ms-office) {
    return 200 '<html><head><meta http-equiv="refresh" content="0"/></head><body></body></html>';
}

You can put it in the server or location block.
Works like charm.

那伤。 2024-09-05 11:42:04

这是我在 WordPress 中的解决方案。将其添加到主题或其他插件文件中的functions.php中。

如果您的系统(例如 WP)将注销的用户发送到登录页面并重定向到他们尝试访问的页面,这可能会很有帮助。 Word 正在将用户发送到此页面,但 WP 无法正确处理用户已登录的情况。此代码检查是否存在当前用户以及是否传递了redirect_to 参数。如果是这样,它将重定向到redirect_to 位置。

function my_logged_in_redirect_to()
{
global $current_user;

if($current_user->ID && $_REQUEST['redirect_to'])
{           
    wp_redirect($_REQUEST['redirect_to']);
    exit;
}
}
add_action('wp', 'my_logged_in_redirect_to');

Here is my solution for this in WordPress. Add this to functions.php in your theme or another plugin file.

This may be helpful if your system, like WP, sends logged out users to a login page with a redirect to the page they were trying to access. Word was sending users to this page, but then WP wasn't properly handling the case where a user was already logged in. This code checks if there is a current user and a redirect_to param passed. If so, it redirects to the redirect_to location.

function my_logged_in_redirect_to()
{
global $current_user;

if($current_user->ID && $_REQUEST['redirect_to'])
{           
    wp_redirect($_REQUEST['redirect_to']);
    exit;
}
}
add_action('wp', 'my_logged_in_redirect_to');
寻梦旅人 2024-09-05 11:42:04

这是针对 Excel 的 VBA 修复。相同的概念也适用于 Microsoft Word。基本上,代码不是从 Excel 内触发链接,而是从 shell 内执行链接。
代码如下:

Private Sub Worksheet_FollowHyperlink(ByVal objLink As Hyperlink)
    Application.EnableEvents = False
    Dim strAddress As String
    strAddress = "explorer " & objLink.TextToDisplay
    Dim dblReturn As Double
    dblReturn = Shell(strAddress)
    Application.EnableEvents = True
End Sub
  1. 对于包含链接的 Excel 工作表,右键单击工作表选项卡,然后单击查看代码。 VBA 编辑器出现。
  2. 将代码粘贴到窗口中,然后关闭编辑器。
  3. 修改页面中的每个链接,使其仅指向其所在的单元格。为此,请执行以下操作:
  4. 右键单击该链接,然后单击编辑超链接。将出现“编辑超链接”窗口。
  5. 单击置于本文档中
  6. 单击工作表名称。
  7. 对于键入单元格引用,输入单元格引用(例如 A4)。
  8. 单击确定

一些注意事项:

  • 您需要将电子表格另存为启用宏的电子表格
    (.xlsm)。当用户打开电子表格时,系统会要求他们
    启用宏。如果他们回答“否”,则链接将不起作用。
  • 这些说明基于Excel 2010。想必以后的版本类似。

Here's a VBA fix, for Excel. The same concept can be applied for Microsoft Word. Basically, rather than firing off the link from within Excel, the code executes the link from within a shell.
Here's the code:

Private Sub Worksheet_FollowHyperlink(ByVal objLink As Hyperlink)
    Application.EnableEvents = False
    Dim strAddress As String
    strAddress = "explorer " & objLink.TextToDisplay
    Dim dblReturn As Double
    dblReturn = Shell(strAddress)
    Application.EnableEvents = True
End Sub
  1. For the Excel sheet that contains the links, right-click the sheet tab and click View Code. The VBA editor appears.
  2. Paste the code into the window, and close the editor.
  3. Modify each link in the page so it simply points back to the cell that it is in. To do so:
  4. Right-click the link, and click Edit Hyperlink. An Edit Hyperlink window appears.
  5. Click Place In This Document.
  6. Click the sheet name.
  7. For Type the cell reference, enter a cell reference (e.g. A4).
  8. Click OK.

A couple of notes:

  • You will need to save the spreadsheet as a macro-enabled spreadsheet
    (.xlsm). When users open the spreadsheet, they will be asked to
    enable macros. If they answer No, the links will not work.
  • These instructions are based on Excel 2010. Presumably later versions are similar.
遥远的绿洲 2024-09-05 11:42:04

我不敢相信他们称这是一项功能。
然而,这是 Apache 的一个功能修复:

RewriteEngine On

# Send a 200 to MS Office so it just hands over control to the browser
# It does not use existing session cookies and would be redirected to the login page otherwise
# https://www.wimpyprogrammer.com/microsoft-office-link-pre-fetching-and-single-sign-on/

RewriteCond %{HTTP_USER_AGENT} ;\sms-office(\)|;)
RewriteRule .* - [R=200,L]

可能不是最佳性能,因为发送整个页面而不是空响应,但我不想添加另一个 Apache 模块只是为了修复这样的 idio^H^H^H^ H 特征。

I can't believe they call this a feature.
However, here's a featurefix for Apache:

RewriteEngine On

# Send a 200 to MS Office so it just hands over control to the browser
# It does not use existing session cookies and would be redirected to the login page otherwise
# https://www.wimpyprogrammer.com/microsoft-office-link-pre-fetching-and-single-sign-on/

RewriteCond %{HTTP_USER_AGENT} ;\sms-office(\)|;)
RewriteRule .* - [R=200,L]

Might not be best performance wise, as the whole page gets sent instead of an empty response, but I did not want to add another Apache modules just for fixing such an idio^H^H^H^H feature.

别低头,皇冠会掉 2024-09-05 11:42:04

我必须为 ASP.NET 站点解决这个问题,但我只想使用 javascript/ jQuery:

var isCoBrowse = ('<%= Session["user"].ToString().ToLower() %>' != '0');
if (isCoBrowse && window.location.href.indexOf('ReturnUrl=') >= 0 && window.location.href.indexOf('dllCheq') == -1) {
    //redirect to the ReturnUrl & add dllCheq to the URI
    var toRedirect = decodeURIComponent(gup('ReturnUrl', window.location.href)) + '&dllCheq';
    window.location = toRedirect;
}

我从以下位置获得了 gup 函数:
如何从 URL 参数获取值?

I had to solve this issue for an ASP.NET site but I only wanted to use javascript/ jQuery:

var isCoBrowse = ('<%= Session["user"].ToString().ToLower() %>' != '0');
if (isCoBrowse && window.location.href.indexOf('ReturnUrl=') >= 0 && window.location.href.indexOf('dllCheq') == -1) {
    //redirect to the ReturnUrl & add dllCheq to the URI
    var toRedirect = decodeURIComponent(gup('ReturnUrl', window.location.href)) + '&dllCheq';
    window.location = toRedirect;
}

I got the gup function from:
How to get the value from the URL parameter?

不喜欢何必死缠烂打 2024-09-05 11:42:04

使用下面链接中微软提供的修复程序。 https://support.microsoft.com/en-us/kb/218153

Use fix provided by microsoft given link below. https://support.microsoft.com/en-us/kb/218153

骑趴 2024-09-05 11:42:04

在 ColdFusion / Lucee 中

<cfif cgi.HTTP_USER_AGENT contains "Excel" OR cgi.HTTP_USER_AGENT contains "ms-office">
    <cfabort/>
</cfif>

And in ColdFusion / Lucee

<cfif cgi.HTTP_USER_AGENT contains "Excel" OR cgi.HTTP_USER_AGENT contains "ms-office">
    <cfabort/>
</cfif>
纵情客 2024-09-05 11:42:04

我怀疑这是你如何设置 cookie 的问题。

由于网络创建方式的性质,example.com 不被视为与 www.example.com 相同的域;因此:您可以在 www.example.com 登录,而不是在 example.com 登录。

换句话说,检查您的 Word 或 Excel 文件中的 URL - 它是否与您在浏览器中登录的域相同?

对于这种 cookie 不一致的问题,有两种修复/解决方案:
1. 重定向任何试图加载不带 www 的网站的人。到与 www 相同的页面。 (或反之亦然),或
2. 当您设置 cookie 时,请确保将域参数指定为“.example.com”。前导点表示 cookie 在该域的所有子域上也应该有效。

我怀疑浏览器最终识别它的原因是因为您最终可能会登陆到与您登录方式具有相同域结构的 URL。

希望这会有所帮助。

I suspect this is a matter of how you are setting the cookie(s).

Due to the nature of how the web was created, example.com is not seen as the same domain as www.example.com; hence: you can be logged in at www.example.com and not logged in at example.com.

So in other words, check the URL in your word or excel file - is it the same domain as how you are logged in within your browser?

There are two fixes/solutions to this cookie inconsistency:
1. redirect anyone who tries to load your site without the www. to the same page with the www. (or vice versa), or
2. when you are setting the cookie, make sure to specify the domain argument as ".example.com". The leading dot indicates the cookie should be valid on all subdomains of that domain as well.

I suspect the reason the browser eventually recognizes it is because you probably eventually end up landing on a URL with the same domain structure as how you are logged in.

Hope this helps.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文