MENU

Spring Bootのログイン処理の基本実装

Spring Boot(Spring Security)を使って、簡単なログインフォームを作成し、認証機能を実装する方法をシンプルにまとめました。初心者の方でも試しやすい内容になっています!

データーベースを使った認証方法の解説はコチラをみてください。

目次

前提条件

  • Kotlinを使用
  • Spring Boot 3.4.1を使ったプロジェクト

必要な依存関係

Spring Boot Initializerで、以下の依存関係を追加してください:

  • Spring Web
  • Spring Security
  • Thymeleaf
  • Spring Boot DevTools(必要なら)

build.gradle.ktsに以下のような依存関係が含まれているはずです:

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-security")
    implementation("org.springframework.boot:spring-boot-starter-thymeleaf")
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("org.jetbrains.kotlin:kotlin-reflect")
    implementation("org.thymeleaf.extras:thymeleaf-extras-springsecurity6")
    developmentOnly("org.springframework.boot:spring-boot-devtools")
}

セキュリティ設定クラスの作成

次に、ログイン周りの設定をするためのクラスを作ります。このクラスでは、どのページにアクセスを許可するか、ログインページの設定、ログイン後の遷移先など、ログイン処理全体の詳細を決定します。

コード例

package com.example.securityconfig

import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.security.provisioning.InMemoryUserDetailsManager
import org.springframework.security.core.userdetails.User
import org.springframework.security.core.userdetails.UserDetailsService
import org.springframework.security.web.SecurityFilterChain

@Configuration
class SecurityConfig {

    // パスワードをハッシュ化するためのエンコーダーをBeanとして定義
    @Bean
    fun passwordEncoder(): PasswordEncoder = BCryptPasswordEncoder()

    // ユーザー情報をメモリ上に定義するサービスを設定
    @Bean
    fun userDetailsService(passwordEncoder: PasswordEncoder): UserDetailsService {
        val user = User.builder()
            .username("user") // ユーザー名を設定
            .password(passwordEncoder.encode("password")) // パスワードをハッシュ化して設定
            .roles("USER") // ユーザーに付与するロールを設定
            .build()
        return InMemoryUserDetailsManager(user) // メモリ上のユーザー管理サービスを返す
    }

    // セキュリティ設定の詳細を定義するBean
    @Bean
    fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
        http.authorizeHttpRequests { auth ->
            auth.requestMatchers("/login").permitAll() // ログインページは誰でもアクセス可能
                .anyRequest().authenticated() // その他のページは認証が必要
        }
        http.formLogin { form ->
            form.loginPage("/login") // ログインページを設定
                .failureUrl("/login?error") // ログイン失敗時のリダイレクト先
                .defaultSuccessUrl("/home", true) // ログイン成功時のリダイレクト先
                .permitAll() // ログインページへのアクセスを許可
        }
        http.logout { logout ->
            logout.logoutUrl("/logout") // ログアウト時のURLを設定
                .logoutSuccessUrl("/login") // ログアウト成功後のリダイレクト先
                .permitAll() // ログアウトへのアクセスを許可
        }
        return http.build() // 設定を反映
    }
}

ログインフォーム、ホーム画面の作成

次に、ログイン画面(login.html)とホーム画面(home.html)を作ります。

login.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Login</title>
</head>
<body>
<form th:action="@{/login}" method="post">
    <div>
        <label for="username">Username:</label>
        <input type="text" id="username" name="username">
    </div>
    <div>
        <label for="password">Password:</label>
        <input type="password" id="password" name="password">
    </div>
    <button type="submit">Login</button>

    <div th:if="${param.error}">
        ユーザー名またはパスワードが間違っています。
    </div>
</form>
</body>
</html>

home.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Home</title>
</head>
<body>
<h1>ようこそ!</h1>

<form th:action="@{/logout}" method="post">
    <button type="submit">ログアウト</button>
</form>
</body>
</html>

ログアウトのURLに対して、POSTでアクセスしないとセッションが破棄されないので注意

コントローラーの作成

