4
4

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-Boot ~Spring-Securityを使ったDB認証~

Last updated at Posted at 2021-08-14

はじめに

最近SpringBootを学び始めたプログラミング初学者です。
SpringSecurityを使用したDB認証を実装しようとしたのですが、思った以上に詰まってしまったので備忘録も兼ねて記事にしました。
本当に必要最小限の機能しか実装していないので、とりあえずSpringSecurityを使ったDB認証がしたい!という人向けの記事になるかと思います。

環境

  • Eclipse 4.19.0
  • SpringBoot 2.5.3
  • Java11
  • ビルドツール:Maven
  • テンプレートエンジン:Thymeleaf
  • O/Rマッパー:MyBatis
  • DB:MySQL

SpringInitializrの設定Spring_initializr.png

出来上がったpom.xml

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.5.3</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>Login6</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>Login6</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>11</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>2.2.0</version>
		</dependency>
		<dependency>
			<groupId>org.thymeleaf.extras</groupId>
			<artifactId>thymeleaf-extras-springsecurity5</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

プロジェクトのフォルダ構成

パッケージ・エクスプローラー
  • UserModel.java

    DBから取得したユーザ―情報を格納するEntityクラス

    UserDetailsをimplementsする
  • UserListMapper.java

    Mapperインターフェース (MyBatisで使用するため詳細説明は省略)
  • UserListMapper.xml

    Mapperインターフェースに対応するXMLファイル(MyBatisで使用するため詳細説明は省略)
  • UserService.java

    サービスクラス

    UserDetailsServiceをimplementsして実装する
  • MainController.java

    コントローラークラス
  • Login6Application.java

    SpringBootの起動クラス
  • WebSecurityConfig.java

    SpringSecurityの設定を行うクラス

    WebSecurityConfigurerAdapterをextendsして実装する
  • loginForm.html

    ログイン認証を行う画面
  • success.html

    ログイン認証に成功した時に遷移する画面
  • application.properties

    DBへの接続情報を記述しているファイル
  • mybatis-config.xml

    MyBatisの設定ファイル
  • pom.xml

    プロジェクトのビルドに関する情報やライブラリの依存関係などが記述されているファイル

Login6Application.java

SpringBootの起動クラスです。
MyBatis用の設定を追記していること以外はデフォルトのままです。

Login6Application.java
package com.example;

import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.ClassPathResource;

@SpringBootApplication
@MapperScan(basePackages="com.example.persistence")
public class Login6Application {

	public static void main(String[] args) {
		SpringApplication.run(Login6Application.class, args);
	}

	
    // MyBatisの設定
    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
        // コンフィグファイルの読み込み
        sessionFactory.setConfigLocation(new ClassPathResource("/mybatis-config.xml"));
        return sessionFactory.getObject();
    }
}

MainController.java

コントローラークラスです。

MainController.java
package com.example.web;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class MainController {

	@RequestMapping("/loginForm")
	public String loginForm() {
		return "loginForm";
	}

	@RequestMapping(value = "/loginForm", params = "error")
	public String error(Model model) {
		model.addAttribute("errorMsg", "ログイン認証に失敗しました");
		return "loginForm";
	}

	@RequestMapping("/success")
	public String success() {
		return "success";
	}
}

WebSecurityConfig.java

SpringSecurityの設定用クラスです。
詳細はコード内のコメントに記載していますが、このクラスでログイン認証の具体的な設定を行っています。

WebSecurityConfig.java
package com.example;

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.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.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import com.example.service.UserService;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

	@Autowired
	private UserService userService;
	
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //アクセスポリシーの設定
        http.authorizeRequests()
            //指定したパターンごとに制限をかける
            .antMatchers("/js/**", "/css/**").permitAll()//制限なし
            .anyRequest().authenticated();//上記以外は制限あり
        //フォーム認証の設定
        http.formLogin()
            /*ログインページの指定(指定なしの場合デフォルトのものが用意される)
              コントローラークラスでこのURLをマッピングしておく*/
            .loginPage("/loginForm")  
            /*ログイン処理のパス(このURLへリクエストが送られると認証処理が実行される)
              ログインページのformタグのth:action属性にこのURLを指定しておく*/
            .loginProcessingUrl("/login")
            /*ログインページのログイン情報入力欄のname属性に以下の名称を指定する*/
            .usernameParameter("user")
            .passwordParameter("pass")
            /*ログイン成功時に遷移するページ(trueで成功時は常にここに飛ぶ)
             コントローラークラスでこのURLをマッピングしておく*/
            .defaultSuccessUrl("/success", true)
            /*失敗時の遷移先、アクセス制限は解除する
              コントローラークラスでこのURLをマッピングしておく*/
            .failureUrl("/loginForm?error").permitAll();
    }
    
    /**
     * 認証するユーザー情報をデータベースからロードする処理
     * @param auth 認証マネージャー生成ツール
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        ///認証するユーザー情報をデータベースからロードする
        //その際、パスワードはBCryptでハッシュ化した値を利用する
        auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
    }
 
    /**
     * パスワードをBCryptでハッシュ化するクラス
     * ハッシュ化するクラスも幾つか種類があるみたいです
     * @return パスワードをBCryptで暗号化するクラスオブジェクト
     */
    @Bean
    PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
}

