【Spring Security】ログイン機能を作成

SpringBootでログインログアウト機能を実装したいと思います。

本記事では、長編となるため、ログイン機能のみを紹介します。

ログアウト機能はこちらの記事からお願い致します。

SpringBootのプロジェクトを生成

こちらのサイトからSpringBootのプロジェクトを作成しましょう。

実行環境

ProjectGradle
LanguageJava
SpringBoot3.1.1
PackagingJar
Java17

Dependenciesに入れるもの

  • Spring Web
  • Thymeleaf
  • Spring Security
  • PostgresSQL Driver

ログイン、ログアウト機能ということで、ユーザーやパスワードを記憶するデータベースを使います。

今回は、PostgreSQLというデータベースを使用します。

以上を選択して、プロジェクトを生成しましょう。

PostgresSQLをインストールする

データベースを使用するため、PostgreSQLをインストールする必要があります。

本記事では、SpringBootの内容に焦点を当てているため、割愛させてください。

ネットを調べていただければ、たくさん情報があります。

PostgresSQLにテーブルを生成

ユーザー情報を格納するテーブルを生成します。

PostgreSQLにログインして、新しくデータベースを作成しましょう。

postgres=# create database authdb;
CREATE DATABASE

新しく作ったデータベース「authdb」に移動します。

postgres=# \c authdb;
データベース"authdb"にユーザ"postgres"として接続しました。
authdb=#

テーブルを生成します。

ユーザーとパスワードを管理するusersテーブルとそのユーザーと権限を管理するauthoritiesテーブルを生成します。

authdb=# create table users(
authdb(# id serial not null primary key,
authdb(# username varchar(100) not null,
authdb(# password varchar(100) not null,
authdb(# enabled boolean not null default false
authdb(# );
CREATE TABLE
authdb=# create table authorities(
authdb(# id serial not null primary key,
authdb(# username varchar(100) not null,
authdb(# authority varchar(100) not null
authdb(# );
CREATE TABLE
authdb=#

application.propertiesを編集

生成した当初は、何も書かれていないファイルだと思います。

こちらのファイルに以下のコードを入力しましょう。

spring.datasource.url=jdbc:postgresql://localhost:5432/authdb
spring.datasource.username=postgres
spring.datasource.password=postgres
spring.datasource.driverClassName=org.postgresql.Driver

1行目にpostgreSQLのポート番号とデータベース名を記述

2行目と3行目は、postgreSQLに入る際のユーザ名とパスワードを入力しています。

4行目は、postgreSQLのドライバークラス名になります。

SpringSecurityのメソッドセキュリティ

本記事では、SpringSecurityのメソッドセキュリティを使用します。

メソッドセキュリティとは、メソッドごとに認証(アクセスできるロール)を指定できる特徴があります。

管理者のみ実行したい場合やすべてのユーザが実行したい場合のメソッドごとに指定できるのです。

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

では、SpringBootの○○Application.javaと同じ階層にSecurityConfig.javaを作成しましょう。

そして、以下のようなコードを記述しましょう。

エディタがVSCodeであれば、import文を書かなくてもTabキーで補完されます。

また、補完されなかったときは、赤い波線からQuickFixでimport~を選択しましょう。

package com.example.demo;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableMethodSecurity
public class SecurityConfig {
    @Autowired
    private DataSource dataSource;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
        http.authorizeHttpRequests(authorize->{
            authorize.anyRequest().permitAll();
        });
        http.formLogin(form->{
            form.defaultSuccessUrl("/home");
        });
        return http.build();
    }
}

12,13行目に、@Configurationと@EnableMethodSecurityがあります。

これで、メソッドセキュリティを使いますよと宣言しています。因みに@は、アノテーションを言います。

21行目ので、すべてのリクエストのアクセスを許可しています。

23~25行目は、ログイン成功時のURL先を指定しています。ログイン成功すると”/home”に移動します。

ログイン後のページの生成

ログイン成功時のページを作成しましょう。

ログインできたか確認するだけなので、簡素なページにします。

resources/templatesにhome.htmlを作ります。

<!DOCTYPE html>
<html>
    <meta charset="UTF-8">
    <body>
        <h2>ようこそ</h2>
    </body>
</html>

コントローラの生成

SecurityControllerを生成します。

package com.example.demo;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class SecurityController {
    @RequestMapping("/home")
    @PreAuthorize("hasRole('USER')")
    public ModelAndView index(ModelAndView mav) {
        mav.setViewName("home");
        return mav;
    }
}

10行目で/loginにアクセスするとhomeメソッドが実行されます。

homeメソッドは、home.htmlを返します。

しかし、ログインされていない場合、自動的に/loginにリダイレクトされ、ログインフォームが表示されます。

アプリケーションを実行し、localhost:8080/homeにアクセスしましょう。

ログインフォームが出てきて、localhost:8080/loginに変わったでしょうか。ここで、ログインをし、成功するとhomeメソッドが実行され、home.htmlが返されます。

しかし、現状では、ユーザーが登録されていないためログインできません。

したがって、ユーザーを登録しましょう。

ユーザーの登録

SecurityConfig.javaに以下のコードを追記します。

  @Bean
    public UserDetailsManager userDetailsManager() {
        JdbcUserDetailsManager users = new JdbcUserDetailsManager(this.dataSource);
        users.createUser(makeUser("user","password","USER"));
        return users;
    }
    private UserDetails makeUser(String user,String pw,String role) {
        return User.withUsername(user)
        .password(
            PasswordEncoderFactories
            .createDelegatingPasswordEncoder()
            .encode(pw))
            .roles(role)
            .build();
    }

文頭に以下のimport文を宣言する必要があります。

import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.provisioning.JdbcUserDetailsManager;
import org.springframework.security.provisioning.UserDetailsManager;

これで、アプリケーションを実行すると

ユーザー名:user、パスワード:password、権限:USERというユーザー情報が登録されます。

アプリケーションを実行してlocalhost:8080/homeにアクセスしましょう。

ログインフォームにuserとpasswordを入力し、ログインしてみます。

ようこその文字がでたらログイン成功です。

また、ユーザーの登録の際に追記したコードは、消しておくコメントアウトしておきましょう。

なぜなら、アプリケーションを実行毎にユーザーが追加されてしまうからです。

そして、以下のコードを追記します。

    @Bean
    public UserDetailsManager userDetailsManager() {
        return new JdbcUserDetailsManager(this.dataSource);
    }

こうすることでアプリケーションを再度実行してもログイン機能が正常に動きます。

以上までがSpringSecurityを用いたログイン機能の実装でした。

続きのログアウト機能はこちらの記事からお願い致します。