JavaからWindows上で別のユーザーの下でコードを実行する

こんにちは 次に、JNAを使用してJavaからWindowsの別のユーザーアカウントでコードを実行する方法を説明します。



実用価値



一般に、これは、1人のユーザーがアクセスできるリソースにアクセスする必要があり、現在のスレッドを実行しているユーザーがこれらのリソースにアクセスする権限を持っていない場合に必要になることがあります。 ただし、目的のユーザーのユーザー名、ドメイン、およびパスワードがあります。

たとえば、特定のフォルダーからクライアントファイルを提供できるサーバーがあります。 フォルダへのアクセスは、NTFSアクセス許可によって制限されます。 また、サーバー自体にはこれらのフォルダーへのアクセス権がありません(これは正しいことです。サーバーは、サーバーディレクトリと一時フォルダーにのみアクセスできる別のアカウントで起動します)。 ただし、クライアントはこれらのフォルダーへのアクセスを許可されているユーザーの資格情報を知っています。 質問は小さく、適切なユーザーとしてログインしてアクセスします。



理論



まず、ユーザーの権利を使用するには、適切なユーザーの資格情報でプロセスまたはスレッドを開始し、次にこのスレッド/プロセスから必要なリソースにアクセスする必要があることを知っておく必要があります。 MSDNおよびフォーラムでは、次のことをアドバイスしています。

1)CreateProcessWithLogonW-必要な資格情報で指定されたアプリケーションをすぐに起動します。 最も簡単なオプションであり、最適ではない-2、3行のコードを実行するために、アプリケーション全体を起動します。

2)多くの機能-LogonUser、CreateThread、SetThreadToken、ResumeThread。 最初の機能では、指定された資格情報でシステムにログインし、トークンを取得します。 2番目の関数では、スリープスレッドを作成します。 3番目は、作成されたストリームのトークンを示します。 4番目では、作成されたスリープスレッドを実行します。

2番目のオプションについて説明します。 追加のコード行がいくつか必要ですが、最適です。



実装



最初に、Javaからネイティブコードを実行するものを選択する必要があります。 JNIとJNAの2つの方法を知っています。 Java用の最初の「ネイティブ」であり、最速(一部の著者による)。 ただし、呼び出された関数の説明を含む追加のネイティブライブラリを記述する必要があります。 そのためには、外部からの身体の動きが必要です。 さらに、単純ではありますが、ネイティブコードを記述することは、Javaプログラマにとって最も些細な作業ではありません。 2番目のケースでは、インターフェースで呼び出された関数を記述するだけで十分です-それを使用できます。 さらに、JNAのWindowsの多くのタイプと機能はすでに説明されています。

JNAを使用するには、メインのJNAコードを含むjna.jarと、さまざまなプラットフォーム(Mac、Win、* nix)のいくつかの機能とタイプの説明を含むplatform.jarが必要です。

それでは、コーディングに移りましょう。 最初に、呼び出された関数(2つのインターフェイスを作成しましょう)を説明します。MoreKernel32はplatform.jarからKernel32インターフェイスを継承し、MoreAdvapi32は同じjarからAdvapi32を継承します。

これらのインターフェイスに次のメソッドを記述します。

public interface MoreAdvapi32 extends Advapi32 { static final MoreAdvapi32 instance = (MoreAdvapi32) Native.loadLibrary("advapi32", MoreAdvapi32.class, W32APIOptions.DEFAULT_OPTIONS); Boolean SetThreadToken(HANDLEByReference pointer, HANDLE Token); }
      
      







 public interface MoreKernel32 extends Kernel32 { static final MoreKernel32 instance = (MoreKernel32) Native.loadLibrary("kernel32", MoreKernel32.class,W32APIOptions.DEFAULT_OPTIONS); DWORD ResumeThread(WinNT.HANDLE hThread); WinNT.HANDLE CreateThread(Pointer lpThreadAttributes, Pointer dwStackSize, Callback lpStartAddress, Pointer lpParameter, DWORD dwCreationFlags, Pointer lpThreadId); }
      
      







