【问题标题】:TSQL - Executing CLR PermissionTSQL - 执行 CLR 权限
【发布时间】:2011-05-05 13:13:13
【问题描述】:

我从 CLR(.net 程序集)获得了一个 sql 过程,执行时返回错误

Msg 6522, Level 16, State 1, Procedure sp_HelloWorld, Line 0
A .NET Framework error occurred during execution of user defined routine or aggregate 'sp_HelloWorld': 
System.Security.SecurityException: Request for the permission of type 'System.Data.SqlClient.SqlClientPermission, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed.
System.Security.SecurityException: 
   at System.Security.CodeAccessSecurityEngine.Check(Object demand, StackCrawlMark& stackMark, Boolean isPermSet)
   at System.Security.PermissionSet.Demand()
   at System.Data.Common.DbConnectionOptions.DemandPermission()
   at System.Data.SqlClient.SqlConnection.PermissionDemand()
   at System.Data.SqlClient.SqlConnectionFactory.PermissionDemand(DbConnection outerConnection)
   at System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory)
   at System.Data.SqlClient.SqlConnection.Open()
   at HelloWorld.SQLCLR.HelloWorld()

这是我的 SQL 脚本

go
drop procedure HelloWorld
drop assembly HelloWorld
GO

create assembly HelloWorld from 'F:\HelloWorld.dll'
with permission_set = safe
Go
create procedure sp_HelloWorld
as external name HelloWorld.[HelloWorld.SQLCLR].HelloWorld
go
exec sp_HelloWorld

这是我的班级(大会)

using Microsoft.SqlServer.Server;
using System.Data.SqlTypes;
using System.Data.SqlClient;
using System.Security.Permissions;
using System.Data;

namespace HelloWorld
{
    public class SQLCLR
    {
        [Microsoft.SqlServer.Server.SqlProcedure]
        public static void HelloWorld()
        {
            string connectString1 = @"Data Source=localhost;Initial Catalog=ItemData;Integrated Security=True";

            SqlClientPermission permission = new SqlClientPermission(PermissionState.None);
            permission.Add(connectString1, "", KeyRestrictionBehavior.AllowOnly);
            permission.PermitOnly();
            SqlConnection sqlcon = new SqlConnection(connectString1);
            sqlcon.Open();
            SqlCommand sqlcmd = new SqlCommand("SELECT Top 1 * FROM ItemData.dbo.Item", sqlcon);
            SqlDataReader reader = sqlcmd.ExecuteReader();
            SqlContext.Pipe.Send(reader);
            sqlcon.Close();
        }
    }
}

【问题讨论】:

  • Juvil,如果您仍然感兴趣,我添加了一个 answer 来解释问题并更正代码以获得最佳实践。我还解释了为什么其他答案不起作用(在这些答案的 cmets 中)。

标签: sql-server tsql sqlclr database-permissions


【解决方案1】:

问题只是您试图访问标记为SAFE 的程序集中的外部资源。访问外部资源需要将程序集设置为至少EXTERNAL_ACCESS(在某些情况下为UNSAFE)。但是,查看您的代码,您只是尝试连接到本地实例,在这种情况下,有一种更简单(且更快)的方法:使用 "Context Connection = true;" 作为 ConnectionString。