最後に、ページを表示するためのコントローラーを作ります。

package com.example.securityconfig

import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.GetMapping

@Controller
class LoginController {

    // ログインページを表示するエンドポイント
    @GetMapping("/login")
    fun login(): String = "login" // login.htmlを返す

    // ホームページを表示するエンドポイント
    @GetMapping("/home")
    fun home(): String = "home" // home.htmlを返す
}

動作確認

プロジェクトを起動して、ブラウザでhttp://localhost:8080にアクセスします。

ログインフォームが表示されたら、以下の情報を使ってログインしてください:

  • ユーザー名: user
  • パスワード: password

ログイン成功後、ホーム画面に移動します。

ユーザー名、パスワードの組み合わせが間違っている場合は、「ユーザー名またはパスワードが間違っています。」と表示されるはずです。

意外なハマりポイント

Spring Securityには、CSRF(クロスサイトリクエストフォージェリ)攻撃対策のための機能がデフォルトで有効になっています。

CSRFは認証されたユーザーが意図せずに悪意のあるリクエストを送信してしまう攻撃手法です。

CSRF攻撃について詳しい解説は以下のリンクを参考にしてください:

POST、PUT、DELETEでパラメーターを送信するとき、CSRFトークンを送信しないと自動的にSpring Securityが403のステータスエラーを出力します。

ブラウザの「ソースコードを表示」等を使って、HTMLを覗いてみると、身に覚えのないname="_csrf”のinputが追加されているはずです。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Login</title>
</head>
<body>
<form action="/login" method="post"><input type="hidden" name="_csrf" value="-jL4Tq__qMcGSm1A08Jr9H0cNaKAk93Wd72zQZuQiKbXdN8EygCcL5qezP4rKAt1te9fxRh6GJvkq-j7QYqCcKry7pW0Tbpg"/>
    <div>
        <label for="username">Username:</label>
        <input type="text" id="username" name="username">
    </div>
    <div>
        <label for="password">Password:</label>
        <input type="password" id="password" name="password">
    </div>
    <button type="submit">Login</button>
</form>
</body>
</html>

このname=“_csrf”がない場合は、<form th:action="@{/login}" method="post”>thが抜けてないか、method=postが抜けてないか確認してください。

とりあえずサンプルでログイン認証処理を動かしたいなら、CSRF攻撃対策の機能を無効化することが出来ます。

基本的にcsrf.disable()設定は使わないようにしましょう。

// セキュリティ設定の詳細を定義するBean
@Bean
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
    // CSRF攻撃対策のトークンを無効化
    httpSecurity.csrf { csrf -> csrf.disable() }

    http.authorizeHttpRequests { auth ->
        auth.requestMatchers("/login").permitAll() // ログインページは誰でもアクセス可能
            .anyRequest().authenticated() // その他のページは認証が必要
    }
    http.formLogin { form ->
        form.loginPage("/login") // カスタムログインページを設定
            .failureUrl("/login?error") // ログイン失敗時のリダイレクト先
            .defaultSuccessUrl("/home", true) // ログイン成功時のリダイレクト先
            .permitAll() // ログインページへのアクセスを許可
    }
    http.logout { logout ->
        logout.logoutUrl("/logout") // ログアウト時のURLを設定
            .logoutSuccessUrl("/login") // ログアウト成功後のリダイレクト先
            .permitAll() // ログアウトへのアクセスを許可
    }
    return http.build() // 設定を反映
}

おわりに

以上で、Spring Securityを使った簡単なログイン処理が完成です!この仕組みを元にして、次はデータベースと連携して動的なユーザー管理を実現したり、JWT認証を活用してAPIを安全に保護する仕組みについて解説していく予定です。

Spring Bootで、データベースを使ったログイン認証の解説はコチラ

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

C#、C++、Java、Kotlinを使って開発をしているエンジニアです。最近はVue.jsにも触れています。
このブログではプログラミングの知識をシェアしています。
趣味のコーヒー(フレンチプレス愛用!)

目次