LogonUserメソッドは既にAdvapi32インターフェイスに含まれているため、記述する必要はありません。

JNAがネイティブライブラリでどの関数を呼び出すべきかをどのように見つけるかについてのいくつかの言葉。 これを行うには、これらの関数のインターフェースを記述して、名前とパラメーターが一致するようにする必要があります。 次に、次のパラメーターを指定してNative.loadLibraryを呼び出す必要があります-ロードするライブラリーの名前、ライブラリーがマップするインターフェースの名前、およびダウンロードオプション。 その結果、これらの関数を使用できるインターフェイスへのリンクを取得します。

次に、私たちが行ったことをさらに詳しく説明します。

1)インターフェイスの機能を説明しました。

2)kernel32およびadvapi32ライブラリをダウンロードし、Native.loadLibrary関数を呼び出して、作成したインターフェイスにマッピングしました。 ロードされたライブラリへのリンクは、各インターフェイスのインスタンス変数に保存されていました。

次に、2つのクラスを検討します。 最初のクラスThreadFuncは、ユーザースレッドで実行されるように設計されています。 コードは非常に単純です-読み取り用にファイルを開き、ファイルの可用性を確認するだけです。 最も重要なことは、クラスがCallbackインターフェースを実装し、その中にコールバック()関数が作成されたことです。 これにより、JNAはコールバック用のクラスを決定できます。 コールバックインターフェイスコードを見ると、呼び出された関数の名前と無視される関数の名前が記述されていることがわかります。

 package jna; import java.io.FileInputStream; import java.io.FileNotFoundException; import com.sun.jna.Callback; public class ThreadFunc implements Callback { private String fileName; private FileInputStream in; public FileInputStream getIn() { return in; } public String getFile() { return fileName; } public void setFile(String file) { this.fileName = file; } public void callback() { File testAccess = new File(fileName); if (testAccess.canRead()) { System.out.println("     "); try { in = new FileInputStream(fileName); } catch (FileNotFoundException e) { e.printStackTrace(); } }else{ System.out.println("     "); } } }
      
      





