【Kotlin】Spring Boot2 での Spring Security 設定（認証なし）

# Spring Boot2 での Spring Security 設定
- ログイン不要ウェブサイトの場合（デフォルトでは全て認証が必要な状態となる）
- csrf チェック適用設定
- アクセスエラーハンドラ
- Kotlin

# 実施環境
開発PC: Windows 10
Java: 8
Kotlin: 0.8.19
Eclipse: 2019-06 (4.12.0)

## 設定用のクラス
#### WebSecurityConfigurerAdapter を実装した SecurityConfig クラス
- このクラスを設置すると自動的に設定される
- 基本全てのページで認証不要
- /api/ のURLは csrf チェックをしない 
- GET の場合は csrf チェックをしない

```kotlin
class SecurityConfig {

    @Configuration
    @EnableWebSecurity
    open class SecurityConfig : WebSecurityConfigurerAdapter() {

        override fun configure(http: HttpSecurity) {
            // 全てのページへのアクセスを許可
            http.authorizeRequests()
                .antMatchers("/**").permitAll()

            // csrf の適用設定
            val csrfRequestMatcher = object:RequestMatcher {
                val disabledRequestMatcher = AntPathRequestMatcher("/api/**")
                override fun matches(request: HttpServletRequest):Boolean {
                    // GETならチェックしない
                    if ("GET" == request.getMethod()) {
                        return false
                    }
                    // 指定されたURLならチェックしない
                    if (disabledRequestMatcher.matches(request)) {
                        return false
                    }
                    return true
                }
            }
            // csrf
            http
                .csrf()
                .requireCsrfProtectionMatcher(csrfRequestMatcher)

            // アクセスエラーハンドラ
            http
                .exceptionHandling()
                .accessDeniedHandler(getAccessDeniedHandler())
        }

        // カスタムアクセスエラーハンドラ
        @Bean
        open fun getAccessDeniedHandler():AccessDeniedHandler {
            return CustomAccessDeniedHandler()
        }
    }
}
```

#### AccessDeniedHandlerImpl を実装した CustomAccessDeniedHandler クラス
- csrf チェックで引っかかった場合
	- /sample/ URLの場合は /sample?timeout へ転送
	- その他は /?timeout へ転送

```kotlin
internal class CustomAccessDeniedHandler: AccessDeniedHandlerImpl() {

    @Throws(IOException::class, ServletException::class)
    override fun handle(request: HttpServletRequest,
                        response: HttpServletResponse,
                        accessDeniedException: org.springframework.security.access.AccessDeniedException) {

        // csrf無効の場合 + csrf がない場合
        if (accessDeniedException is MissingCsrfTokenException ||
            accessDeniedException is InvalidCsrfTokenException) {

            val uri = request.getRequestURI()
            // url に sample があれば timeout 転送
            if (uri.contains("/sample/")) {
                response.sendRedirect(request.getContextPath() + "/sample?timeout")
                return
            }
            // 通常は top へ転送
            else {
                response.sendRedirect(request.getContextPath() + "/?timeout")
                return
            }
        }
        super.handle(request, response, accessDeniedException)
    }

    fun isSessionInvalid(request: HttpServletRequest):Boolean {

        try {
            val session = request.getSession(false)
            return session == null || !request.isRequestedSessionIdValid()
        }
        catch (e: IllegalStateException) {
            return true
        }
    }
}
```

以上、お疲れさまでした！