我试图在 Visual Studio 中调用外部 Web 服务,但收到错误消息。
System.Security.SecurityException:请求“System.Security.Permissions.SecurityPermission,mscorlib,Version=4.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089”类型的权限失败。
系统.安全.安全异常:
在 System.Security.CodeAccessSecurityEngine.Check(对象需求,StackCrawlMark& stackMark,Boolean isPermSet)
在 System.Security.CodeAccessPermission.Demand()
在 System.Net.ServicePointManager.set_ServerCertificateValidationCallback(RemoteCertificateValidationCallback 值)
以下是调用Web服务的程序。
public partial class UserDefinedFunctions
{
[Microsoft.SqlServer.Server.SqlFunction(DataAccess = DataAccessKind.Read)]
[return: SqlFacet(MaxSize = -1)]
public static SqlString NYP_RestGet(SqlString uri)
{
String document;
System.Net.ServicePointManager.ServerCertificateValidationCallback +=
delegate(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certificate,
System.Security.Cryptography.X509Certificates.X509Chain chain,
System.Net.Security.SslPolicyErrors sslPolicyErrors)
{
return true; // **** Always accept
};
// Set up the request, including authentication
WebRequest req = WebRequest.Create(Convert.ToString(uri));
((HttpWebRequest)req).UserAgent = "CLR web client on SQL Server";
req.ContentType = "application/xml";
((HttpWebRequest)req).Accept = "application/xml";
WebResponse resp = req.GetResponse();
Stream dataStream = resp.GetResponseStream();
StreamReader rdr = new StreamReader(dataStream);
document = (String)rdr.ReadToEnd();
rdr.Close();
dataStream.Close();
resp.Close();
return (document);
}
};
对于网络相关的请求本身,您需要将程序集设置为PERMISSION_SET = EXTERNAL_ACCESS
。然而,使用System.Net.ServicePointManager.ServerCertificateValidationCallback
不幸的是需要PERMISSION_SET = UNSAFE
。如果您没有明确需要覆盖 SSL 证书的处理,那么您应该删除该委托,因为最好将程序集设置为EXTERNAL_ACCESS
.
不幸的是,Visual Studio / SSDT(SQL Server Data Tools)并不能很容易地采取适当的步骤来允许您的程序集设置为EXTERNAL_ACCESS
or UNSAFE
。然而,它们确实可以很容易地设置TRUSTWORTHY
选项ON
,这主要是一个坏主意。
请这样做not set TRUSTWORTHY ON
除非绝对必要!并且只有在加载您未构建且无法重新签名的程序集时才应该是“必要的”。这种情况主要发生在加载不受“支持”的 .NET Framework 库(因此尚未在 SQL Server 的 CLR 主机中)时。除这些情况外,您不应将数据库设置为TRUSTWORTHY ON
因为它打开了一个安全漏洞。
相反,最好执行以下操作:
USE [master];
CREATE ASYMMETRIC KEY [SomeKey]
AUTHORIZATION [dbo]
FROM EXECUTABLE FILE = 'C:\path\to\Some.dll';
CREATE LOGIN [SomeLogin]
FROM ASYMMETRIC KEY [SomeKey];
GRANT EXTERNAL ACCESS ASSEMBLY TO [SomeLogin]; -- or "UNSAFE" instead of "EXTERNAL ACCESS"
上述操作只需针对每个实例、每个键执行一次。所以如果你使用相同的snk
/ pfx
为所有程序集创建一个文件,然后每个 SQL Server 实例只需执行上面显示的步骤一次;程序集和包含这些程序集的数据库的数量并不重要。
这种方法允许您保持数据库的更好安全性(通过保持TRUSTWORTHY
set to OFF
)并允许更精细地控制哪些程序集甚至允许设置为EXTERNAL_ACCESS
and/or UNSAFE
(因为您可以通过使用不同的密钥进行签名和基于这些不同密钥的登录来分开)。
有关安全选项的更详细的演练,请参阅我在 SQL Server Central 上撰写的以下文章:通往 SQLCLR 4 级的阶梯:安全性(外部和不安全的程序集) http://www.sqlservercentral.com/articles/Stairway+Series/112888/(需要免费注册)。
其他注意事项:
- 有关通过 SQLCLR 调用 Web 服务和网页的更多说明,请参阅我的回答:从 SQL CLR 调用 Web 服务? https://stackoverflow.com/questions/32875954/call-web-service-from-sql-clr/32877528#32877528
- 没有必要使用
Convert.ToString(uri)
。全部Sql*
类型有一个.Value
返回适当的本机类型的属性。所以将其替换为uri.Value
.
- 你不需要
DataAccess = DataAccessKind.Read
in the SqlFunction
属性,因为您没有进行任何数据访问。环境DataAccess = DataAccessKind.Read
对性能有轻微影响,因此由于您没有使用它,因此将其删除。
-
dataStream
and rdr
(成为一个Stream
and StreamReader
分别)是“一次性”对象,所以你真的需要调用.Dispose()
在他们身上,你可能可以这样做而不是.Close()
.
- 您没有任何错误处理,这意味着如果在
Dispose
在这两个对象上调用方法时,您的进程可能会保持打开外部网络句柄,并且在应用程序域回收之前可能不会释放该句柄,而这可能不会持续很长时间。您需要使用using
构造或正确构造一个try
/ catch
/ finally
。在 SQL Server 中运行而不执行这两件事之一的代码是相当危险的。
- 有关使用 SQL Server 的 CLR 主机的各种细微差别的更多详细信息,请参阅我的文章:SQLCLR 5 级阶梯:开发(在 SQL Server 中使用 .NET) http://www.sqlservercentral.com/articles/Stairway+Series/119429/(需要免费注册)。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)