2番目のJnaTestクラスがメインです。ここでは、ストリームが作成され、ユーザーがシステムにログオンし、トークンがインストールされます。最も重要なのは、このgimp全体のために、ファイルを読み取ることです。

 package jna; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.channels.FileChannel; import com.sun.jna.platform.win32.Advapi32; import com.sun.jna.platform.win32.WinBase; import com.sun.jna.platform.win32.WinDef.DWORD; import com.sun.jna.platform.win32.WinNT.HANDLE; import com.sun.jna.platform.win32.WinNT.HANDLEByReference; public class JnaTest { private static final String fileName = "D:\\user1\\hitman.jpg"; public static void main(String[] args) { FileInputStream in = null; FileOutputStream out = null; FileChannel fcin = null; FileChannel fcout = null; try { File testAccess = new File(fileName); if (testAccess.canRead()) { System.out.println("     "); }else{ System.out.println("     "); } //  // CREATE_SUSPENDED DWORD flag = new DWORD(4L); ThreadFunc func = new ThreadFunc(); func.setFile(fileName); HANDLE hThread = MoreKernel32.instance.CreateThread(null, null, func, null, flag, null); if (hThread == null) { System.out.println("  "); return; } //    HANDLEByReference phToken = new HANDLEByReference(); Boolean logonResult = Advapi32.INSTANCE.LogonUser("user1", "HORROR", "1234567q-",WinBase.LOGON32_LOGON_NETWORK, WinBase.LOGON32_PROVIDER_DEFAULT, phToken); if (logonResult == false) { System.out.println("  "); return; } //   HANDLEByReference pThread = new HANDLEByReference(hThread); Boolean setTokenResult = MoreAdvapi32.instance.SetThreadToken(pThread, phToken.getValue()); if (setTokenResult == false) { System.out.println("  "); return; } //     MoreKernel32.instance.ResumeThread(hThread); MoreKernel32.instance.WaitForSingleObject(hThread, -1); MoreKernel32.instance.CloseHandle(hThread); MoreKernel32.instance.CloseHandle(phToken.getValue()); //  in = func.getIn(); if (in == null) { return; } out = new FileOutputStream("D:\\test.zip"); fcin = in.getChannel(); fcout = out.getChannel(); fcin.transferTo(0, fcin.size(), fcout); } catch (Exception e) { e.printStackTrace(); } finally { //  if (fcin != null) { try { fcin.close(); } catch (IOException e) { e.printStackTrace(); } } if (fcout != null) { try { fcout.close(); } catch (IOException e) { e.printStackTrace(); } } if (in != null) { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } if (out != null) { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
      
      





このコードをさらに詳しく考えてみましょう。

最初に、ストリームのクラスを作成し、開くファイルを指定します。 次に、2つのパラメーター(ファイルを開くためのクラスへの参照とCREATE_SUSPENDEDを作成するためのフラグ)を使用してCreateThread関数を実行します。 実行後、スレッドへのハンドルを取得します。 次に、ハンドルがnullかどうかを確認し、ストリームが作成されていないので、作業を完了する必要があります。

コードの2番目のブロックはログイン用です。 ここで、LogonUser関数を開始する前に、ユーザートークンが書き込まれるハンドルへのリンクを作成する必要があります。 関数を開始するためのパラメーターとして、ログイン、ドメイン、パスワード、メモリポインター、トークンおよび追加のユーザーログインパラメーターが書き込まれる場所を指定します。 関数実行の結果として、trueおよびユーザートークンまたはfalseを取得します。 次に戻り値を確認し、falseの場合はプログラムを終了します。

次のブロックは非常に単純です。そこでストリームを開始し、完了するまで待機して、トークンを閉じます。

ファイルの読み取りも複雑ではありません-FileInputStreamとFileOutputStreamからファイルチャネルを取得し、ファイルをコピーします。

リソースクロージャは標準で、Javaで受け入れられます-finallyブロックで。

次に、いくつかのメモ:

1)ストリームを作成するとき、ストリームの存続期間を通じてストリームで呼び出されるクラスへの参照を保持する必要があります。 このクラスへの外部アクセスが不要な場合でも。 そうしないと、クラスがガベージコレクターによって削除される可能性があり、誤ったデータで満たされたメモリにアクセスしようとします。 実際には、JNA Webサイトに書かれていましたが、後で見つけたのは、いくつかのAccessエラーを見つけたときだけです。

2)リソースを開いて別のストリームに転送すると、アクセス権が保存されます。 ここでこれを確認できます。ファイルは1人のユーザーのアクセス権でストリームで開かれており、ファイル自体は別の権限で別のストリームで読み取られます。



実行例



次に、プログラムの実行を確認します。 最初に、フォルダーとファイルを作成します。





次に、このフォルダーにアクセス権を割り当てます。





図1フォルダーuser1





図2ユーザーuser1のアクセスを許可





図3ユーザーTimのアクセスを許可



プログラムを実行します。

ファイルはメインストリームから読み取ることができます。

ファイルは追加ストリームから読み取ることができます。



次に、ユーザーのTimフォルダーへのアクセスを拒否します。





図 4ユーザーTimのフォルダーへのアクセス許可を拒否します



エクスプローラーでフォルダーを開いてみましょう。 エラーメッセージが表示されます。





図 5フォルダーを開くときのエラーメッセージ



次に、プログラムを実行します。

メインストリームからファイルを読み取れません

ファイルは追加ストリームから読み取ることができます。



リソースリンク



Jna



ログオンユーザー



作成スレッド



再開スレッド



SetThreadToken



All Articles