如何捕获 SharePoint 2007/2010 的 403 Forbidden 的真实错误
来源:互联网 发布:python内存管理机制 编辑:程序博客网 时间:2024/06/10 00:12
存在的问题
在 SharePoint 2007 上你可能会碰到莫名其妙的 403 Forbidden 错误,SharePoint 丢出这个错误时简单得不能再简单,空白一片就 403 Forbidden 两个词,如下图
你还可能看到是“网站拒绝显示此网页(The website declined to show this webpage) ”的错误信息,如下图。你可以通过 IE 菜单栏 > 工具 > Internet 选项 > 高级 > 浏览 >去掉“显示友好 HTTP 错误信息” 来看到内部错误。
可惜的是,这个简单一点都不美,而且还很可恨。因为我们不知道到底背后发生了什么错误,如果此时能在系统日志或 SharePoint日志(12/logs下)找到详细错误还好点(实际上从下面的源码分析中,对于 403 错误,SharePoint 没有机会写ULS日志,因为直接Response.End 了),否则就郁闷了,可能得调试诊断半天甚至几天才能找到问题所在。
解密这个问题
我曾经就有刚加载一个自定义 WebPart 之后就 403,因为 Web Part比较复杂,找到不跟踪信息,只好一个方法一个方法的去注释,去调试,最终发现是写一个没有权限的文件引起的(抛出UnauthorizedAccessException),足足花了一个下午。因此,猜想 UnauthorizedAccessException可能是被 SharePoint 截获了,然后就直接就返回 403 了。发现 sharepoint 的站点 web.config 中,其httpModules 配置节移除了所有继承的 module,而将Microsoft.SharePoint.ApplicationRuntime.SPRequestModule 配置为第一个 module:
1
<
httpmodules
>
2
<
clear
>
3
<
add
type
=
"Microsoft.SharePoint.ApplicationRuntime.SPRequestModule,Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral,PublicKeyToken=71e9bce111e9429c"
name
=
"SPRequest"
>
4
<
add
type
=
"System.Web.Caching.OutputCacheModule"
name
=
"OutputCache"
>
5
<
add
type
=
"System.Web.Security.FormsAuthenticationModule"
name
=
"FormsAuthentication"
>
6
<!-- omitted ... ->
7
</
add
></
add
></
add
></
clear
></
httpmodules
>
通过 Reflector 查看 Microsoft.SharePoint.ApplicationRuntime.SPRequestModule 类源码,可以看到作为 HttpApplication.Error 的处理程序的 ErrorAppHandler 方法中确实对 UnauthorizedAccessException 异常进行了特殊处理:
01
private
void
ErrorAppHandler(
object
oSender, EventArgs ea)
02
{
03
HttpApplication app = oSender
as
HttpApplication;
04
if
(app !=
null
)
05
{
06
HttpContext context = app.Context;
07
if
(context !=
null
)
08
{
09
Exception error = context.Error;
10
if
(error !=
null
)
11
{
12
context.Items[
"HttpHandlerException"
] =
"1"
;
13
while
(error.InnerException !=
null
)
14
{
15
error = error.InnerException;
16
}
17
if
(error
is
UnauthorizedAccessException)
18
{
19
SPUtilityInternal.Send403(context.Response);
20
}
21
// omitted....
22
}
23
}
24
}
25
}
SPUtilityInternal.Send403 内部则是调用了 SendResponse(response, 0x193, "403 FORBIDDEN");
01
internal
static
void
SendResponse(HttpResponse response,
int
code,
string
strBody)
02
{
03
HttpContext current = HttpContext.Current;
04
object
obj2 = current.Items[
"ResponseEnded"
];
05
if
((obj2 ==
null
) || !((
bool
) obj2))
06
{
07
current.Items[
"ResponseEnded"
] =
true
;
08
response.StatusCode = code;
09
response.Clear();
10
if
(strBody !=
null
)
11
{
12
response.Write(strBody);
13
}
14
response.End();
15
}
16
}
解决这个问题
那么,我们是不是也可以自己写一个 HttpModule 订阅 HttpApplication.Error 事件,捕获UnauthorizedAccessException 异常,并按我们期望的格式输出呢?当然可以,很关键的一步是要在SPRequestModule 之前注册我们自定义的 HttpModule。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Diagnostics;
namespace LeoWorks.SharePoint.ErrorHandlerModule
{
class ErrorHandlerModule : IHttpModule
{
#region IHttpModule Members
public void Dispose()
{
}
public void Init(HttpApplication app)
{
app.Error += new EventHandler(app_Error);
}
void app_Error(object sender, EventArgs e)
{
//Debug.Assert(false);
HttpApplication app = (HttpApplication)sender;
HttpContext ctx = app.Context;
if (ctx != null)
{
Exception err = ctx.Error;
if (err != null)
{
// it's helpful to handle all the types of exception especially when the application
// is throwing a chain of exceptions, because by default SharePoint will only show you
// the root exepction but you may want to get the full stack trace.
if (Convert.ToBoolean(ctx.Items["LW_HandleAllErrors"]))
{
HandleError(ctx, err); // output the full stack trace
}
else
{
Exception innerErr = err;
while (innerErr.InnerException != null)
{
innerErr = innerErr.InnerException;
}
if (innerErr is UnauthorizedAccessException)
{
HandleError(ctx, err); // output the full stack trace
}
}
}
}
}
void HandleError(HttpContext ctx, Exception err)
{
ctx.Response.Clear();
ctx.Response.Write(String.Format("
1
<div style=
"background:rgb(255, 255, 204) none repeat scroll 0% 0%; -moz-background-clip:border; -moz-background-origin: padding; -moz-background-inline-policy:continuous;"
><code></code><pre>{0}</pre><code></code></div>", err.ToString()));
2
ctx.ClearError();
3
ctx.Response.End();
4
}
5
6
#endregion
7
}
8
}
将编译好的的 LeoWorks.SharePoint.ErrorHandlerModule.dll 放入sharepoint站点所在的目录的 bin 文件夹,并在 web.config 配置此 module:
1
<
httpmodules
>
2
<
clear
>
3
<
add
type
=
"LeoWorks.SharePoint.ErrorHandlerModule.ErrorHandlerModule, LeoWorks.SharePoint.ErrorHandlerModule"
name
=
"LWSPErrorHandler"
>
4
<
add
type
=
"Microsoft.SharePoint.ApplicationRuntime.SPRequestModule,Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral,PublicKeyToken=71e9bce111e9429c"
name
=
"SPRequest"
>
5
<!-- ... -->
6
</
add
></
add
></
clear
></
httpmodules
>
update 2010.4.13
今天发现 SharePoint 内部有些权限验证也会直接调用 SPUtilityInternal.Send403 方法,从而引发 403Forbidden。因此,碰到这个错误,如果不是 UnauthorizedAccessException 异常,那么可以通过Reflector 去查看页面的后台源码,分析内部实现,就能找到问题所在了。比如,在非缺省级网站集访问“自助式网站创建”页面/_layouts/scsignup.aspx 时,就会得到 403 Forbidden。
测试我们的自定义的 Module
在 12/templates/layouts 目录中建立一个自己的 .aspx 文件,这里我命名为LWK_Throws403.aspx,并放入了专享文件夹 leoworks.net。粘贴如下代码,在浏览器中打开该页面,并点击 Throws按钮,此时我们就可以看到浏览器输出了调用堆栈。
01
<%@ Page Language="C#" AutoEventWireup="true" %>
02
03
04
05
<
script
type
=
"text/C#"
runat
=
"server"
>
06
07
protected void btnThrow_Click(object sender, EventArgs e)
08
{
09
// An UnauthorizedAccessException will cause the SharePoint engine to response
10
// a simple HTTP 403 error,
11
// for example,
12
// try to write to a file that current user has no permission to access
13
// System.IO.File.AppendAllText(@"C:/nobodycanwrite/test.txt", "I want to...");
14
// or explicitly throws an UnauhorizedAccessException
15
Context.Items["LW_HandleAllErrors"] = rblHandleType.SelectedIndex == 1;
16
if (rblExType.SelectedIndex == 3)
17
{
18
ThrowExceptionChain(false);
19
}
20
else if (rblExType.SelectedIndex == 2)
21
{
22
ThrowExceptionChain(true);
23
}
24
else if (rblExType.SelectedIndex == 1)
25
{
26
throw new UnauthorizedAccessException("mo xu you");
27
}
28
else
29
{
30
throw new ApplicationException("generic error!");
31
}
32
}
33
34
void ThrowExceptionChain(bool throws403)
35
{
36
try
37
{
38
if (!throws403)
39
{
40
throw new ApplicationException("generic error");
41
}
42
else
43
{
44
throw new UnauthorizedAccessException("mo xu you");
45
}
46
}
47
catch (Exception ex)
48
{
49
throw new Exception("yuan wang!", ex);
50
}
51
}
52
53
</
script
>
54
55
56
57
58
59
60
61
<
div
>
62
Use LWSPErrorHandlerModule to handle
63
<
asp:radiobuttonlist
id
=
"rblHandleType"
runat
=
"server"
repeatdirection
=
"Horizontal"
repeatlayout
=
"Flow"
>
64
<
asp:listitem
selected
=
"True"
>Only Unauhorized</
asp:listitem
>
65
<
asp:listitem
>All Exception</
asp:listitem
>
66
</
asp:radiobuttonlist
>
67
<
br
>
68
Throws
69
<
asp:radiobuttonlist
id
=
"rblExType"
runat
=
"server"
repeatdirection
=
"Horizontal"
repeatlayout
=
"Flow"
>
70
<
asp:listitem
>Generic Exception</
asp:listitem
>
71
<
asp:listitem
selected
=
"True"
>Unauthorized Access Exception</
asp:listitem
>
72
<
asp:listitem
>Nested Unauthorized Exceptions</
asp:listitem
>
73
<
asp:listitem
>Nested Generic Exceptions</
asp:listitem
>
74
</
asp:radiobuttonlist
>
75
<
br
>
76
<
asp:button
id
=
"Button1"
onclick
=
"btnThrow_Click"
runat
=
"server"
text
=
"Throw"
>
77
</
asp:button
></
div
>
关于 SharePoint 2010
SharePoint 2010 依然存在此问题,上面的方法依然适用于 SharePoint 2010。只是需要注意 SharePoint2010 默认将所有的 httpModule 配置在 IIS7 专有的 system.webServer/modules 节中,因此我们的LeoWorks.SharePoint.ErrorHandlerModule 也要在此配置,目的是保证其配置在 SharePoint 的SPRequestModule 之前。(当然如果你喜欢,可以将所有 module 移回 system.web/httpModules。)。SharePoint 2010 配置如下:
1
<
system.webserver
>
2
<
modules
runallmanagedmodulesforallrequests
=
"true"
>
3
<!-- ... -->
4
<
add
type
=
"LeoWorks.SharePoint.ErrorHandlerModule.ErrorHandlerModule,LeoWorks.SharePoint.ErrorHandlerModule, Version=1.0.0.0,Culture=neutral, PublicKeyToken=24fb2cde482f6824"
name
=
"LWSPErrorHandler"
>
5
<
add
type
=
"Microsoft.SharePoint.ApplicationRuntime.SPRequestModule,Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral,PublicKeyToken=71e9bce111e9429c"
name
=
"SPRequestModule"
precondition
=
"integratedMode"
>
6
<!-- ... -->
7
</
add
>
8
<!-- ... -->
9
</
add
></
modules
></
system.webserver
>
其他说明
1. 仔细分析 app_Error 方法,会发现我们通过 if(Convert.ToBoolean(ctx.Items["LW_HandleAllErrors"])) 判断是处理所有的异常还是仅仅处理Unauthorized 异常。这个标识是我们再 LWK_Throws403.aspx 中调用 throw之前写入的,这里仅仅是为了演示方便,你可能更喜欢写在配置文件中,比如 web.config/appSettings 中。
2. 此外,在调用 HandleError 方法时,我们总是传入 Context.Error ,而不是其内部根异常,这主要是为了输出完整的异常链信息。而 SharePoint 内部总是仅仅输出根异常。
3. 对于同时调试多个 SharePoint web app,你可能更喜欢将LeoWorks.SharePoint.ErrorHandlerModule.dll 部署在 GAC 中而不是每个 web app 的 bin中。此时 web.config 中应该用完整的程序集信息:
4. 如果你的堆栈信息无法看到出错的代码行数,除了你要确保你同时部署了 debug 的 dll 和对应版本的 pdb 外,要将web.config 的 trust level 配置为 Full,sharepoint 默认是 WSS_Minimal,假如是 .aspx页面错误,还要将 system.web/compilation 的 debug 配置为 true 。
5. 这个 Module 仅应该在开发环境中使用,生产环境应该移除掉,避免暴露过多的信息给用户。
源码下载
LeoWorks.SharePoint.ErrorHandlerModule.zip (18.37 kb)
- 如何捕获 SharePoint 2007/2010 的 403 Forbidden 的真实错误
- Sharepoint 403 Forbidden 错误
- Apache [forbidden 403]错误的解决办法
- Sip 403 forbidden错误的解决方法
- nginx出现403 forbidden的错误
- Nginx 403 Forbidden错误的解决方法
- nginx 403 forbidden错误的解决
- 403 Forbidden错误的原因和解决方法
- 如何查看SharePoint未知错误的详细信息
- 如何查看SharePoint未知错误的详细信息
- 如何查看SharePoint未知错误的详细信息
- apache 报错 Access forbidden! 403错误的解决方法
- svn checkout 报 403 Forbidden 错误的处理方法
- nginx出现403 forbidden错误的两种原因
- SVN提交代码出现403 Forbidden的错误
- nginx “403 Forbidden” 错误的原因及解决办法
- nginx出现403 forbidden错误的两种原因
- github 提交报403 forbidden的错误解决
- Taobao自主研发分布式文件系统TFS
- 新人报到
- 全排列算法的理解
- 全排列算法的理解
- 如何将服务端的多个文件打包下载(转)
- 如何捕获 SharePoint 2007/2010 的 403 Forbidden 的真实错误
- linux 下 CollabNetSubVersion 的安装与配置
- 构造函数的问题
- c++ 基本函数。。。
- 关于 GAC 目录
- .NET 如何访问环境变量
- proc文件系统剪辑
- 最长递增子序列 O(NlogN)算法
- ASP.NET 安全结构