@AuthenticationPrincipal
Spring Securityでは、セッションユーザ情報をシンプルに取得できるアノテーション @AuthenticationPrincipal
が存在します。
メソッドのパラメーターまたはメソッドの戻り値に Authentication.getPrincipal()をバインドするアノテーションです。
型が UserDetails
実装型であるメソッドの引数、あるいは戻り値に付与することで、Authentication.getPrincipal()の呼び出し結果を埋め込んでくれるアノテーションです。
UserDetails
はSpring Securityにおいてユーザ被認証主体の情報を保持する役割を持ち、Spring Security上では UserDetails
の持つ情報を使って認証・認可の仕組みを提供しますが、その UserDetails
オブジェクトを簡単に取得できます。
よくあるのが、Controllerメソッドの引数に埋め込んでログイン済みユーザ情報を使用するシーンです。
@RestController @RequestMapping("/users") class UserController { /** * ユーザ取得 - 【GET】/users/me */ @GetMapping("/me") fun get(@AuthenticationPrincipal user: User): UserResponse = UserResponse.create(user) // Userからユーザ情報レスポンスを作成 }
上記Controllerクラスではログインユーザ情報を取得するAPIを実装していますが、
その際に @AuthenticationPrincipal
で取得したログインユーザ情報を使用しています。
@AuthenticationPrincipal
を使用するための準備
このように便利なアノテーションですが、利用のために幾つか下準備が必要です。
よく失念するので、手順の再確認も兼ねてメモしました。
UserDetails
実装クラスの定義
何はともあれインスタンスのクラスがないことには始まりません。
Spring Securityでのユーザ認証・認可を利用する場合、既に作成していることと思います。
data class User( val email: String, val pass: String, val roleType: RoleType, ) : UserDetails { override fun getAuthorities(): MutableCollection<out GrantedAuthority> { return AuthorityUtils.createAuthorityList(roleType.name) } override fun isEnabled(): Boolean { return true } override fun getUsername(): String { return email } override fun isCredentialsNonExpired(): Boolean { return true } override fun getPassword(): String { return pass } override fun isAccountNonExpired(): Boolean { return true } override fun isAccountNonLocked(): Boolean { return true } }
UserDetailsService
実装の定義
UserDetails
オブジェクトをハンドルするサービスクラスも定義します。
既にSpring Securityが提供している UserDetailsService
を実装したServiceクラスを定義します。
loadUserByUsername
をオーバーライドし、先程作成した UserDetails
実装クラスオブジェクトを提供するロジックを記述します。
@Service class UserDetailsServiceImpl : UserDetailsService { @Throws(UsernameNotFoundException::class) override fun loadUserByUsername(email: String?): UserDetails { // Userクラスオブジェクトを返す } }
WebSecurityConfigurerAdapter
派生クラスで UserService
を登録
WebSecurityConfigurerAdapter
派生クラスを定義し、 configure
メソッド内で先程の UserService
を登録します。
派生クラスはBeanとして登録し、@EnableWebSecurity
も忘れずに。
@Configuration @EnableWebSecurity class SecurityConfig( @Autowired private val userDetailsService: UserDetailsService, ) : WebSecurityConfigurerAdapter() { override fun configure(auth: AuthenticationManagerBuilder?) { auth?.userDetailsService(userDetailsService) } ... }
呼び出し
あとは実際に呼び出してみます。null
でなければOK。
@RestController @RequestMapping("/users") class UserController { /** * ユーザ取得 - 【GET】/users/me */ @GetMapping("/me") fun get(@AuthenticationPrincipal user: User): UserResponse = UserResponse.create(user) // Userからユーザ情報レスポンスを作成 }