ここでの私の目標は、(1)設計どおりにSQL FILESTREAMを効果的に使用し、(2) SQL FileTable とその基になる FILESTREAM Windows ディレクトリへのアクセスを 2 つの Active Directory アカウント (Web によって提供される資格情報) のいずれかに制限する、添付ファイル マネージャーを用意することです。 /app.config ファイル)、および (3) このライブラリが含まれるサイトまたはアプリの現在の Windows 認証ユーザーを効果的に無視します。
私はすでにこの問題に関する多くの投稿を見てきましたが、私が見つけたほとんどすべての提案を試してみましたが、まだほとんどまたはまったく成功していません.
私は持っている:
- 接続文字列で統合セキュリティを有効にしました:
"Server={server};Database=AttachmentsGlobal;User ID={domain}\\{user};Password={password};Trusted_Connection=False;Integrated Security=true;"
- フォルダーのアクセス許可を変更することにより、SQL Server 2012 が実行されている Windows Server 2012 上の FileTable ディレクトリへの同じユーザー RW アクセスが与えられます。
filestream
ファイル テーブルと列に対する選択、変更、更新、挿入、および削除の権限をユーザーに付与します。- オブジェクトを呼び出すときに同じユーザーを偽装するために、オンラインで見つけたカスタムの偽装クラスを実装しました
SqlFileStream
(以下を参照)。 - SQL Server側でエラーを見つけようとしました。イベント ビューアーで確認できるのは、以下で試みたなりすましがユーザーのログオンに成功したことだけです。
- SQL Server の FILESTREAM 設定で「リモート接続を許可する」が有効になっていることを確認しました。
using(Impersonator){}
以下のブロックを無効にするたびに機能しSaveFile
ますが、それ以外の場合は失敗します。また、ローカルでデバッグするときに接続文字列を無視するようです: 私は管理者であるため、ユーザー アカウントは添付ファイル フォルダーにアクセスできますが、明示的にアクセス権を持たないユーザーに接続文字列を変更しても、正常にアップロードされます。 . これは、現在のアプリケーションユーザーの資格情報をサーバーに渡し、提供されたユーザーを無視する「統合セキュリティ」設定であると想定しています。Visual Studioを別のユーザーとして実行する以外に、これをうまくテストする方法がわかりません(試してみましたが失敗しました)。
私がばかげたことをしていて、解決策にかなり近づいていることを教えてください。この信じられないほどあいまいな「アクセスが拒否されました」というエラーは、私を夢中にさせています! ありがとう。
C#:
// SqlFileItem is just a simple model mapping all the columns of the file
// table (except the filestream itself)
public SqlFileItem UploadFile(Stream fileStream, string fileName,
SqlFileItem parent, AttachmentType restricted) {
const string uploadSproc = "InsertAttachment";
const string metadataSproc = "GetMetadata";
SqlFileItem rt;
using (TransactionScope ts = new TransactionScope()) {
using (SqlConnection conn = GetConnection(restricted)) {
conn.Open();
string serverPath;
byte[] serverXfer;
Guid streamIdOfUploadedFile;
using (SqlCommand cmd = conn.CreateCommand()) {
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = uploadSproc;
cmd.Parameters.Add("@fName", SqlDbType.VarChar).Value =
fileName;
cmd.Parameters.Add("@parent", SqlDbType.VarChar).Value =
parent.path_locator.ToString();
using (SqlDataReader rdr = cmd.ExecuteReader()) {
rdr.Read();
serverPath = rdr.GetSqlString(0).Value;
serverXfer = rdr.GetSqlBinary(1).Value;
streamIdOfUploadedFile = rdr.GetGuid(2);
rdr.Close();
}
}
// See below for SaveFile
SaveFile(fileStream, serverPath, serverXfer, exportRestriction);
using (SqlCommand cmd = conn.CreateCommand()) {
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = metadataSproc;
cmd.Parameters.Add("@streamId",
SqlDbType.UniqueIdentifier).Value =
streamIdOfUploadedFile;
using (SqlDataReader rdr = cmd.ExecuteReader()) {
rt =
DataReaderToList<SqlFileItem>(rdr)
.FirstOrDefault();
}
}
}
ts.Complete();
}
return rt;
}
// Performs the actual streaming/saving operation.
private static void SaveFile(Stream src, string serverPath,
byte[] serverXfer, AttachmentType restricted) {
const int blockSize = 1024*512;
// My attempt at impersonating
Impersonator imp = restricted == AttachmentType.Open
? new Impersonator(@"{openActiveDirectoryUser}", @"{domain}",
@"{password}")
: new Impersonator(@"{restrictedActiveDirectoryUser}", @"{domain}",
@"{password}");
using (imp) {
using (src) {
using (
// The line below is where it breaks with a Win32Exception.
// The full text of it from VS2015 is provided below.
SqlFileStream dest = new SqlFileStream(serverPath,
serverXfer, FileAccess.Write)) {
byte[] buffer = new byte[blockSize];
int bytesRead;
while ((bytesRead = src.Read(buffer, 0, buffer.Length)) >
0) {
dest.Write(buffer, 0, bytesRead);
dest.Flush();
}
dest.Close();
}
src.Close();
}
}
}
SQL:
ALTER PROCEDURE [dbo].[InsertAttachment]
@fileName varchar(255),
@parent hierarchyid
AS
BEGIN
SET NOCOUNT ON;
DECLARE @returns TABLE (stream_id UNIQUEIDENTIFIER)
INSERT INTO Attachments(file_stream, name, is_directory, path_locator)
OUTPUT inserted.stream_id INTO @returns
VALUES (0x, @fileName, 0, dbo.GetCustomPathLocator(@parent.ToString()))
SELECT file_stream.PathName()
,GET_FILESTREAM_TRANSACTION_CONTEXT()
,aft.stream_id
FROM Attachments att
INNER JOIN @returns rt on att.stream_id = rt.stream_id
END
ALTER PROCEDURE [dbo].[GetMetadata]
@streamId uniqueidentifier
AS
BEGIN
SELECT stream_id
-- omit file_stream varbinary(max)
,name
,path_locator
,parent_path_locator
,file_type
,cached_file_size
,creation_time
,last_write_time
,last_access_time
,is_directory
-- omit remaining is_* flags
FROM Attachments
WHERE stream_id = @streamId
END
例外:
System.ComponentModel.Win32Exception was unhandled
ErrorCode=-2147467259
HResult=-2147467259
Message=Access is denied
NativeErrorCode=5
Source=System.Data
StackTrace:
at System.Data.SqlTypes.SqlFileStream.OpenSqlFileStream(String path, Byte[] transactionContext, FileAccess access, FileOptions options, Int64 allocationSize)
at System.Data.SqlTypes.SqlFileStream..ctor(String path, Byte[] transactionContext, FileAccess access, FileOptions options, Int64 allocationSize)
at System.Data.SqlTypes.SqlFileStream..ctor(String path, Byte[] transactionContext, FileAccess access)
at FileTableAttachmentLibrary.AttachmentManager.SaveFile(Stream src, String serverPath, Byte[] serverXfer, AttachmentScope exportRestricted) in C:\_projects\FileTableAttachmentLibrary\FileTableAttachmentLibrary\FileTableAttachmentLibrary\AttachmentManager.cs:line 498
at FileTableAttachmentLibrary.AttachmentManager.UploadFile(Stream fileStream, String fileName, SqlFileItem parent, AttachmentScope exportRestriction) in C:\_projects\FileTableAttachmentLibrary\FileTableAttachmentLibrary\FileTableAttachmentLibrary\AttachmentManager.cs:line 210
at TestAttachments.Program.AttachmentManagerTest() in C:\_projects\FileTableAttachmentLibrary\FileTableAttachmentLibrary\TestAttachments\Program.cs:line 69
at TestAttachments.Program.Main(String[] args) in C:\_projects\FileTableAttachmentLibrary\FileTableAttachmentLibrary\TestAttachments\Program.cs:line 26
InnerException: null