36
38

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Spring SecurityでWeb APIのAPIキー認証を作成する

Last updated at Posted at 2018-03-31

やりたいこと

Spring SecurityでWeb API呼び出し時などで、リクエスト毎にヘッダのAPIキーで認証する。

Javaによる例は個人ブログの記事【Spring】Spring Securityを使ってWeb APIの認可を行うに載せています。この記事では@PreAuthorizeによる権限チェックの例も載せています。

実現方法

  1. セッションを使用せずリクエスト毎に認証を実施する設定を行う
  2. リクエストヘッダ中のAPIキーによる認証処理を行うためのフィルター、サービスを作成する
  3. 2.で作成したフィルター、サービスを登録

1.リクエスト毎に認証を実施する設定

WebSecurityConfigurerAdapterのconfigureメソッドでセッションのポリシーをステートレスに設定する。
具体的には以下のようにSessionCreationPolicy.STATELESSを設定する。

@Configuration
@EnableWebSecurity
class SecurityConfig: WebSecurityConfigurerAdapter() {

    override fun configure(http: HttpSecurity?) {
        http
                ?.authorizeRequests()
                ?.antMatchers("/hello/**")?.hasAnyRole("USER", "ADMIN")
                ?.antMatchers("/admin/**")?.hasRole("ADMIN")
                ?.anyRequest()
                ?.authenticated()
                ?.and()
                ?.addFilter(preAuthenticatedProcessingFilter())
                ?.exceptionHandling()
                ?.authenticationEntryPoint(http401UnauthorizedEntryPoint())

        // リクエスト毎に認証を実施
        http?.sessionManagement()?.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
    }
    ....

2. リクエストヘッダ中のAPIキーによる認証処理を作成

Spring SecurityのPre-Authenticationを使用する。AbstractPreAuthenticatedProcessingFilterとAuthenticationUserDetailsServiceを使用しリクエストヘッダーからAPIキーを取得するフィルターとAPIキーによる認証を行うサービスを作成する。

フィルターの作成

AbstractPreAuthenticatedProcessingFilterを継承したクラスを作成し、getPreAuthenticatedCredentialsメソッドにリクエストヘッダ中のAPIキーを取り出す処理を作成する。

class MyPreAuthenticatedProcessingFilter: AbstractPreAuthenticatedProcessingFilter() {

   // APIキーを取得
   override fun getPreAuthenticatedCredentials(request: HttpServletRequest?): Any {
       return request?.getHeader("X-Auth-Token") ?: ""
   }

   override fun getPreAuthenticatedPrincipal(request: HttpServletRequest?): Any {
       return ""
   }
}

サービスの作成

以下のようにAuthenticationUserDetailsServiceを継承したクラスを作成し、取得したAPIキーによる認証処理を作成する。
型パラメータにはPreAuthenticatedAuthenticationTokenを指定する。
ここでは、テストのため適当な処理を書いているが、外部APIやDBを使ってAPIキーの検証する処理を書く。

class MyAuthenticationUserDetailService: AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> {

    override fun loadUserDetails(token: PreAuthenticatedAuthenticationToken?): UserDetails {
    
        // MyPreAuthenticatedProcessingFilterで取得したAPIキー
        val credential = token?.credentials
        
        if (credential.toString() == "") {
            throw UsernameNotFoundException("User not found")
        }

        return when (credential) {
            "token1" -> User("user", "", AuthorityUtils.createAuthorityList("ROLE_USER"))
            "token2" -> User("admin", "", AuthorityUtils.createAuthorityList("ROLE_ADMIN"))
            else -> User("user", "", AuthorityUtils.createAuthorityList("ROLE_INVALID") )
        }
    }
}

3. フィルター、サービスを登録

作成したフィルータ、サービスがDIされるように以下のように Bean定義を追加する。

@Configuration
@EnableWebSecurity
class SecurityConfig: WebSecurityConfigurerAdapter() {

    override fun configure(http: HttpSecurity?) {
        http
                ?.authorizeRequests()
                ?.antMatchers("/hello/**")?.hasAnyRole("USER", "ADMIN")
                ?.antMatchers("/admin/**")?.hasRole("ADMIN")
                ?.anyRequest()
                ?.authenticated()
                ?.and()
                ?.addFilter(preAuthenticatedProcessingFilter())

        http?.sessionManagement()?.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
    }

    // サービス
    @Bean
    fun authenticationUserDetailsService(): AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> {
        return MyAuthenticationUserDetailService()
    }

    // フィルター登録
    @Bean
    fun preAuthenticatedAuthenticationProvider(): PreAuthenticatedAuthenticationProvider {
        return PreAuthenticatedAuthenticationProvider().apply {
            setPreAuthenticatedUserDetailsService(authenticationUserDetailsService())
            setUserDetailsChecker(AccountStatusUserDetailsChecker())
        }
    }

    // フィルター
    @Bean
    fun preAuthenticatedProcessingFilter(): AbstractPreAuthenticatedProcessingFilter {
        return MyPreAuthenticatedProcessingFilter().apply {
            setAuthenticationManager(authenticationManager())
        }
    }
}
36
38
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
36
38

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?