上下文连接是与当前进程/会话的直接连接,有时也称为进程内连接。使用上下文连接的好处是:

  • 可以在标记为SAFE的程序集中完成
  • 访问本地临时对象(临时表和临时过程,两者的名称都以单个 # 而不是双 ## 开头)
  • 访问SET CONTEXT_INFOCONTEXT_INFO()
  • 没有连接启动开销

还有:

  • 无论您使用进程内连接、上下文连接还是常规/外部连接,都不需要使用SqlClientPermission 正式请求权限
  • 您应该始终通过调用它们的Dispose() 方法来清理外部资源。并非所有对象都有这个,但SqlConnectionSqlCommandSqlDataReader 肯定有。人们通常将一次性对象包装在 using() 块中,因为它是一个编译器宏,可扩展为 try / finally 结构,该结构调用 finally 中的 Dispose() 方法以确保调用它,即使发生错误。
  • 许多/大多数一次性对象的Dispose() 方法会自动处理对Close() 的调用,因此您通常不需要显式调用Close()

您的代码应如下所示:

[Microsoft.SqlServer.Server.SqlProcedure]
public static void HelloWorld()
{
  using (SqlConnection sqlcon = new SqlConnection("Context Connection = true;")
  {
    using (SqlCommand sqlcmd = new SqlCommand("SELECT Top 1 * FROM ItemData.dbo.Item",
               sqlcon))
    {
      sqlcon.Open();

      using (SqlDataReader reader = sqlcmd.ExecuteReader())
      {
        SqlContext.Pipe.Send(reader);
      }
    }
  }
}

【讨论】:

  • 很棒的信息...拥有这个非常有意义。但!我们需要更重要的一件事.....测试我们在哪里运行以在此字符串和典型字符串之间进行选择,以便我们可以在设计时使用典型的连接字符串。我们如何测试我们是否有这个“sql server Context”?
  • 刚刚找到答案...也是你写的:)。也许你可以添加链接...stackoverflow.com/a/31279625/1518460
【解决方案2】:

我只是想为此添加我的两种感觉。我正在做一些非常相似的事情,但我遇到了同样的错误。这是我发现的,但是 b/c 我没有这个级别的数据库访问权限,我无法测试它。

最简单的(虽然不是 MSDN 建议只喷射 CLR proc 来运行) 就是将权限级别设置为External_Access...

SQL Server 主机策略级别权限集代码访问权限集 SQL Server 主机授予程序集的安全权限 策略级别由指定的权限集决定 创建程序集。共有三个权限集:SAFE、 EXTERNAL_ACCESS 和不安全。

权限级别在 CLR 项目的属性页上设置 ,数据库选项卡-设置权限级别-外部,设置Aassembly Owner-dbo,然后运行 ​​tsql 'ALTER DATABASE DataBaseName SET TRUSTWORTHY ON' 这将完成工作! - 并且 SmtpClient 可以正常工作... 然后做对并使用强名称密钥文件对 Assenbly 进行签名...

Full Post Here...

【讨论】:

  • 鉴于希望连接到本地实例,这当然不是最简单甚至不是最好的方法。将数据库设置为 TRUSTWORTHY ON,虽然既快速又简单,但却是一种通常没有必要的坏方法。诚然,最后一条语句确实说“然后正确地做...”,但如果有人让它与TRUSTWORTHY ON 一起工作,那么他们稍后会回来做这件事的可能性很小。无论哪种方式,我都会在answer 中解释真正的问题。
【解决方案3】:

您是否将 DB 集设置为 Trusrtworth ON 并启用 clr?

试试这个

sp_configure 'clr enabled', 1
GO
RECONFIGURE
GO

ALTER DATABASE [YourDatabase] SET TRUSTWORTHY ON
GO

我有一个关于如何使用 CLR 存储过程的指南 here,这可能会有所帮助。

【讨论】:

  • 这对我没有帮助。
  • @Ryan 和 Raymund:这些步骤无济于事。如果尚未启用 CLR,则尝试运行任何 SQLCLR 代码都会出错,指出尚未启用 CLR 集成。将 DB 设置为 TRUSTWORTHY ON,虽然快速简单并且通常有助于 EXTERNAL_ACCESS 或 UNSAFE 程序集,但通常没有必要,因为它会带来安全风险。这里的问题是程序集设置为 SAFE,它不允许任何外部请求。第 1 步是将程序集设置为 EXTERNAL_ACCESS,但我在answer 中显示了一个更好的选项。
  • 我同意这不是真的。问题是权限。其他评论和其他答案直击问题的核心。
猜你喜欢
  • 2017-11-16
  • 1970-01-01
  • 1970-01-01
  • 2017-01-06
  • 2022-06-24
  • 2011-03-03
  • 1970-01-01
  • 2017-08-22
  • 2016-04-15
相关资源
最近更新 更多