Spring Security | 認証 | Authentication
AuthenticationはSpring Securityを構成する重要なクラスの一つです。ユーザー名や付与された権限など、認証済みユーザーの情報を保持します。
認証に成功すると、AuthenticationProviderは認証済みのAuthenticationオブジェクトを作成して返します。isAuthenticated()はtrueを返します。その後の認可チェックなどで、このオブジェクトを参照します。
Authenticationの取得と参照できる情報
SecurityContextHolderSampleServlet.java
package sample.spring.security.servlet;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import static java.util.stream.Collectors.*;
@WebServlet("/authentication")
public class SecurityContextHolderSampleServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
System.out.println("[authorities]");
System.out.println(" " + auth.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(joining("\n ")));
WebAuthenticationDetails details = (WebAuthenticationDetails) auth.getDetails();
System.out.println("[details]");
System.out.println(" IP Address : " + details.getRemoteAddress());
System.out.println(" Session ID : " + details.getSessionId());
UserDetails principal = (UserDetails) auth.getPrincipal();
System.out.println("[principal]");
System.out.println(" username : " + principal.getUsername());
System.out.println(" password : " + principal.getPassword());
System.out.println("[credentials]");
System.out.println(" " + auth.getCredentials());
}
}
実行結果
[authorities]
USER
[details]
IP Address : 0:0:0:0:0:0:0:1
Session ID : 0225051BCDFE2C34D55DF4FA9D9685C2
[principal]
username : hoge
password : null
[credentials]
null
- 現在のリクエストに関連する
Authenticationは、SecurityContextHolder.getContext().getAuthentication()で取得します。SecurityContextHolder.getContext()は、現在のリクエストに関連するSecurityContextを返します。
Authentication.getAuthorities()は、ログインユーザーに付与された権限を返します。Authentication.getDetails()は、デフォルトでWebAuthenticationDetailsを返します。- IPアドレスとセッションIDを取得できます。
Authentication.getPrincipal()は、ログインユーザーのUserDetailsを返します。Authentication.getCredentials()は認証に使用する情報、通常はログインパスワードを返します。
安全のため、ログイン成功後にAuthenticationManager(ProviderManager)がパスワード情報をnullへ設定して明示的に削除します。eraseCredentialsAfterAuthenticationオプションで削除を無効にできます。
Authenticationの保存場所
認証後、Authenticationを保持するSecurityContextはデフォルトでHttpSessionへ保存されます。
同じセッションで再度アクセスすると、保存されたSecurityContextが取得され、SecurityContextHolderへ設定されます。
デフォルトでは、SecurityContextHolderはThreadLocalを通じてSecurityContextを保存します。これにより、現在のリクエストに関連するコンテキストへグローバルにアクセスできます。
厳密には、SecurityContextHolderはSecurityContextHolderStrategyのインスタンスを保持します。ThreadLocalSecurityContextHolderStrategyがSecurityContextをThreadLocalへ保存します。
SecurityContextPersistenceFilterがSecurityContextの読み書きとリクエスト単位のThreadLocalのクリアを行います。
ThreadLocal以外の保存方法
SecurityContextHolderStrategyには、ThreadLocalSecurityContextHolderStrategy以外にも2つの実装があります。
GlobalSecurityContextHolderStrategy
アプリケーション全体で1つのSecurityContextを保存します。
実行ユーザーが1人だけの可能性があるスタンドアロンアプリケーションなどで役立ちます。複数のスレッドを作成しても、すべてで同じ認証情報を共有できます。
InheritableThreadLocalSecurityContextHolderStrategy
新しいスレッドを作成した場合に、親スレッドのSecurityContextを共有します。
複数ユーザーが利用するアプリケーションで、ユーザーごとの処理から新しいスレッドを作成し、バックグラウンド処理を実行する場合に役立ちます。スレッドは分かれますが、認証情報は親スレッドから継承します。
保存方式は次のいずれかの方法で変更できます。
システムプロパティで指定する
システムプロパティspring.security.strategyをJVM起動オプションで渡します。
Tomcat起動時に指定する場合
$ set JAVA_OPTS=-Dspring.security.strategy=MODE_INHERITABLETHREADLOCAL
$ cd %TOMCAT_HOME%\bin
$ startup.bat
staticメソッドで指定する
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
SecurityContextHolderにはsetStrategyName(String)が用意されています。引数にはSecurityContextHolderで定義された定数を指定します。