UserService.java

UserDetailsServiceをimplementsしているサービスクラスです。
実装する必要があるのはloadUserByUsernameメソッドだけで、ユーザーネーム(今回はid)を引数にユーザ情報を返す処理を記述します。
今回はO/RマッパーにMyBatisを使用しており、マッパーインタフェースのcertificateメソッドを呼んでいます。

UserService.java
package com.example.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import com.example.persistence.UserListMapper;

@Service
public class UserService implements UserDetailsService{
	@Autowired
	private UserListMapper userListMapper;

	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		return userListMapper.certificate(username);
	}
}

UserModel.java

DBから取得したユーザー情報を格納するEntityクラスです。
UserDetailsをImplementsして、UserDetailsに記述してあるメソッドを実装します。

UserModel.java
package com.example.domain;

import java.util.Collection;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

public class UserModel implements UserDetails{
	private String id;
	private String pass;

	@Override
	public Collection<? extends GrantedAuthority> getAuthorities() {
		//1つのユーザーがROLEを複数持つときに使うみたい
		return null;
	}
	@Override
	public String getPassword() {
               //パスワードを返す
		return pass;
	}
	@Override
	public String getUsername() {
               //ユーザー名(今回はid)を返す。
		return id;
	}
	@Override
	public boolean isAccountNonExpired() {
		//ユーザアカウントが認証期限切れしていないかどうかを返す
                //認証期限切れしている場合falseを返すが、今回は特に考慮していないので
                //固定でtrueを返しておく(以下メソッドでも同様の理由でtrueを返している)
		return true;
	}
	@Override
	public boolean isAccountNonLocked() {
		//ユーザアカウントがロックしていないかどうか
		return true;
	}
	@Override
	public boolean isCredentialsNonExpired() {
		//ユーザアカウントの資格が認証期限切れしていないかどうか
		return true;
	}
	@Override
	public boolean isEnabled() {
		//ユーザアカウントが無効になっていないか
		return true;
	}
}

UserListMapper.java

マッパーインターフェースクラスです。
MyBatisに関することなので説明は省略します。

UserListMapper.java
package com.example.persistence;

import org.apache.ibatis.annotations.Param;
import com.example.domain.UserModel;

public interface UserListMapper {
	public UserModel certificate(@Param("id") String id);
}

UserListMapper.xml

マッパーインターフェースクラスに対応するxmlファイルです。
同様に説明は省略します。

UserListMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC
        "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.example.persistence.UserListMapper">
    <select id="certificate" resultType="com.example.domain.UserModel">
    	SELECT id, pass FROM userlist WHERE id = #{id}
    </select>
</mapper>

loginForm.html

ログイン認証を行う画面です。
UserIDとPasswordの入力欄のname属性と、formタグのth:action属性はWebSecurityConfig.javaで設定しているものを記述します。

loginForm.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>ログイン認証</title>
</head>
<body>
	<form th:action="@{/login}" method="post">
	
		<span th:if="${errorMsg}" th:text="${errorMsg}"></span><br>
	
		UserID:<input type="text" name="user" /><br> 
		Password:<input type="password" name="pass" /><br>
		
		<input type="submit" value="認証"/>
	</form>
</body>
</html>

success.html

ログイン認証が成功した時に遷移する画面です。

success.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>認証成功</title>
</head>
<body>
認証成功!!!
</body>
</html>

application.properties

DBへの接続情報を記述しています。

application.properties
# DBのドライバ設定
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 接続用URL
spring.datasource.url=jdbc:mysql://XXX/XXX
# ユーザ名
spring.datasource.username=XXX
# パスワード
spring.datasource.password=XXX

mybatis-config.xml

MyBatisの設定情報を記述しています。

mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC
        "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true" />
    </settings>
</configuration>

テーブル定義

参考までにテーブル情報を載せておきます。
ちなみに、画面で入力されたパスワードはハッシュ化され、予めDBに登録してあるパスワード(のハッシュ値)と比較されます。
そのため、DBに格納するパスワードは予めハッシュ化しておく必要があります。
今回ハッシュ化に使用したBCryptPasswordEncoderクラスを利用して予めハッシュ化したパスワードをDBに登録しておくといいでしょう。

create.sql
CREATE TABLE userlist (
  id int NOT NULL,
  pass varchar(60) DEFAULT NULL,
  PRIMARY KEY (`id`)
) 

参考にした記事

SpringSecurity Reference
spring boot security + DB認証を試した時のポイント
最初のSpring Security - フォーム認証&画面遷移
Spring Security 使い方メモ 認証・認可
Spring-Bootでログイン機能を実装してみる

4
4
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
4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?