SpringSecurity勉強メモ


# Why?

最近ios+SpringBootでアプリ開発的なことをしており、勉強しながら実装してるのでメモを残したい。
できれば毎日。
では行ってみよう。

# tl;dr
SpringBootで簡易にサーバーを立てたい
サーバーに対してid/passのログイン認証機能をつける
認証通れば200番、認証エラーで401、そのほかのエンドポイントで403を返すまでやる
db使わずにインメモリーでユーザー情報を持てるので、一旦それで実装

# 前提

- gradle
- kotlin
- SpringBoot
- 認証機能にSpringSecurityを使います



# 必要な実装


```gradle:build.gradle
dependencies {
    implementation('org.springframework.boot:spring-boot-starter-security')
    implementation('org.springframework.boot:spring-boot-starter-web')
}
```


```kotlin:SecurityConfig.kt

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Bean
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.security.provisioning.InMemoryUserDetailsManager
import javax.servlet.http.HttpServletResponse


// このアノテーションをつけて
@EnableWebSecurity
class SecurityConfig : WebSecurityConfigurerAdapter() { // このクラスを継承した設定ファイル用のclassを作成
    @Autowired
    lateinit var passwordEncoder: PasswordEncoder // パスワードハッシュ用のモジュール

    @Autowired
    fun configureGlobal(auth: AuthenticationManagerBuilder) {
        // インメモリーで認証情報を持てるInMemoryUserDetailsManagerをAuthenticationManagerBuilderに渡してあげる。
        auth.userDetailsService(createInMemoryUserDetailsManager())
                .passwordEncoder(passwordEncoder)
    }

    @Bean
    fun createInMemoryUserDetailsManager(): InMemoryUserDetailsManager {
        return InMemoryUserDetailsManager()
    }

    override fun configure(http: HttpSecurity) {
        // 一旦認証を通さなくてよいエンドポイントはなしで。追加もできる。
        http
                .authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginProcessingUrl("/login") // このloginのエンドポイントは固定？
                .usernameParameter("username") // リクエストパラメータ名
                .passwordParameter("password")
                // 認証成功の処理
                .successHandler { _, response, _ ->
                    response.status = HttpServletResponse.SC_OK
                    response.writer.write("Login Successful")
                }
                // 認証失敗の処理
                .failureHandler { _, response, _ ->
                    response.status = HttpServletResponse.SC_UNAUTHORIZED
                    response.writer.write("Unauthorized")
                }
                .and()
                .exceptionHandling()
                .authenticationEntryPoint{ _, response, _ ->
                    //それ以外の処理。未認証で他ののエンド叩こうとするとエラーになる。
                    response.status = HttpServletResponse.SC_FORBIDDEN
                    response.writer.write("Forbidden")
                }
                .and()
                .csrf()
                .disable()
    }

}
```

```kotlin:DummyDatabaseInitializer.kt

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Bean
import org.springframework.stereotype.Component
import org.springframework.security.core.userdetails.User
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.security.core.GrantedAuthority
import org.springframework.security.provisioning.InMemoryUserDetailsManager

// このアノテーションでAutoWiredしてくれる
// ロード時にDIコンテナに置いてくれる感じ？？？
@Component
class DummyDatabaseInitializer {

    @Autowired
    lateinit var passwordEncoder: PasswordEncoder

    @Autowired
    lateinit var inMemoryUserDetailsManager: InMemoryUserDetailsManager

    // ユーザーオブジェクトを足すだけ。設定したidとpassでログインできる
    @Bean
    fun addDummyData() {
        var user1 = User(
                "〜〜〜〜@example.com",
                passwordEncoder.encode("passowrd〜〜〜"),
                true,
                true,
                true,
                true,
                listOf(GrantedAuthority { "USER" })
        )

        inMemoryUserDetailsManager.createUser(user1)

    }
}
```



# 参考


https://qiita.com/nenokido2000/items/22a97a26a5858ddb164f
https://qiita.com/yokobonbon/items/6459bec1f09b57982bc1
https://qiita.com/opengl-8080/items/032ed0fa27a239bdc1cc
https://qiita.com/NagaokaKenichi/items/2742749a93df15b55d24
https://qiita.com/KevinFQ/items/abc7369cb07eb4b9ae29