<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>ゴーゴーリラックスライフブログ</title>
	<atom:link href="https://gogorelaxlifeblog.com/feed/" rel="self" type="application/rss+xml" />
	<link>https://gogorelaxlifeblog.com</link>
	<description></description>
	<lastBuildDate>Tue, 25 Mar 2025 10:45:56 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.7.2</generator>
<atom:link rel="hub" href="https://pubsubhubbub.appspot.com"/><atom:link rel="hub" href="https://pubsubhubbub.superfeedr.com"/><atom:link rel="hub" href="https://websubhub.com/hub"/>	<item>
		<title>SpringSecurity6+MyBatisでroleを使った認可（実装編）</title>
		<link>https://gogorelaxlifeblog.com/springsecurity6role-implementation/</link>
					<comments>https://gogorelaxlifeblog.com/springsecurity6role-implementation/#respond</comments>
		
		<dc:creator><![CDATA[ごゆるり一族]]></dc:creator>
		<pubDate>Sat, 22 Mar 2025 06:31:06 +0000</pubDate>
				<category><![CDATA[技術]]></category>
		<category><![CDATA[spring]]></category>
		<guid isPermaLink="false">https://gogorelaxlifeblog.com/?p=1782</guid>

					<description><![CDATA[SpringSecurity6系は5.3までと比べるとSecurityConfigの書き方が変わってるので勉強を兼ねて独自のログイン画面を用意しroleを使った認可機能をjavaで作成してみました。 またMyBatisと [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>SpringSecurity6系は5.3までと比べるとSecurityConfigの書き方が変わってるので勉強を兼ねて独自のログイン画面を用意しroleを使った認可機能をjavaで作成してみました。</p>



<p>またMyBatisとH2 Databaseを使うことでDB構築をせずとも、手軽にDBと連携できるようにしてみました。</p>



<p>なお、記載内容が多くなったため、記事を2つに分けてます。</p>



<p>前編は以下をご参照ください。</p>



<figure class="wp-block-embed is-type-wp-embed"><div class="wp-block-embed__wrapper">

<a href="https://gogorelaxlifeblog.com/springsecurity6role-setup" title="SpringSecurity6+MyBatisでroleを使った認可（設定編）" class="blogcard-wrap internal-blogcard-wrap a-wrap cf"><div class="blogcard internal-blogcard ib-left cf"><div class="blogcard-label internal-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail internal-blogcard-thumbnail"><img decoding="async" width="160" height="90" src="https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/16-160x90.png" class="blogcard-thumb-image internal-blogcard-thumb-image wp-post-image" alt="" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/16-160x90.png 160w, https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/16-120x68.png 120w, https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/16-320x180.png 320w" sizes="(max-width: 160px) 100vw, 160px" /></figure><div class="blogcard-content internal-blogcard-content"><div class="blogcard-title internal-blogcard-title">SpringSecurity6+MyBatisでroleを使った認可（設定編）</div><div class="blogcard-snippet internal-blogcard-snippet">SpringSecurity6系は5.3までと比べるとSecurityConfigの書き方が変わってるので勉強を兼ねて独自のログイン画面を用意しroleを使った認可機能をjavaで作成してみました。またMyBatisとH2 Databaseを使うことでDB構築をせずとも、手軽にDBと連携できるようにしてみました。</div></div><div class="blogcard-footer internal-blogcard-footer cf"><div class="blogcard-site internal-blogcard-site"><div class="blogcard-favicon internal-blogcard-favicon"><img decoding="async" src="https://www.google.com/s2/favicons?domain=https://gogorelaxlifeblog.com" alt="" class="blogcard-favicon-image internal-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain internal-blogcard-domain">gogorelaxlifeblog.com</div></div><div class="blogcard-date internal-blogcard-date"><div class="blogcard-post-date internal-blogcard-post-date">2025.03.22</div></div></div></div></a>
</div></figure>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc1">開発環境</span></h2>
<p>私が構築した時の開発環境は以下の通りとなります。</p>


<figure class="wp-block-flexible-table-block-table"><table class="has-fixed-layout"><thead><tr><th>環境</th><th>バージョン</th></tr></thead><tbody><tr><td>Java</td><td>17</td></tr><tr><td>SpringBoot</td><td>3.4.3</td></tr><tr><td>SpringSecurity</td><td>6.4.3</td></tr><tr><td>thymeleaf</td><td>3.1.3.RELEASE</td></tr><tr><td>MyBatis</td><td>3.0.4</td></tr><tr><td>H2 Database</td><td>2.3.232</td></tr><tr><td>lombok</td><td>1.18.36</td></tr></tbody></table></figure>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc2">ソース構成</span></h2>
<p>ソース構成は以下の通りです。</p>


<pre class="wp-block-code nohighlight"><code>login-role-project
├── src
│   ├── main
│   │   ├── java
│   │   │   └── com
│   │   │       └── example
│   │   │           └── loginroleproject
│   │   │               ├── config
│   │   │               │   ├── SecurityConfig.java
│   │   │               │   └── SecurityProperties.java
│   │   │               ├── controller
│   │   │               │   ├── AdminController.java
│   │   │               │   ├── LoginController.java
│   │   │               │   └── MemberController.java
│   │   │               ├── entity
│   │   │               │   ├── LoginUsers.java
│   │   │               │   └── UserRole.java
│   │   │               ├── repository
│   │   │               │   ├── LoginUsersMapper.java
│   │   │               │   └── UserRoleMapper.java
│   │   │               ├── security
│   │   │               │   ├── CustomAuthenticationSuccessHandler.java
│   │   │               │   ├── LoginUsersDataInitializer.java
│   │   │               │   └── LoginUsersDetailsService.java
│   │   │               └── LoginRoleProjectApplication.java
│   │   └── resources
│   │       ├── com
│   │       │   └── example
│   │       │       └── loginroleproject
│   │       │           └── repository
│   │       │               ├── LoginUsersMapper.xml
│   │       │               └── UserRoleMapper.xml
│   │       ├── static
│   │       │   └── css
│   │       │       └── styles.css
│   │       ├── templates
│   │       │   ├── admin
│   │       │   │   └── home.html
│   │       │   ├── member
│   │       │   │   └── home.html
│   │       │   └── login.html
│   │       ├── application.yml
│   │       └── schema.sql
└── pom.xml</code></pre>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc3">SQLの作成</span></h2>


<p>MyBatisのmapper.xmlを使ってSQLを使用します。</p>



<p>LoginUsersMapper.xmlを作成します。</p>



<pre class="wp-block-code xml"><code>&lt;?xml version="1.0" encoding="UTF-8" ?&gt;
&lt;!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"&gt;
&lt;mapper namespace="com.example.loginroleproject.repository.LoginUsersMapper"&gt;
    &lt;select id="findUserByUsername" parameterType="string" resultType="LoginUsers"&gt;
        SELECT username, password
        FROM login_users
        WHERE username = #{username}
    &lt;/select&gt;
    &lt;insert id="insertUser" parameterType="LoginUsers"&gt;
        INSERT INTO login_users (username, password, enabled)
        VALUES (#{username}, #{password}, true);
    &lt;/insert&gt;
&lt;/mapper&gt; </code></pre>



<p>namespace=&#8221;com.example.loginroleproject.repository.LoginUsersMapper&#8221;と指定することで後述のLoginUsersMapper.javaに用意されたメソッドとLoginUsersMapper.xmlに用意された各SQLのid名がリンクします。</p>



<p>リンクすることでLoginUsersMapper.javaからLoginUsersMapper.xmlのSQLが実行できるようになります。</p>



<p>resultType=&#8221;LoginUsers&#8221;はSELECTの結果をcom.example.loginroleproject.entity.LoginUsersクラスに設定するために指定してます。</p>



<p>parameterType=&#8221;LoginUsers&#8221;と指定することでLoginUsersクラスのusernameやpassword変数に格納した値がINSERT文のVALUES句のusernameやpasswordに設定されるようにしてます。</p>



<p>複数の値をSQLに渡す必要がある時に便利です。</p>



<p>対してparameterType=&#8221;string&#8221;は文字列型の値をusernameに設定できるようにしてます。</p>



<p>findUserByUsernameはログイン時などに入力したユーザー名が存在するかをチェックするためのSQLです。</p>



<p>insertUserはアプリ起動時にログインアカウントを生成するためのSQLです。<br><br><br>続いてUserRoleMapper.xmlを作成します。</p>



<pre class="wp-block-code xml"><code>&lt;?xml version="1.0" encoding="UTF-8" ?&gt;
&lt;!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"&gt;
&lt;mapper namespace="com.example.loginroleproject.repository.UserRoleMapper"&gt;
    &lt;select id="findRolesByUsername" parameterType="string" resultType="UserRole"&gt;
        SELECT username, role
        FROM user_roles
        WHERE username = #{username}
    &lt;/select&gt;
    &lt;insert id="insertUserRole" parameterType="UserRole"&gt;
        INSERT INTO user_roles (username, role)
        VALUES (#{username}, #{role});
    &lt;/insert&gt;
&lt;/mapper&gt; </code></pre>



<p>namespace=&#8221;com.example.loginroleproject.repository.UserRoleMapper&#8221;と指定することで後述のUserRoleMapper.javaに用意されたメソッドとUserRoleMapper.xmlに用意された各SQLのid名がリンクします。</p>



<p>findRolesByUsernameはログインしたユーザーのロールを取得するためのSQLです。</p>



<p>insertUserRoleはアプリ起動時にログインアカウントのロールを設定するためのSQLです。</p>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc4">Entityクラスの作成</span></h2>


<p>後述のMapperインターフェースで使用するEntityクラスを作成します。</p>



<p>LoginUsersクラスを作成します。</p>



<pre class="wp-block-code java"><code>package com.example.loginroleproject.entity;

import lombok.Getter;
import lombok.Setter;

/**
 * ログインユーザーを表すエンティティクラス
 */
@Getter
@Setter
public class LoginUsers {
    /**
     * ユーザー名（主キー）
     */
    private String username;

    /**
     * パスワード（ハッシュ化済み）
     */
    private String password;
}</code></pre>



<p>UserRoleクラスを作成します。</p>



<pre class="wp-block-code java"><code>package com.example.loginroleproject.entity;

import lombok.Getter;
import lombok.Setter;

/**
 * ユーザーロールを表すエンティティクラス
 */
@Getter
@Setter
public class UserRole {
    /**
     * ユーザー名（外部キー）
     */
    private String username;

    /**
     * ロール名
     */
    private String role;
}</code></pre>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc5">Mapperインターフェースの作成</span></h2>


<p>各Mapper.xmlを使うMapperインターフェースを作成します。</p>



<p>LoginUsersMapperクラスを作成します。</p>



<pre class="wp-block-code java"><code>package com.example.loginroleproject.repository;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import com.example.loginroleproject.entity.LoginUsers;

/**
 * ログインユーザーのデータアクセスを担当するインターフェース
 */
@Mapper
public interface LoginUsersMapper {
    /**
     * ユーザー名に基づいてユーザー情報を取得します
     *
     * @param username ユーザー名
     * @return ユーザー情報。存在しない場合はnull
     */
    LoginUsers findUserByUsername(@Param("username") String username);

    /**
     * 新しいユーザーを登録します
     *
     * @param loginUsers 登録するユーザー情報
     */
    void insertUser(LoginUsers loginUsers);
}</code></pre>



<p>MapperアノテーションによってMyBatisがLoginUsersMapperをMapperインターフェースとして自動的に検出します。</p>



<p>こうすることでLoginUsersMapper.javaを通じてLoginUsersMapper.xmlのSQLを使うことができます。<br><br><br>続いてUserRoleMapperクラスを作成し、UserRoleMapper.javaを通じてUserRoleMapper.xmlのSQLを使用します。</p>



<pre class="wp-block-code java"><code>package com.example.loginroleproject.repository;

import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import com.example.loginroleproject.entity.UserRole;

/**
 * ユーザーロールのデータアクセスを担当するインターフェース
 */
@Mapper
public interface UserRoleMapper {
    /**
     * 指定されたユーザー名に紐づくロール一覧を取得します
     *
     * @param username ユーザー名
     * @return ロール一覧
     */
    List&lt;UserRole&gt; findRolesByUsername(@Param("username") String username);

    /**
     * ユーザーにロールを割り当てます
     *
     * @param userRole ユーザーロール情報
     */
    void insertUserRole(UserRole userRole);
}</code></pre>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc6">ログインアカウントを生成するクラスの作成</span></h2>


<p>今回はdata.sqlを使ってログインアカウントを生成しません。</p>



<p>H2のDBにデータを生成するのはdata.sqlを使うのが一般的だと思います。</p>



<p>しかし、data.sqlを使ってデータを生成しても、BCryptPasswordEncoderを使っているためにハッシュが一致せずに正しいパスワードを入力してもエラーとなってしまいます。</p>



<p>そのためアプリ起動時にデータをインサートする処理を実行するためにLoginUsersDataInitializerクラスを作成します。</p>



<pre class="wp-block-code java"><code>package com.example.loginroleproject.security;

import java.util.List;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.example.loginroleproject.config.SecurityProperties;
import com.example.loginroleproject.config.SecurityProperties.UsersConfig;
import com.example.loginroleproject.entity.LoginUsers;
import com.example.loginroleproject.entity.UserRole;
import com.example.loginroleproject.repository.LoginUsersMapper;
import com.example.loginroleproject.repository.UserRoleMapper;
import lombok.RequiredArgsConstructor;

/**
 * アプリケーション起動時にユーザーデータを初期化する設定クラス。 application.ymlに定義されたユーザー情報をデータベースに登録します。
 */
@RequiredArgsConstructor
@Component
@Configuration
public class LoginUsersDataInitializer {

    private final LoginUsersMapper loginUsersMapper;
    private final UserRoleMapper userRoleMapper;
    private final PasswordEncoder passwordEncoder;
    private final SecurityProperties securityProperties;

    /**
     * アプリケーション起動時に実行されるBean。 設定されたユーザー情報をデータベースに登録します。
     *
     * @return ApplicationRunner ユーザー初期化を実行するBean
     */
    @Bean
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public ApplicationRunner initializeUsers() {
        return args -&gt; {
            List&lt;UsersConfig&gt; users = securityProperties.getUsers();

            if (users == null || users.isEmpty()) {
                return;
            }

            users.forEach(this::initializeUser);
        };
    }

    /**
     * 個別のユーザーを初期化します。
     *
     * @param userConfig ユーザー設定情報
     */
    private void initializeUser(UsersConfig userConfig) {
        String username = userConfig.getUsername();

        if (isUserExists(username)) {
            return;
        }

        createUserWithRoles(username, userConfig.getPassword(), userConfig.getRole());
    }

    /**
     * ユーザーが既に存在するか確認します。
     *
     * @param username 確認するユーザー名
     * @return ユーザーが存在する場合はtrue
     */
    private boolean isUserExists(String username) {
        return loginUsersMapper.findUserByUsername(username) != null;
    }

    /**
     * ユーザーとロールを作成します。
     *
     * @param username ユーザー名
     * @param password パスワード
     * @param role 割り当てるロール
     */
    private void createUserWithRoles(String username, String password, String role) {
        createUser(username, password);
        assignRoleToUser(username, role);
    }

    /**
     * ユーザーを作成します。
     *
     * @param username ユーザー名
     * @param password パスワード
     */
    private void createUser(String username, String password) {
        LoginUsers loginUsers = new LoginUsers();
        loginUsers.setUsername(username);
        loginUsers.setPassword(passwordEncoder.encode(password));
        loginUsersMapper.insertUser(loginUsers);
    }

    /**
     * ユーザーにロールを割り当てます。
     *
     * @param username ユーザー名
     * @param role 割り当てるロール
     */
    private void assignRoleToUser(String username, String role) {
        UserRole userRole = new UserRole();
        userRole.setUsername(username);
        userRole.setRole(role);
        userRoleMapper.insertUserRole(userRole);
    }
}</code></pre>



<p>initializeUsersメソッドではBeanアノテーションを付与することでBeanとしてApplicationRunnerを登録するようにしてます。</p>



<p>また以下の処理をApplicationRunnerとして返却することでApplicationRunnerクラスのrunメソッドを実行した時に以下の処理が実行されるようにしてます。</p>



<pre class="wp-block-code java"><code>        return args -&gt; {
            List&lt;UsersConfig&gt; users = securityProperties.getUsers();

            if (users == null || users.isEmpty()) {
                return;
            }

            users.forEach(this::initializeUser);
        };</code></pre>



<p>Springはアプリ起動時にApplicationRunner.runを実行します。</p>



<p>そのため、アプリケーション起動時にBeanとして登録された上記のログインアカウントを生成するインサート処理が実行されるようになります。<br><br>以下はDBのトランザクション管理をするためのアノテーションです。</p>



<pre class="wp-block-code java"><code> @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)</code></pre>



<p>propagation = Propagation.REQUIREDはすでにトランザクションが存在する場合はそのトランザクションを使い、そうでない場合は新しくトランザクションを作るようにする設定となります。</p>



<p>このため、トランザクションを無駄に作成することがないため、パフォーマンスが向上します。</p>



<p>また複数のトランザクションが作成されることにより部分的にコミットされてしまうといったことがありません。</p>



<p>rollbackFor = Exception.classはExceptionクラスとExceptionクラスのサブクラス（つまり何らかの例外）が発生したらロールバックを行うという設定になります。<br><br><br>LoginUsersDataInitializerの全体の流れは以下の通りとなります。</p>



<pre class="wp-block-code nohighlight"><code>+-------------------------------------------+
|          アプリケーション起動             |
+-------------------+-----------------------+
                    |
                    ▼
+-------------------------------------------+
|       ApplicationRunner#run 実行          |
|     (initializeUsers()メソッド内)         |
+-------------------+-----------------------+
                    |
                    ▼
+-------------------------------------------+
|    SecurityPropertiesからユーザー設定取得  |
|    List&lt;UsersConfig&gt; users = ...         |
+-------------------+-----------------------+
                    |
                    ▼
      +----------------------------+
      |   ユーザーリストが空？     |---はい---&gt; 終了
      +------------+--------------+
                   |
                   | いいえ
                   ▼
      +----------------------------+
      |  ユーザーごとに処理実行    |
      |  users.forEach()          |
      +------------+--------------+
                   |
                   ▼
+-------------------------------------------+
|      initializeUser(UsersConfig)          |
+-------------------+-----------------------+
                    |
                    ▼
      +----------------------------+
      |  ユーザーが既に存在する？  |---はい---&gt; 処理スキップ
      |  isUserExists(username)   |           (次のユーザーへ)
      +------------+--------------+
                   |
                   | いいえ
                   ▼
+-------------------------------------------+
|   createUserWithRoles(username,           |
|                     password, role)       |
+-------------------+-----------------------+
                    |
                    ▼
+-------------------------------------------+
|   createUser(username, password)          |
|    - LoginUsers エンティティ作成          |
|    - パスワードをハッシュ化               |
|    - loginUsersMapper.insertUser()        |
+-------------------+-----------------------+
                    |
                    ▼
+-------------------------------------------+
|   assignRoleToUser(username, role)        |
|    - UserRole エンティティ作成            |
|    - userRoleMapper.insertUserRole()      |
+-------------------+-----------------------+
                    |
                    ▼
      +----------------------------+
      |   次のユーザーに進む      |
      |   (もしあれば)           |
      +----------------------------+</code></pre>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc7">認証用のサービスクラス作成</span></h2>


<p>MyBatisを使う場合、UserDetailsServiceクラスを実装して独自の認証用サービスクラスを作成する必要があります。</p>



<p>そのため、LoginUsersDetailsServiceクラスを作成します。</p>



<pre class="wp-block-code java"><code>package com.example.loginroleproject.security;

import java.util.List;
import java.util.stream.Collectors;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
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 org.springframework.transaction.annotation.Transactional;
import com.example.loginroleproject.entity.LoginUsers;
import com.example.loginroleproject.entity.UserRole;
import com.example.loginroleproject.repository.LoginUsersMapper;
import com.example.loginroleproject.repository.UserRoleMapper;
import lombok.RequiredArgsConstructor;

/**
 * Spring Securityの認証に必要なユーザー情報を提供するサービスクラスです。 データベースに格納されたユーザー情報を取得し、Spring Securityで使用可能な
 * UserDetailsオブジェクトに変換する役割を担います。 トランザクション管理を行い、データの整合性を保ちます。
 */
@RequiredArgsConstructor
@Service
@Transactional(readOnly = true)
public class LoginUsersDetailsService implements UserDetailsService {

    private final LoginUsersMapper loginUsersMapper;
    private final UserRoleMapper userRoleMapper;

    private static final String ROLE_PREFIX = "ROLE_";

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        LoginUsers loginUsers = loginUsersMapper.findUserByUsername(username);
        if (loginUsers == null) {
            throw new UsernameNotFoundException("User not found: " + username);
        }

        List&lt;UserRole&gt; userRoles = userRoleMapper.findRolesByUsername(username);
        List&lt;SimpleGrantedAuthority&gt; authorities = userRoles.stream()
                .map(role -&gt; new SimpleGrantedAuthority(ROLE_PREFIX + role.getRole()))
                .collect(Collectors.toList());

        return User.builder().username(loginUsers.getUsername()).password(loginUsers.getPassword())
                .authorities(authorities).build();
    }
}</code></pre>



<p>以下の処理によってログイン画面で入力されたユーザー名がDBに存在するかチェックしてます。</p>



<pre class="wp-block-code java"><code>        LoginUsers loginUsers = loginUsersMapper.findUserByUsername(username);
        if (loginUsers == null) {
            throw new UsernameNotFoundException("User not found: " + username);
        }</code></pre>



<p>以下の処理によってログインするユーザーのロールを取得し、SpringSecurityがロールを管理するためのクラスであるSimpleGrantedAuthorityの変数に設定をします。</p>



<p>SpringSecurityでロールを管理するためにはプレフィックスに&#8221;ROLE_&#8221;という文字列を付与する必要があるのでSimpleGrantedAuthorityの変数に設定する際に付与してます。</p>



<pre class="wp-block-code java"><code>        List&lt;UserRole&gt; userRoles = userRoleMapper.findRolesByUsername(username);
        List&lt;SimpleGrantedAuthority&gt; authorities = userRoles.stream()
                .map(role -&gt; new SimpleGrantedAuthority(ROLE_PREFIX + role.getRole()))
                .collect(Collectors.toList());</code></pre>



<p>ログイン画面で入力されたユーザー名、パスワードと先ほど取得したロールを使ってUserDetailsインスタンスを生成して返却します。</p>



<pre class="wp-block-code java"><code>        return User.builder().username(loginUsers.getUsername()).password(loginUsers.getPassword())
                .authorities(authorities).build();</code></pre>



<p>loadUserByUsernameメソッドでUserDetailsを返却することで、生成されたUserDetailsインスタンスを使ってSpringが提供するAuthenticationManagerクラスがDBに登録されているパスワードと一致してるか認証を行います。</p>



<p>また、返却したUserDetailsはSpringSecurityによってセッション情報に登録されます。</p>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc8">ログイン画面用のコントローラー作成</span></h2>


<p>ログイン画面用のコントローラーを作成します。</p>



<p>ログイン画面を表示するloginメソッドを用意してます。</p>



<pre class="wp-block-code java"><code>package com.example.loginroleproject.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import lombok.RequiredArgsConstructor;

/**
 * ログイン関連の画面遷移を制御するコントローラー
 */
@Controller
public class LoginController {

    /**
     * ログイン画面を表示します
     *
     * @return ログイン画面のビュー名
     */
    @GetMapping("/login")
    public String login() {
        return "login";
    }
}</code></pre>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc9">ログイン後画面のコントローラー作成</span></h2>


<p>ログイン後に表示する画面用のコントローラーを作成します。</p>



<p>ロールがADMIN権限のユーザーのみがアクセスできるAdminControllerを作成します。</p>



<p>前編のSecurityConfigによってADMIN権限のみ「/admin」というURLにアクセスできるようにしているため、AdminControllerはADMIN権限専用のコントローラーとなってます。</p>



<p>AdminControllerではADMIN権限のみがアクセスできる「/admin/home.html」を表示するメソッドを用意してます。</p>



<pre class="wp-block-code java"><code>package com.example.loginroleproject.controller;

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

/**
 * 管理者専用機能を提供するコントローラークラス /admin/以下のパスにマッピングされたエンドポイントを管理します
 */
@Controller
@RequestMapping("/admin")
public class AdminController {

    /**
     * 管理者用ホーム画面を表示します
     *
     * @return 管理者ホーム画面のビュー名
     */
    @GetMapping("/home")
    public String home() {
        return "admin/home";
    }
}</code></pre>



<p>続いてロールがMEMBER権限のユーザーのみがアクセスできるMemberControllerを作成します。</p>



<p>前編のSecurityConfigによってADMIN権限のみ「/member」というURLにアクセスできるようにしているため、MemberControllerはADMIN権限専用のコントローラーとなってます。</p>



<p>MemberControllerではMEMBER権限のみがアクセスできる「/member/home.html」を表示するメソッドを用意してます。</p>



<pre class="wp-block-code java"><code>package com.example.loginroleproject.controller;

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

/**
 * 一般会員専用機能を提供するコントローラークラス /member/以下のパスにマッピングされたエンドポイントを管理します
 */
@Controller
@RequestMapping("/member")
public class MemberController {

    /**
     * 会員用ホーム画面を表示します
     *
     * @return 会員ホーム画面のビュー名
     */
    @GetMapping("/home")
    public String home() {
        return "member/home";
    }
}</code></pre>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc10">ログイン画面とログイン後の画面作成</span></h2>


<p>ログイン画面とログイン後の画面を作成します。</p>



<p>まずはログイン画面であるlogin.htmlです。</p>



<pre class="wp-block-code html"><code>&lt;!DOCTYPE html&gt;
&lt;html xmlns:th="http://www.thymeleaf.org"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;title&gt;ログイン - MyApp&lt;/title&gt;
    &lt;link rel="stylesheet" th:href="@{/css/style.css}"&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;div class="login-container"&gt;
    &lt;h2&gt;ログイン&lt;/h2&gt;
    &lt;div class="error-message" th:if="${param.error}"&gt;
        ユーザー名またはパスワードが間違っています。
    &lt;/div&gt;
    &lt;form method="post" th:action="@{/login}"&gt;
        &lt;div class="input-group"&gt;
            &lt;label for="username"&gt;ユーザー名&lt;/label&gt;
            &lt;input id="username" name="username" required type="text"&gt;
        &lt;/div&gt;
        &lt;div class="input-group"&gt;
            &lt;label for="password"&gt;パスワード&lt;/label&gt;
            &lt;input id="password" name="password" required type="password"&gt;
        &lt;/div&gt;
        &lt;button class="login-button" type="submit"&gt;ログイン&lt;/button&gt;
    &lt;/form&gt;
&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>



<p>ログイン画面で使用するCSSとしてstyle.cssを作成します。</p>



<pre class="wp-block-code css"><code>body {
    font-family: Arial, sans-serif;
    background-color: #f4f4f4;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
}

.login-container {
    background-color: #fff;
    padding: 20px;
    border-radius: 10px;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
    width: 300px;
    text-align: center;
}

.login-container h2 {
    margin-bottom: 20px;
}

.input-group {
    margin-bottom: 15px;
    text-align: left;
}

.input-group label {
    display: block;
    margin-bottom: 5px;
}

.input-group input {
    width: 95%;
    padding: 8px;
    border: 1px solid #ccc;
    border-radius: 5px;
}

.login-button {
    background-color: #007bff;
    color: #fff;
    border: none;
    padding: 10px;
    width: 100%;
    border-radius: 5px;
    cursor: pointer;
}

.login-button:hover {
    background-color: #0056b3;
}

.error-message {
    color: red;
    margin-bottom: 10px;
}</code></pre>



<p>ADMIN権限のみがアクセスできる「/admin/home.html」を作成します。</p>



<pre class="wp-block-code html"><code>&lt;html lang="ja"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;title&gt;管理者ホーム&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h1&gt;管理者ページ&lt;/h1&gt;
&lt;p&gt;ようこそ、管理者ユーザーさん！&lt;/p&gt;
&lt;div&gt;
    &lt;a href="/logout"&gt;ログアウト&lt;/a&gt;
&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt; </code></pre>



<p>MEMBER権限のみがアクセスできる「/member/home.html」を作成します。</p>



<pre class="wp-block-code html"><code>&lt;html lang="ja"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;title&gt;会員ホーム&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h1&gt;会員ページ&lt;/h1&gt;
&lt;p&gt;ようこそ、会員ユーザーさん！&lt;/p&gt;
&lt;div&gt;
    &lt;a href="/logout"&gt;ログアウト&lt;/a&gt;
&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt; </code></pre>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc11">アプリ起動用のクラス作成</span></h2>


<p>最後にアプリ起動用のクラスを作成します。</p>



<pre class="wp-block-code java"><code>package com.example.loginroleproject;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * ログイン認証・認可機能を持つSpring Bootアプリケーションのメインクラスです。 このクラスはアプリケーションのエントリーポイントとして機能し、Spring
 * Boot環境の初期化を行います。
 */
@SpringBootApplication
public class LoginRoleProjectApplication {

    /**
     * アプリケーションのエントリーポイントとなるメインメソッドです。 Spring Boot環境を初期化し、アプリケーションを起動します。
     *
     * @param args コマンドライン引数
     */
    public static void main(String&#91;] args) {
        SpringApplication.run(LoginRoleProjectApplication.class, args);
    }
}</code></pre>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc12">ログイン画面へアクセス</span></h2>


<p>ブラウザでhttp://localhost:8080/loginにアクセスすると以下のようなログイン画面が表示されます。</p>



<figure class="wp-block-image size-full"><img fetchpriority="high" decoding="async" width="598" height="491" src="https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/image.png" alt="" class="wp-image-1669" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/image.png 598w, https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/image-300x246.png 300w" sizes="(max-width: 598px) 100vw, 598px" /></figure>



<p>ユーザー名とパスワードはapplication.ymlの以下の値となります。</p>



<pre class="wp-block-code plaintext"><code>  users:
    - username: admin
      password: admin123
      role: ADMIN
    - username: user1
      password: user123
      role: MEMBER
    - username: user2
      password: user456
      role: MEMBER</code></pre>



<p>ADMIN権限でログインしたい場合はユーザー名にadmin、パスワードにadmin123と入力し、ログインボタンを押下すればログインが成功します。</p>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc13">h2-consoleへアクセス</span></h2>


<p>ブラウザでhttp://localhost:8080/h2-consoleへアクセスすると以下のような画面へアクセスできます。</p>



<figure class="wp-block-image size-full"><img decoding="async" width="454" height="364" src="https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/image-1.png" alt="" class="wp-image-1672" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/image-1.png 454w, https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/image-1-300x241.png 300w" sizes="(max-width: 454px) 100vw, 454px" /></figure>



<p>Connectボタンを押すと以下のような画面が表示できます。</p>



<figure class="wp-block-image size-full"><img decoding="async" width="823" height="369" src="https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/image-2.png" alt="" class="wp-image-1674" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/image-2.png 823w, https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/image-2-300x135.png 300w, https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/image-2-768x344.png 768w" sizes="(max-width: 823px) 100vw, 823px" /></figure>



<p>この画面では上記のようにSQLを実行でき、H2上の各テーブルのデータを見ることができるので便利です。<br><br><br></p>



<p>なお、記載内容が多くなったため、記事を2つに分けてます。</p>



<p>前編は以下をご参照ください。</p>



<figure class="wp-block-embed is-type-wp-embed"><div class="wp-block-embed__wrapper">

<a href="https://gogorelaxlifeblog.com/springsecurity6role-setup" title="SpringSecurity6+MyBatisでroleを使った認可（設定編）" class="blogcard-wrap internal-blogcard-wrap a-wrap cf"><div class="blogcard internal-blogcard ib-left cf"><div class="blogcard-label internal-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail internal-blogcard-thumbnail"><img decoding="async" width="160" height="90" src="https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/16-160x90.png" class="blogcard-thumb-image internal-blogcard-thumb-image wp-post-image" alt="" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/16-160x90.png 160w, https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/16-120x68.png 120w, https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/16-320x180.png 320w" sizes="(max-width: 160px) 100vw, 160px" /></figure><div class="blogcard-content internal-blogcard-content"><div class="blogcard-title internal-blogcard-title">SpringSecurity6+MyBatisでroleを使った認可（設定編）</div><div class="blogcard-snippet internal-blogcard-snippet">SpringSecurity6系は5.3までと比べるとSecurityConfigの書き方が変わってるので勉強を兼ねて独自のログイン画面を用意しroleを使った認可機能をjavaで作成してみました。またMyBatisとH2 Databaseを使うことでDB構築をせずとも、手軽にDBと連携できるようにしてみました。</div></div><div class="blogcard-footer internal-blogcard-footer cf"><div class="blogcard-site internal-blogcard-site"><div class="blogcard-favicon internal-blogcard-favicon"><img decoding="async" src="https://www.google.com/s2/favicons?domain=https://gogorelaxlifeblog.com" alt="" class="blogcard-favicon-image internal-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain internal-blogcard-domain">gogorelaxlifeblog.com</div></div><div class="blogcard-date internal-blogcard-date"><div class="blogcard-post-date internal-blogcard-post-date">2025.03.22</div></div></div></div></a>
</div></figure>



<p></p>
]]></content:encoded>
					
					<wfw:commentRss>https://gogorelaxlifeblog.com/springsecurity6role-implementation/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>SpringSecurity6+MyBatisでroleを使った認可（設定編）</title>
		<link>https://gogorelaxlifeblog.com/springsecurity6role-setup/</link>
					<comments>https://gogorelaxlifeblog.com/springsecurity6role-setup/#respond</comments>
		
		<dc:creator><![CDATA[ごゆるり一族]]></dc:creator>
		<pubDate>Sat, 22 Mar 2025 06:30:39 +0000</pubDate>
				<category><![CDATA[技術]]></category>
		<category><![CDATA[spring]]></category>
		<guid isPermaLink="false">https://gogorelaxlifeblog.com/?p=1692</guid>

					<description><![CDATA[SpringSecurity6系は5.3までと比べるとSecurityConfigの書き方が変わってるので勉強を兼ねて独自のログイン画面を用意しroleを使った認可機能をjavaで作成してみました。 またMyBatisと [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>SpringSecurity6系は5.3までと比べるとSecurityConfigの書き方が変わってるので勉強を兼ねて独自のログイン画面を用意しroleを使った認可機能をjavaで作成してみました。</p>



<p>またMyBatisとH2 Databaseを使うことでDB構築をせずとも、手軽にDBと連携できるようにしてみました。</p>



<p>なお、記載内容が多くなったため、記事を2つに分けてます。</p>



<p>後編は以下をご参照ください。</p>



<figure class="wp-block-embed is-type-wp-embed"><div class="wp-block-embed__wrapper">

<a href="https://gogorelaxlifeblog.com/springsecurity6role-implementation" title="SpringSecurity6+MyBatisでroleを使った認可（実装編）" class="blogcard-wrap internal-blogcard-wrap a-wrap cf"><div class="blogcard internal-blogcard ib-left cf"><div class="blogcard-label internal-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail internal-blogcard-thumbnail"><img decoding="async" width="160" height="90" src="https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/17-160x90.png" class="blogcard-thumb-image internal-blogcard-thumb-image wp-post-image" alt="" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/17-160x90.png 160w, https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/17-120x68.png 120w, https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/17-320x180.png 320w" sizes="(max-width: 160px) 100vw, 160px" /></figure><div class="blogcard-content internal-blogcard-content"><div class="blogcard-title internal-blogcard-title">SpringSecurity6+MyBatisでroleを使った認可（実装編）</div><div class="blogcard-snippet internal-blogcard-snippet">SpringSecurity6系は5.3までと比べるとSecurityConfigの書き方が変わってるので勉強を兼ねて独自のログイン画面を用意しroleを使った認可機能をjavaで作成してみました。またMyBatisとH2 Databaseを使うことでDB構築をせずとも、手軽にDBと連携できるようにしてみました。</div></div><div class="blogcard-footer internal-blogcard-footer cf"><div class="blogcard-site internal-blogcard-site"><div class="blogcard-favicon internal-blogcard-favicon"><img decoding="async" src="https://www.google.com/s2/favicons?domain=https://gogorelaxlifeblog.com" alt="" class="blogcard-favicon-image internal-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain internal-blogcard-domain">gogorelaxlifeblog.com</div></div><div class="blogcard-date internal-blogcard-date"><div class="blogcard-post-date internal-blogcard-post-date">2025.03.22</div></div></div></div></a>
</div></figure>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc1">開発環境</span></h2>
<p>私が構築した時の開発環境は以下の通りとなります。</p>


<figure class="wp-block-flexible-table-block-table"><table class="has-fixed-layout"><thead><tr><th>環境</th><th>バージョン</th></tr></thead><tbody><tr><td>Java</td><td>17</td></tr><tr><td>SpringBoot</td><td>3.4.3</td></tr><tr><td>SpringSecurity</td><td>6.4.3</td></tr><tr><td>thymeleaf</td><td>3.1.3.RELEASE</td></tr><tr><td>MyBatis</td><td>3.0.4</td></tr><tr><td>H2 Database</td><td>2.3.232</td></tr><tr><td>lombok</td><td>1.18.36</td></tr></tbody></table></figure>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc2">ソース構成</span></h2>
<p>ソース構成は以下の通りです。</p>


<pre class="wp-block-code nohighlight"><code>login-role-project
├── src
│   ├── main
│   │   ├── java
│   │   │   └── com
│   │   │       └── example
│   │   │           └── loginroleproject
│   │   │               ├── config
│   │   │               │   ├── SecurityConfig.java
│   │   │               │   └── SecurityProperties.java
│   │   │               ├── controller
│   │   │               │   ├── AdminController.java
│   │   │               │   ├── LoginController.java
│   │   │               │   └── MemberController.java
│   │   │               ├── entity
│   │   │               │   ├── LoginUsers.java
│   │   │               │   └── UserRole.java
│   │   │               ├── repository
│   │   │               │   ├── LoginUsersMapper.java
│   │   │               │   └── UserRoleMapper.java
│   │   │               ├── security
│   │   │               │   ├── CustomAuthenticationSuccessHandler.java
│   │   │               │   ├── LoginUsersDataInitializer.java
│   │   │               │   └── LoginUsersDetailsService.java
│   │   │               └── LoginRoleProjectApplication.java
│   │   └── resources
│   │       ├── com
│   │       │   └── example
│   │       │       └── loginroleproject
│   │       │           └── repository
│   │       │               ├── LoginUsersMapper.xml
│   │       │               └── UserRoleMapper.xml
│   │       ├── static
│   │       │   └── css
│   │       │       └── styles.css
│   │       ├── templates
│   │       │   ├── admin
│   │       │   │   └── home.html
│   │       │   ├── member
│   │       │   │   └── home.html
│   │       │   └── login.html
│   │       ├── application.yml
│   │       └── schema.sql
└── pom.xml</code></pre>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc3">pom.xml</span></h2>
<p>必要なライブラリを導入するためにpom.xmlを以下のように記述します。</p>


<pre class="wp-block-code xml"><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;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"&gt;
    &lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;
    &lt;parent&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-parent&lt;/artifactId&gt;
        &lt;version&gt;3.4.3&lt;/version&gt;
        &lt;relativePath/&gt; &lt;!-- lookup parent from repository --&gt;
    &lt;/parent&gt;
    &lt;groupId&gt;com.example&lt;/groupId&gt;
    &lt;artifactId&gt;login-role-project&lt;/artifactId&gt;
    &lt;version&gt;0.0.1-SNAPSHOT&lt;/version&gt;
    &lt;name&gt;login-role-project&lt;/name&gt;
    &lt;description&gt;login-role-project&lt;/description&gt;
    &lt;url/&gt;
    &lt;licenses&gt;
        &lt;license/&gt;
    &lt;/licenses&gt;
    &lt;developers&gt;
        &lt;developer/&gt;
    &lt;/developers&gt;
    &lt;scm&gt;
        &lt;connection/&gt;
        &lt;developerConnection/&gt;
        &lt;tag/&gt;
        &lt;url/&gt;
    &lt;/scm&gt;
    &lt;properties&gt;
        &lt;java.version&gt;17&lt;/java.version&gt;
    &lt;/properties&gt;
    &lt;dependencies&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-starter-security&lt;/artifactId&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-starter-thymeleaf&lt;/artifactId&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-starter-validation&lt;/artifactId&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.mybatis.spring.boot&lt;/groupId&gt;
            &lt;artifactId&gt;mybatis-spring-boot-starter&lt;/artifactId&gt;
            &lt;version&gt;3.0.4&lt;/version&gt;
        &lt;/dependency&gt;

        &lt;dependency&gt;
            &lt;groupId&gt;com.h2database&lt;/groupId&gt;
            &lt;artifactId&gt;h2&lt;/artifactId&gt;
            &lt;scope&gt;runtime&lt;/scope&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.projectlombok&lt;/groupId&gt;
            &lt;artifactId&gt;lombok&lt;/artifactId&gt;
            &lt;optional&gt;true&lt;/optional&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt;
            &lt;scope&gt;test&lt;/scope&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.security&lt;/groupId&gt;
            &lt;artifactId&gt;spring-security-test&lt;/artifactId&gt;
            &lt;scope&gt;test&lt;/scope&gt;
        &lt;/dependency&gt;
    &lt;/dependencies&gt;

    &lt;build&gt;
        &lt;plugins&gt;
            &lt;plugin&gt;
                &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
                &lt;artifactId&gt;maven-compiler-plugin&lt;/artifactId&gt;
                &lt;configuration&gt;
                    &lt;annotationProcessorPaths&gt;
                        &lt;path&gt;
                            &lt;groupId&gt;org.projectlombok&lt;/groupId&gt;
                            &lt;artifactId&gt;lombok&lt;/artifactId&gt;
                        &lt;/path&gt;
                    &lt;/annotationProcessorPaths&gt;
                &lt;/configuration&gt;
            &lt;/plugin&gt;
            &lt;plugin&gt;
                &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
                &lt;artifactId&gt;spring-boot-maven-plugin&lt;/artifactId&gt;
                &lt;configuration&gt;
                    &lt;excludes&gt;
                        &lt;exclude&gt;
                            &lt;groupId&gt;org.projectlombok&lt;/groupId&gt;
                            &lt;artifactId&gt;lombok&lt;/artifactId&gt;
                        &lt;/exclude&gt;
                    &lt;/excludes&gt;
                &lt;/configuration&gt;
            &lt;/plugin&gt;
        &lt;/plugins&gt;
    &lt;/build&gt;

&lt;/project&gt;</code></pre>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc4">application.ymlの作成</span></h2>


<p>アプリの設定を行うapplication.ymlを以下のように記述します。</p>



<pre class="wp-block-code plaintext"><code>spring:
  datasource:
    url: jdbc:h2:mem:testdb
    driver-class-name: org.h2.Driver
    username: sa
    password:
  h2:
    console:
      enabled: true
      path: /h2-console

logging:
  level:
    org.springframework.security: DEBUG

security:
  login-url: /login
  logout-url: /logout
  permit-urls:
    - /login
    - /css/**
    - /js/**
    - /images/**
  users:
    - username: admin
      password: admin123
      role: ADMIN
    - username: user1
      password: user123
      role: MEMBER
    - username: user2
      password: user456
      role: MEMBER
  roles:
    admin: ADMIN
    user: MEMBER
  paths:
    admin: /admin/**
    user: /member/**
  h2-console-url: /h2-console/**

mybatis:
  type-aliases-package: com.example.loginroleproject.entity
  configuration:
    map-underscore-to-camel-case: true</code></pre>



<p>以下を指定することでh2をDBとして使用することしてます。h2のDBにログインする時のpasswordは未入力で大丈夫です。</p>



<p>なお、h2は軽量でローカルでの開発には便利ですが、安定性やスケーラビリティに欠けるので商用利用はしないように留意ください。</p>



<pre class="wp-block-code plaintext"><code>spring:
  datasource:
    url: jdbc:h2:mem:testdb
    driver-class-name: org.h2.Driver
    username: sa
    password:</code></pre>



<p></p>



<p>以下はh2のDBの中身を見れるようにh2-consoleを有効化してます。</p>



<pre class="wp-block-code plaintext"><code>h2:
  console:
    enabled: true
    path: /h2-console</code></pre>



<p></p>



<p>以下はデバッグ用にSpringSecurityのログレベルをDEBUGに指定してます。</p>



<pre class="wp-block-code plaintext"><code>org.springframework.security: DEBUG</code></pre>



<p></p>



<p>securityプリフィックス以下にある値は後述で示すSecurityPropertiesクラスで使用してます。</p>



<p>例えばlogin-urlにはログイン用のURLを記載します。</p>



<p>permit-urlsには未認証でもアクセスできるURLを記載します。</p>



<p>CSSやJSや画像などの静的ファイルは未認証でもアクセスできないと使用できないため許可します。</p>



<pre class="wp-block-code plaintext"><code>  login-url: /login
  logout-url: /logout
  permit-urls:
    - /login
    - /css/**
    - /js/**
    - /images/**</code></pre>



<p></p>



<p>usersにはログインで使用するユーザー名とパスワードを記載します。</p>



<pre class="wp-block-code plaintext"><code>  users:
    - username: admin
      password: admin123
      role: ADMIN
    - username: user1
      password: user123
      role: MEMBER
    - username: user2
      password: user456
      role: MEMBER</code></pre>



<p>rolesにはどのようなロールがあるかを記載します。</p>



<pre class="wp-block-code plaintext"><code>  roles:
    admin: ADMIN
    user: MEMBER</code></pre>



<p></p>



<p>type-aliases-packageで指定したパッケージ配下のクラスはパッケージ名を省略してMyBatisのXMLで使用できます。</p>



<p>map-underscore-to-camel-case: trueを指定することでデータベースのカラム名をキャメルケースに自動変換できるようにします。</p>



<pre class="wp-block-code plaintext"><code>mybatis:<br>  type-aliases-package: com.example.loginproject.entity<br>  configuration:<br>    map-underscore-to-camel-case: true</code></pre>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc5">schema.sqlの作成</span></h2>


<p>アプリケーション起動時にDBのテーブルが作成されるようにschema.sqlを作成します。</p>



<pre class="wp-block-code sql"><code>CREATE TABLE IF NOT EXISTS LOGIN_USERS (
    USERNAME VARCHAR (50) PRIMARY KEY,
    PASSWORD VARCHAR (100) NOT NULL,
    ENABLED BOOLEAN NOT NULL
);

CREATE TABLE IF NOT EXISTS USER_ROLES (
    USERNAME VARCHAR (50) NOT NULL,
    ROLE VARCHAR (20) NOT NULL,
    PRIMARY KEY (USERNAME, ROLE),
    FOREIGN KEY (USERNAME) REFERENCES LOGIN_USERS (USERNAME)
);</code></pre>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc6">application.ymlを扱うクラスの作成</span></h2>
<p>application.ymlにlogin-urlといったログイン用のURLなどを定数として定義しました。</p>
<p>それらの定数を扱うためのクラスであるSecurityPropertiesクラスを作成します。</p>


<pre class="wp-block-code java"><code>package com.example.loginroleproject.config;

import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@Component
@Validated
@ConfigurationProperties(prefix = "security")
public class SecurityProperties {
    @NotBlank(message = "Login URL must be specified")
    private String loginUrl;

    @NotBlank(message = "Logout URL must be specified")
    private String logoutUrl;

    @NotEmpty(message = "Permit URLs must be specified")
    private List&lt;String&gt; permitUrls;

    @NotEmpty(message = "Users must be configured")
    private List&lt;UsersConfig&gt; users;

    private RolesConfig roles;

    private PathsConfig paths;

    @NotBlank(message = "H2 console URL must be specified")
    private String h2ConsoleUrl;

    @Getter
    @Setter
    public static class UsersConfig {
        @NotBlank(message = "Username must be specified")
        private String username;

        @NotBlank(message = "Password must be specified")
        private String password;

        @NotBlank(message = "Role must be specified")
        private String role;
    }

    @Getter
    @Setter
    public static class RolesConfig {
        @NotBlank(message = "Admin role must be specified")
        private String admin;

        @NotBlank(message = "User role must be specified")
        private String user;
    }

    @Getter
    @Setter
    public static class PathsConfig {
        @NotBlank(message = "Admin path must be specified")
        private String admin;

        @NotBlank(message = "User path must be specified")
        private String user;
    }
}
</code></pre>



<p>lombokのgetterとsetterアノテーションによってgetterとsetterメソッドを自作しないで自動的に生成されるようにしてます。<br><br><br>Validated、NotBlankとNotEmptyアノテーションによってymlファイルから値を読み込んだ時に値がない場合にエラーとなるようにしてます。</p>



<p>アプリ起動時にバリデーションが行われることでアプリ起動に必要な値がすべて揃ってるかをチェックしてます。</p>



<p>なお、コレクションフレームワーク（List型）はNotBlankが使用できないため、NotEmptyでチェックしてます。<br><br><br>ConfigurationPropertiesアノテーションによってapplication.yml上のsecurityプレフィックス配下にあるものをすべてこのクラスの変数に格納するようにしてます。</p>



<p>permit-urlsプレフィックスが以下のようにハイフンを使って複数値を定義しているため、List&lt;String&gt;でpermitUrlsを宣言してます。</p>



<pre class="wp-block-code plaintext"><code>  permit-urls:
    - /login
    - /css/**
    - /js/**
    - /images/**</code></pre>



<p>usersプレフィックスが複数の項目を持つため、UsersConfigというインナークラスを作成しています。</p>



<p>rolesとpathsプレフィックスも複数の項目を持つため、RolesConfigとPathsConfigというインナークラスを作成してます。</p>



<pre class="wp-block-code plaintext"><code>  users:
    - username: admin
      password: admin123
      roles:
        - ADMIN
    - username: user1
      password: user123
      roles:
        - MEMBER
    - username: user2
      password: user456
      roles:
        - MEMBER</code></pre>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc7">SpringSecurityの設定</span></h2>


<p>SpringSecurityの設定を行うクラスを作成します。</p>



<pre class="wp-block-code java"><code>package com.example.loginroleproject.config;

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.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import com.example.loginroleproject.security.CustomAuthenticationSuccessHandler;
import lombok.RequiredArgsConstructor;

/**
 * Spring Securityの設定クラス 認証・認可の設定とセキュリティ関連の設定を行います
 */
@RequiredArgsConstructor
@Configuration
public class SecurityConfig {
    private static final String X_FRAME_OPTIONS_HEADER = "X-Frame-Options";
    private static final String X_FRAME_OPTIONS_SAMEORIGIN = "SAMEORIGIN";
    private static final String X_FRAME_OPTIONS_DENY = "DENY";

    private final SecurityProperties securityProperties;
    private final CustomAuthenticationSuccessHandler authenticationSuccessHandler;

    /**
     * Spring Securityの設定を構成します
     *
     * @param http HttpSecurityオブジェクト
     * @return 設定済みのSecurityFilterChain
     * @throws Exception 設定中にエラーが発生した場合
     */
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -&gt; auth
                .requestMatchers(securityProperties.getPermitUrls().toArray(String&#91;]::new)).permitAll()
                .requestMatchers(securityProperties.getH2ConsoleUrl()).permitAll()
                .requestMatchers(securityProperties.getPaths().getAdmin()).hasRole(securityProperties.getRoles().getAdmin())
                .requestMatchers(securityProperties.getPaths().getUser()).hasRole(securityProperties.getRoles().getUser())
                .anyRequest().authenticated()
            )
            .formLogin(login -&gt; login
                .loginPage(securityProperties.getLoginUrl())
                .successHandler(authenticationSuccessHandler)
                .permitAll()
            )
            .logout(logout -&gt; logout
                .logoutRequestMatcher(new AntPathRequestMatcher(securityProperties.getLogoutUrl()))
                .logoutSuccessUrl(securityProperties.getLoginUrl())
                .permitAll()
            )
            .csrf(csrf -&gt; csrf
                .ignoringRequestMatchers(securityProperties.getH2ConsoleUrl())
            )
            .headers(headers -&gt; headers
                .addHeaderWriter((request, response) -&gt; {
                    if (new AntPathRequestMatcher(securityProperties.getH2ConsoleUrl()).matches(request)) {
                        response.setHeader(X_FRAME_OPTIONS_HEADER, X_FRAME_OPTIONS_SAMEORIGIN);
                    } else {
                        response.setHeader(X_FRAME_OPTIONS_HEADER, X_FRAME_OPTIONS_DENY);
                    }
                })
            );
        return http.build();
    }

    /**
     * パスワードエンコーダーを提供します
     *
     * @return BCryptPasswordEncoderのインスタンス
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}</code></pre>



<p>RequiredArgsConstructorアノテーションはfinal修飾子が付与されたメンバ変数を引数に持つコンストラクタを自動生成します。</p>



<p>これによって以下のようなコンストラクタを作らずともSecurityPropertiesクラスをインジェクションできるようにし、SecurityConfigクラスでSecurityPropertiesとCustomAuthenticationSuccessHandlerクラスを使用可能としてます。</p>



<pre class="wp-block-code java"><code>    public SecurityConfig(final SecurityProperties securityProperties, final CustomAuthenticationSuccessHandler authenticationSuccessHandler) {
        this.securityProperties = securityProperties;
        this.authenticationSuccessHandler = authenticationSuccessHandler;
    }</code></pre>



<p></p>



<p>以下によってapplication.ymlのpermit-urlsとh2-console-urlプレフィックスで定義したURLについてはログイン認証をせずにアクセスできるようにしてます。</p>



<pre class="wp-block-code java"><code>.requestMatchers(securityProperties.getPermitUrls().toArray(String&#91;]::new)).permitAll()
.requestMatchers(securityProperties.getH2ConsoleUrl()).permitAll()</code></pre>



<p></p>



<p>以下によって「/admin/**」というURLはAdminロールを持つユーザーのみアクセスできるようにしてます。</p>



<p>「/member/**」というURLはMemberロールを持つユーザーのみアクセスできるようにしてます。</p>



<p>この制御によってroleによる認可を実現しています。</p>



<pre class="wp-block-code"><code>.requestMatchers(securityProperties.getPaths().getAdmin()).hasRole(securityProperties.getRoles().getAdmin())
.requestMatchers(securityProperties.getPaths().getUser()).hasRole(securityProperties.getRoles().getUser())</code></pre>



<p></p>



<p>以下によってログイン画面はapplication.ymlのlogin-urlプレフィックスに定義したURLであることを指定してます。</p>



<pre class="wp-block-code java"><code>.loginPage(securityProperties.getLoginUrl())</code></pre>



<p></p>



<p>以下によってログイン成功時の処理はCustomAuthenticationSuccessHandlerクラスを実行するように指定してます。</p>



<p>CustomAuthenticationSuccessHandlerは後続で作成するクラスになります。</p>



<pre class="wp-block-code java"><code>.successHandler(authenticationSuccessHandler)</code></pre>



<p></p>



<p>logoutRequestMatcherによってログアウトの処理を実行するURLを指定してます。</p>



<p>logoutSuccessUrlによってログアウト処理が成功した後のリダイレクト先のURLを指定してます。</p>



<pre class="wp-block-code java"><code>.logoutRequestMatcher(new AntPathRequestMatcher(securityProperties.getLogoutUrl()))
.logoutSuccessUrl(securityProperties.getLoginUrl())</code></pre>



<p></p>



<p>以下によってh2-consoleのみcsrfトークンを無効化してます。</p>



<pre class="wp-block-code java"><code>.csrf(csrf -&gt; csrf
    .ignoringRequestMatchers(securityProperties.getH2ConsoleUrl())
)</code></pre>



<p></p>



<p>以下によってh2-consoleのみiframeを使用可能にしてます。</p>



<pre class="wp-block-code java"><code>.headers(headers -&gt; headers
    .addHeaderWriter((request, response) -&gt; {
        if (new AntPathRequestMatcher(securityProperties.getH2ConsoleUrl()).matches(request)) {
            response.setHeader(X_FRAME_OPTIONS_HEADER, X_FRAME_OPTIONS_SAMEORIGIN);
        } else {
            response.setHeader(X_FRAME_OPTIONS_HEADER, X_FRAME_OPTIONS_DENY);
        }
    })
);</code></pre>



<p></p>



<p>以下によってパスワードの暗号化にBCryptPasswordEncoderを使用するように指定してます。</p>



<pre class="wp-block-code"><code>@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}</code></pre>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc8">ログイン成功時のHandlerクラス作成</span></h2>


<p>ログイン成功時のHandlerクラスを作成します。</p>



<pre class="wp-block-code"><code>package com.example.loginroleproject.security;

import java.io.IOException;
import java.util.Collection;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import com.example.loginroleproject.config.SecurityProperties;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;

/**
 * ユーザーのロールに基づいて適切なURLにリダイレクトするカスタム認証成功ハンドラー
 */
@Component
@RequiredArgsConstructor
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    private static final String ROLE_PREFIX = "ROLE_";
    private final SecurityProperties securityProperties;

    /**
     * 認証成功時に呼び出され、ユーザーのロールに応じた適切なURLにリダイレクトします
     * 
     * @param request リクエスト
     * @param response レスポンス
     * @param authentication 認証情報
     * @throws IOException I/O例外が発生した場合
     * @throws ServletException サーブレット例外が発生した場合
     */
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
            Authentication authentication) throws IOException, ServletException {

        Collection&lt;? extends GrantedAuthority&gt; authorities = authentication.getAuthorities();

        // 管理者ロールを持つ場合は管理者ホーム画面へリダイレクト
        if (authorities.contains(new SimpleGrantedAuthority(
                ROLE_PREFIX + securityProperties.getRoles().getAdmin()))) {
            response.sendRedirect("/admin/home");
        } else {
            // それ以外の場合は会員ホーム画面へリダイレクト
            response.sendRedirect("/member/home");
        }
    }
}</code></pre>



<p>AuthenticationSuccessHandlerインターフェースを実装することでログイン成功時に実行される処理をカスタマイズしてます。</p>



<p>SpringSecurityが管理しているセッション情報からauthenticationを取得し、ログイン後に管理者ロールを持つユーザーとそうでないユーザーによって呼び出すコントローラーを分けるようにしてます。</p>



<p>セッション情報の登録は後編に記すLoginUsersDetailsServiceで行っています。<br><br><br></p>



<p>なお、記載内容が多くなったため、記事を2つに分けてます。</p>



<p>後編は以下をご参照ください。</p>



<figure class="wp-block-embed is-type-wp-embed"><div class="wp-block-embed__wrapper">

<a href="https://gogorelaxlifeblog.com/springsecurity6role-implementation" title="SpringSecurity6+MyBatisでroleを使った認可（実装編）" class="blogcard-wrap internal-blogcard-wrap a-wrap cf"><div class="blogcard internal-blogcard ib-left cf"><div class="blogcard-label internal-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail internal-blogcard-thumbnail"><img decoding="async" width="160" height="90" src="https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/17-160x90.png" class="blogcard-thumb-image internal-blogcard-thumb-image wp-post-image" alt="" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/17-160x90.png 160w, https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/17-120x68.png 120w, https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/17-320x180.png 320w" sizes="(max-width: 160px) 100vw, 160px" /></figure><div class="blogcard-content internal-blogcard-content"><div class="blogcard-title internal-blogcard-title">SpringSecurity6+MyBatisでroleを使った認可（実装編）</div><div class="blogcard-snippet internal-blogcard-snippet">SpringSecurity6系は5.3までと比べるとSecurityConfigの書き方が変わってるので勉強を兼ねて独自のログイン画面を用意しroleを使った認可機能をjavaで作成してみました。またMyBatisとH2 Databaseを使うことでDB構築をせずとも、手軽にDBと連携できるようにしてみました。</div></div><div class="blogcard-footer internal-blogcard-footer cf"><div class="blogcard-site internal-blogcard-site"><div class="blogcard-favicon internal-blogcard-favicon"><img decoding="async" src="https://www.google.com/s2/favicons?domain=https://gogorelaxlifeblog.com" alt="" class="blogcard-favicon-image internal-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain internal-blogcard-domain">gogorelaxlifeblog.com</div></div><div class="blogcard-date internal-blogcard-date"><div class="blogcard-post-date internal-blogcard-post-date">2025.03.22</div></div></div></div></a>
</div></figure>



<p></p>
]]></content:encoded>
					
					<wfw:commentRss>https://gogorelaxlifeblog.com/springsecurity6role-setup/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>SpringSecurity6+MyBatisで独自ログイン認証（実装編）</title>
		<link>https://gogorelaxlifeblog.com/springsecurity6-implementation/</link>
					<comments>https://gogorelaxlifeblog.com/springsecurity6-implementation/#respond</comments>
		
		<dc:creator><![CDATA[ごゆるり一族]]></dc:creator>
		<pubDate>Sun, 16 Mar 2025 14:18:42 +0000</pubDate>
				<category><![CDATA[技術]]></category>
		<category><![CDATA[spring]]></category>
		<guid isPermaLink="false">https://gogorelaxlifeblog.com/?p=1585</guid>

					<description><![CDATA[SpringSecurity6系は5.3までと比べるとSecurityConfigの書き方が変わってるので勉強を兼ねて独自のログイン画面を用意し認証機能をjavaで作成してみました。 またMyBatisとH2 Datab [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>SpringSecurity6系は5.3までと比べるとSecurityConfigの書き方が変わってるので勉強を兼ねて独自のログイン画面を用意し認証機能をjavaで作成してみました。</p>



<p>またMyBatisとH2 Databaseを使うことでDB構築をせずとも、手軽にDBと連携できるようにしてみました。</p>



<p>なお、記載内容が多くなったため、記事を2つに分けてます。</p>



<p>前編は以下をご参照ください。</p>



<figure class="wp-block-embed is-type-wp-embed"><div class="wp-block-embed__wrapper">

<a href="https://gogorelaxlifeblog.com/springsecurity6-setup" title="SpringSecurity6+MyBatisで独自ログイン認証（設定編）" class="blogcard-wrap internal-blogcard-wrap a-wrap cf"><div class="blogcard internal-blogcard ib-left cf"><div class="blogcard-label internal-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail internal-blogcard-thumbnail"><img decoding="async" width="160" height="90" src="https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/14-e1742134167322-160x90.png" class="blogcard-thumb-image internal-blogcard-thumb-image wp-post-image" alt="" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/14-e1742134167322-160x90.png 160w, https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/14-e1742134167322-120x68.png 120w, https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/14-e1742134167322-320x180.png 320w" sizes="(max-width: 160px) 100vw, 160px" /></figure><div class="blogcard-content internal-blogcard-content"><div class="blogcard-title internal-blogcard-title">SpringSecurity6+MyBatisで独自ログイン認証（設定編）</div><div class="blogcard-snippet internal-blogcard-snippet">SpringSecurity6系は5.3までと比べるとSecurityConfigの書き方が変わってるので勉強を兼ねて独自のログイン画面を用意し認証機能をjavaで作成してみました。またMyBatisとH2 Databaseを使うことでDB構築をせずとも、手軽にDBと連携できるようにしてみました。</div></div><div class="blogcard-footer internal-blogcard-footer cf"><div class="blogcard-site internal-blogcard-site"><div class="blogcard-favicon internal-blogcard-favicon"><img decoding="async" src="https://www.google.com/s2/favicons?domain=https://gogorelaxlifeblog.com" alt="" class="blogcard-favicon-image internal-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain internal-blogcard-domain">gogorelaxlifeblog.com</div></div><div class="blogcard-date internal-blogcard-date"><div class="blogcard-post-date internal-blogcard-post-date">2025.03.16</div></div></div></div></a>
</div></figure>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc1">開発環境</span></h2>
<p>私が構築した時の開発環境は以下の通りとなります。</p>


<figure class="wp-block-flexible-table-block-table"><table class="has-fixed-layout"><thead><tr><th>環境</th><th>バージョン</th></tr></thead><tbody><tr><td>Java</td><td>17</td></tr><tr><td>SpringBoot</td><td>3.4.3</td></tr><tr><td>SpringSecurity</td><td>6.4.3</td></tr><tr><td>thymeleaf</td><td>3.1.3.RELEASE</td></tr><tr><td>MyBatis</td><td>3.0.4</td></tr><tr><td>H2 Database</td><td>2.3.232</td></tr><tr><td>lombok</td><td>1.18.36</td></tr></tbody></table></figure>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc2">ソース構成</span></h2>
<p>ソース構成は以下の通りです。</p>


<ul class="wp-block-list is-style-tree">
<li>login-project
<ul class="wp-block-list">
<li>src
<ul class="wp-block-list">
<li>main
<ul class="wp-block-list">
<li>java
<ul class="wp-block-list">
<li>com
<ul class="wp-block-list">
<li>example
<ul class="wp-block-list">
<li>loginproject
<ul class="wp-block-list">
<li>config
<ul class="wp-block-list">
<li>LoginUsersDataInitializer.java</li>



<li>SecurityConfig.java</li>



<li>SecurityProperties.java</li>
</ul>
</li>



<li>controller
<ul class="wp-block-list">
<li>LoginController.java</li>
</ul>
</li>



<li>entity
<ul class="wp-block-list">
<li>LoginUsers.java</li>
</ul>
</li>



<li>mapper
<ul class="wp-block-list">
<li>LoginUsersMapper.java</li>
</ul>
</li>



<li>service
<ul class="wp-block-list">
<li>LoginUsersDetailsService.java</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>



<li>LoginProjectApplication.java</li>
</ul>
</li>



<li>resources
<ul class="wp-block-list">
<li>com
<ul class="wp-block-list">
<li>example
<ul class="wp-block-list">
<li>loginproject
<ul class="wp-block-list">
<li>mapper
<ul class="wp-block-list">
<li>LoginUsersMapper.xml</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>



<li>static
<ul class="wp-block-list">
<li>css
<ul class="wp-block-list">
<li>style.css</li>
</ul>
</li>



<li>templates
<ul class="wp-block-list">
<li>home.html</li>



<li>login.html</li>
</ul>
</li>
</ul>
</li>



<li>application.yml</li>



<li>schema.sql</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>



<li>pom.xml</li>
</ul>
</li>
</ul>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc3">SQLの作成</span></h2>


<p>MyBatisのmapper.xmlを使ってSQLを使用します。</p>



<p>LoginUsersMapper.xmlを作成します。</p>



<pre class="wp-block-code xml"><code>&lt;?xml version="1.0" encoding="UTF-8" ?&gt;
&lt;!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"&gt;
&lt;mapper namespace="com.example.loginproject.mapper.LoginUsersMapper"&gt;
    &lt;select id="findUserByUsername" parameterType="string" resultType="LoginUsers"&gt;
        SELECT username, password
        FROM login_users
        WHERE username = #{username}
    &lt;/select&gt;
    &lt;insert id="insertUser" parameterType="LoginUsers"&gt;
        INSERT INTO login_users (username, password, enabled)
        VALUES (#{username}, #{password}, true);
    &lt;/insert&gt;
&lt;/mapper&gt;</code></pre>



<p>namespace=&#8221;com.example.loginproject.mapper.LoginUsersMapper&#8221;と指定することで後述のLoginUsersMapper.javaに用意されたメソッドとLoginUsersMapper.xmlに用意された各SQLのid名がリンクします。</p>



<p>リンクすることでLoginUsersMapper.javaからLoginUsersMapper.xmlのSQLが実行できるようになります。</p>



<p>resultType=&#8221;LoginUsers&#8221;はSELECTの結果をcom.example.loginproject.entity.LoginUsersクラスに設定するために指定してます。</p>



<p>parameterType=&#8221;LoginUsers&#8221;と指定することでLoginUsersクラスのusernameやpassword変数に格納した値がINSERT文のVALUES句のusernameやpasswordに設定されるようにしてます。</p>



<p>複数の値をSQLに渡す必要がある時に便利です。</p>



<p>対してparameterType=&#8221;string&#8221;は文字列型の値をusernameに設定できるようにしてます。</p>



<p>findUserByUsernameはログイン時などに入力したユーザー名が存在するかをチェックするためのSQLです。</p>



<p>insertUserはアプリ起動時にログインアカウントを生成するためのSQLです。</p>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc4">Entityクラスの作成</span></h2>


<p>後述のMapperインターフェースで使用するEntityクラスを作成します。</p>



<p>LoginUsersクラスを作成します。</p>



<pre class="wp-block-code java"><code>package com.example.loginproject.entity;

import lombok.Data;

@Data
public class LoginUsers {
    private String username;
    private String password;
}</code></pre>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc5">Mapperインターフェースの作成</span></h2>


<p>LoginUsersMapper.xmlを使うMapperインターフェースを作成します。</p>



<p>LoginUsersMapperクラスを作成します。</p>



<pre class="wp-block-code java"><code>package com.example.loginproject.mapper;

import com.example.loginproject.entity.LoginUsers;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface LoginUsersMapper {
    LoginUsers findUserByUsername(String username);
    void insertUser(LoginUsers loginUsers);
}</code></pre>



<p>MapperアノテーションによってMyBatisがLoginUsersMapperをMapperインターフェースとして自動的に検出します。</p>



<p>こうすることでLoginUsersMapper.javaを通じてLoginUsersMapper.xmlのSQLを使うことができます。</p>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc6">ログインアカウントを生成するクラスの作成</span></h2>


<p>今回はdata.sqlを使ってログインアカウントを生成しません。</p>



<p>H2のDBにデータを生成するのはdata.sqlを使うのが一般的だと思います。</p>



<p>しかし、data.sqlを使ってデータを生成しても、BCryptPasswordEncoderを使っているためにハッシュが一致せずに正しいパスワードを入力してもエラーとなってしまいます。</p>



<p>そのためアプリ起動時にデータをインサートする処理を実行するためにLoginUsersDataInitializerクラスを作成します。</p>



<pre class="wp-block-code java"><code>package com.example.loginproject.config;

import com.example.loginproject.entity.LoginUsers;
import com.example.loginproject.mapper.LoginUsersMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@RequiredArgsConstructor
@Component
public class LoginUsersDataInitializer {

    private final LoginUsersMapper loginUsersMapper;
    private final PasswordEncoder passwordEncoder;
    private final SecurityProperties securityProperties;

    @Bean
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public ApplicationRunner dataInitializer() {
        return args -&gt; {
            if (loginUsersMapper.findUserByUsername(securityProperties.getDebugUser().getUsername()) == null) {
                LoginUsers loginUsers = new LoginUsers();
                loginUsers.setUsername(securityProperties.getDebugUser().getUsername());
                loginUsers.setPassword(passwordEncoder.encode(securityProperties.getDebugUser().getPassword()));
                loginUsersMapper.insertUser(loginUsers);
            }
        };
    }
}</code></pre>



<p>dataInitializerメソッドではBeanアノテーションを付与することでBeanとしてApplicationRunnerを登録するようにしてます。</p>



<p>また以下の処理をApplicationRunnerとして返却することでApplicationRunnerクラスのrunメソッドの中身を以下の処理にしてます。</p>



<pre class="wp-block-code java"><code>            if (loginUsersMapper.findUserByUsername(securityProperties.getDebugUser().getUsername()) == null) {
                LoginUsers loginUsers = new LoginUsers();
                loginUsers.setUsername(securityProperties.getDebugUser().getUsername());
                loginUsers.setPassword(passwordEncoder.encode(securityProperties.getDebugUser().getPassword()));
                loginUsersMapper.insertUser(loginUsers);
            }</code></pre>



<p>Springはアプリ起動時にApplicationRunner.runを実行します。</p>



<p>そのため、アプリケーション起動時にBeanとして登録された上記のログインアカウントを生成するインサート処理が実行されるようになります。</p>



<p></p>



<p>以下はDBのトランザクション管理をするためのアノテーションです。</p>



<pre class="wp-block-code java"><code> @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)</code></pre>



<p>propagation = Propagation.REQUIREDはすでにトランザクションが存在する場合はそのトランザクションを使い、そうでない場合は新しくトランザクションを作るようにする設定となります。</p>



<p>このため、トランザクションを無駄に作成することがないため、パフォーマンスが向上します。</p>



<p>また複数のトランザクションが作成されることにより部分的にコミットされてしまうといったことがありません。</p>



<p>rollbackFor = Exception.classはExceptionクラスとExceptionクラスのサブクラス（つまり何らかの例外）が発生したらロールバックを行うという設定になります。</p>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc7">認証用のサービスクラス作成</span></h2>


<p>MyBatisを使う場合、UserDetailsServiceクラスを実装して独自の認証用サービスクラスを作成する必要があります。</p>



<p>そのため、LoginUsersDetailsServiceクラスを作成します。</p>



<pre class="wp-block-code java"><code>package com.example.loginproject.service;

import com.example.loginproject.entity.LoginUsers;
import com.example.loginproject.mapper.LoginUsersMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.userdetails.User;
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;

@RequiredArgsConstructor
@Service
public class LoginUsersDetailsService implements UserDetailsService {

    private final LoginUsersMapper loginUsersMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        LoginUsers loginUsers = loginUsersMapper.findUserByUsername(username);
        if (loginUsers == null) {
            throw new UsernameNotFoundException("User not found: " + username);
        }

        return User.builder()
                .username(loginUsers.getUsername())
                .password(loginUsers.getPassword())
                .build();
    }
}</code></pre>



<p>以下の処理によってログイン画面で入力されたユーザー名がDBに存在するかチェックしてます。</p>



<pre class="wp-block-code java"><code>        LoginUsers loginUsers = loginUsersMapper.findUserByUsername(username);
        if (loginUsers == null) {
            throw new UsernameNotFoundException("User not found: " + username);
        }</code></pre>



<p></p>



<p>存在している場合、ログイン画面で入力されたユーザー名とパスワードを使ってUserDetailsインスタンスを生成します。</p>



<pre class="wp-block-code java"><code>        return User.builder()
                .username(loginUsers.getUsername())
                .password(loginUsers.getPassword())
                .build();</code></pre>



<p>生成されたUserDetailsインスタンスを使ってSpringが提供するAuthenticationManagerクラスがDBに登録されているパスワードと一致してるか認証を行います。</p>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc8">ログイン画面用のコントローラー作成</span></h2>


<p>ログイン画面用のコントローラーを作成します。</p>



<pre class="wp-block-code java"><code>package com.example.loginproject.controller;

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

@Controller
public class LoginController {

    @GetMapping("/login")
    public String login() {
        return "login";
    }

    @GetMapping("/home")
    public String home() {
        return "home";
    }
}</code></pre>



<p>ログイン画面を表示するloginメソッドとログイン成功時の画面を表示するhomeメソッドを用意してます。</p>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc9">ログイン画面とログイン後の画面作成</span></h2>


<p>ログイン画面とログイン後の画面を作成します。</p>



<p>まずはログイン画面であるlogin.htmlです。</p>



<pre class="wp-block-code html"><code>&lt;!DOCTYPE html&gt;
&lt;html xmlns:th="http://www.thymeleaf.org"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;title&gt;ログイン - MyApp&lt;/title&gt;
    &lt;link rel="stylesheet" th:href="@{/css/style.css}"&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;div class="login-container"&gt;
    &lt;h2&gt;ログイン&lt;/h2&gt;
    &lt;div th:if="${param.error}" class="error-message"&gt;
        ユーザー名またはパスワードが間違っています。
    &lt;/div&gt;
    &lt;form th:action="@{/login}" method="post"&gt;
        &lt;div class="input-group"&gt;
            &lt;label for="username"&gt;ユーザー名&lt;/label&gt;
            &lt;input type="text" id="username" name="username" required&gt;
        &lt;/div&gt;
        &lt;div class="input-group"&gt;
            &lt;label for="password"&gt;パスワード&lt;/label&gt;
            &lt;input type="password" id="password" name="password" required&gt;
        &lt;/div&gt;
        &lt;button type="submit" class="login-button"&gt;ログイン&lt;/button&gt;
    &lt;/form&gt;
&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>



<p></p>



<p>ログイン画面で使用するCSSとしてstyle.cssを作成します。</p>



<pre class="wp-block-code css"><code>body {
    font-family: Arial, sans-serif;
    background-color: #f4f4f4;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
}
.login-container {
    background-color: #fff;
    padding: 20px;
    border-radius: 10px;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
    width: 300px;
    text-align: center;
}
.login-container h2 {
    margin-bottom: 20px;
}
.input-group {
    margin-bottom: 15px;
    text-align: left;
}
.input-group label {
    display: block;
    margin-bottom: 5px;
}
.input-group input {
    width: 95%;
    padding: 8px;
    border: 1px solid #ccc;
    border-radius: 5px;
}
.login-button {
    background-color: #007bff;
    color: #fff;
    border: none;
    padding: 10px;
    width: 100%;
    border-radius: 5px;
    cursor: pointer;
}
.login-button:hover {
    background-color: #0056b3;
}
.error-message {
    color: red;
    margin-bottom: 10px;
}</code></pre>



<p></p>



<p>続いてログイン後の画面であるhome.htmlです。</p>



<pre class="wp-block-code html"><code>&lt;!DOCTYPE html&gt;
&lt;html xmlns:th="http://www.thymeleaf.org"&gt;
&lt;head&gt;
    &lt;title&gt;Home&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;Welcome Home!&lt;/h1&gt;
    &lt;p&gt;ログイン成功しました&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc10">アプリ起動用のクラス作成</span></h2>


<p>最後にアプリ起動用のクラスを作成します。</p>



<pre class="wp-block-code java"><code>package com.example.loginproject;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class LoginProjectApplication {

    public static void main(String&#91;] args) {
        SpringApplication.run(LoginProjectApplication.class, args);
    }

}</code></pre>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc11">ログイン画面へアクセス</span></h2>


<p>ブラウザでhttp://localhost:8080/loginにアクセスすると以下のようなログイン画面が表示されます。</p>



<figure class="wp-block-image size-full is-resized"><img decoding="async" width="598" height="491" src="https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/image.png" alt="" class="wp-image-1669" style="width:603px;height:auto" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/image.png 598w, https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/image-300x246.png 300w" sizes="(max-width: 598px) 100vw, 598px" /></figure>



<p></p>



<p>ユーザー名とパスワードはapplication.ymlの以下の値となります。</p>



<pre class="wp-block-code"><code>  debug-user:
    username: user
    password: password</code></pre>



<p>そのため、ユーザー名にuser、パスワードにpasswordと入力し、ログインボタンを押下すればログインが成功します。</p>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc12">h2-consoleへアクセス</span></h2>


<p>ブラウザでhttp://localhost:8080/h2-consoleへアクセスすると以下のような画面へアクセスできます。</p>



<figure class="wp-block-image size-full"><img decoding="async" width="454" height="364" src="https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/image-1.png" alt="" class="wp-image-1672" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/image-1.png 454w, https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/image-1-300x241.png 300w" sizes="(max-width: 454px) 100vw, 454px" /></figure>



<p>Connectボタンを押すと以下のような画面が表示できます。</p>



<figure class="wp-block-image size-full"><img decoding="async" width="823" height="369" src="https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/image-2.png" alt="" class="wp-image-1674" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/image-2.png 823w, https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/image-2-300x135.png 300w, https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/image-2-768x344.png 768w" sizes="(max-width: 823px) 100vw, 823px" /></figure>



<p>この画面では上記のようにSQLを実行でき、H2上の各テーブルのデータを見ることができるので便利です。</p>



<p></p>



<p>なお、記載内容が多くなったため、記事を2つに分けてます。</p>



<p>前編は以下をご参照ください。</p>



<figure class="wp-block-embed is-type-wp-embed"><div class="wp-block-embed__wrapper">

<a href="https://gogorelaxlifeblog.com/springsecurity6-setup" title="SpringSecurity6+MyBatisで独自ログイン認証（設定編）" class="blogcard-wrap internal-blogcard-wrap a-wrap cf"><div class="blogcard internal-blogcard ib-left cf"><div class="blogcard-label internal-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail internal-blogcard-thumbnail"><img decoding="async" width="160" height="90" src="https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/14-e1742134167322-160x90.png" class="blogcard-thumb-image internal-blogcard-thumb-image wp-post-image" alt="" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/14-e1742134167322-160x90.png 160w, https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/14-e1742134167322-120x68.png 120w, https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/14-e1742134167322-320x180.png 320w" sizes="(max-width: 160px) 100vw, 160px" /></figure><div class="blogcard-content internal-blogcard-content"><div class="blogcard-title internal-blogcard-title">SpringSecurity6+MyBatisで独自ログイン認証（設定編）</div><div class="blogcard-snippet internal-blogcard-snippet">SpringSecurity6系は5.3までと比べるとSecurityConfigの書き方が変わってるので勉強を兼ねて独自のログイン画面を用意し認証機能をjavaで作成してみました。またMyBatisとH2 Databaseを使うことでDB構築をせずとも、手軽にDBと連携できるようにしてみました。</div></div><div class="blogcard-footer internal-blogcard-footer cf"><div class="blogcard-site internal-blogcard-site"><div class="blogcard-favicon internal-blogcard-favicon"><img decoding="async" src="https://www.google.com/s2/favicons?domain=https://gogorelaxlifeblog.com" alt="" class="blogcard-favicon-image internal-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain internal-blogcard-domain">gogorelaxlifeblog.com</div></div><div class="blogcard-date internal-blogcard-date"><div class="blogcard-post-date internal-blogcard-post-date">2025.03.16</div></div></div></div></a>
</div></figure>



<p></p>
]]></content:encoded>
					
					<wfw:commentRss>https://gogorelaxlifeblog.com/springsecurity6-implementation/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>SpringSecurity6+MyBatisで独自ログイン認証（設定編）</title>
		<link>https://gogorelaxlifeblog.com/springsecurity6-setup/</link>
					<comments>https://gogorelaxlifeblog.com/springsecurity6-setup/#respond</comments>
		
		<dc:creator><![CDATA[ごゆるり一族]]></dc:creator>
		<pubDate>Sun, 16 Mar 2025 14:18:18 +0000</pubDate>
				<category><![CDATA[技術]]></category>
		<category><![CDATA[spring]]></category>
		<guid isPermaLink="false">https://gogorelaxlifeblog.com/?p=1517</guid>

					<description><![CDATA[SpringSecurity6系は5.3までと比べるとSecurityConfigの書き方が変わってるので勉強を兼ねて独自のログイン画面を用意し認証機能をjavaで作成してみました。 またMyBatisとH2 Datab [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>SpringSecurity6系は5.3までと比べるとSecurityConfigの書き方が変わってるので勉強を兼ねて独自のログイン画面を用意し認証機能をjavaで作成してみました。</p>



<p>またMyBatisとH2 Databaseを使うことでDB構築をせずとも、手軽にDBと連携できるようにしてみました。</p>



<p>なお、記載内容が多くなったため、記事を2つに分けてます。</p>



<p>後編は以下をご参照ください。</p>



<figure class="wp-block-embed is-type-wp-embed"><div class="wp-block-embed__wrapper">

<a href="https://gogorelaxlifeblog.com/springsecurity6-implementation" title="SpringSecurity6+MyBatisで独自ログイン認証（実装編）" class="blogcard-wrap internal-blogcard-wrap a-wrap cf"><div class="blogcard internal-blogcard ib-left cf"><div class="blogcard-label internal-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail internal-blogcard-thumbnail"><img decoding="async" width="160" height="90" src="https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/15-160x90.png" class="blogcard-thumb-image internal-blogcard-thumb-image wp-post-image" alt="" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/15-160x90.png 160w, https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/15-120x68.png 120w, https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/15-320x180.png 320w" sizes="(max-width: 160px) 100vw, 160px" /></figure><div class="blogcard-content internal-blogcard-content"><div class="blogcard-title internal-blogcard-title">SpringSecurity6+MyBatisで独自ログイン認証（実装編）</div><div class="blogcard-snippet internal-blogcard-snippet">SpringSecurity6系は5.3までと比べるとSecurityConfigの書き方が変わってるので勉強を兼ねて独自のログイン画面を用意し認証機能をjavaで作成してみました。またMyBatisとH2 Databaseを使うことでDB構築をせずとも、手軽にDBと連携できるようにしてみました。</div></div><div class="blogcard-footer internal-blogcard-footer cf"><div class="blogcard-site internal-blogcard-site"><div class="blogcard-favicon internal-blogcard-favicon"><img decoding="async" src="https://www.google.com/s2/favicons?domain=https://gogorelaxlifeblog.com" alt="" class="blogcard-favicon-image internal-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain internal-blogcard-domain">gogorelaxlifeblog.com</div></div><div class="blogcard-date internal-blogcard-date"><div class="blogcard-post-date internal-blogcard-post-date">2025.03.16</div></div></div></div></a>
</div></figure>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc1">開発環境</span></h2>
<p>私が構築した時の開発環境は以下の通りとなります。</p>


<figure class="wp-block-flexible-table-block-table"><table class="has-fixed-layout"><thead><tr><th>環境</th><th>バージョン</th></tr></thead><tbody><tr><td>Java</td><td>17</td></tr><tr><td>SpringBoot</td><td>3.4.3</td></tr><tr><td>SpringSecurity</td><td>6.4.3</td></tr><tr><td>thymeleaf</td><td>3.1.3.RELEASE</td></tr><tr><td>MyBatis</td><td>3.0.4</td></tr><tr><td>H2 Database</td><td>2.3.232</td></tr><tr><td>lombok</td><td>1.18.36</td></tr></tbody></table></figure>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc2">ソース構成</span></h2>
<p>ソース構成は以下の通りです。</p>


<ul class="wp-block-list is-style-tree">
<li>login-project
<ul class="wp-block-list">
<li>src
<ul class="wp-block-list">
<li>main
<ul class="wp-block-list">
<li>java
<ul class="wp-block-list">
<li>com
<ul class="wp-block-list">
<li>example
<ul class="wp-block-list">
<li>loginproject
<ul class="wp-block-list">
<li>config
<ul class="wp-block-list">
<li>LoginUsersDataInitializer.java</li>



<li>SecurityConfig.java</li>



<li>SecurityProperties.java</li>
</ul>
</li>



<li>controller
<ul class="wp-block-list">
<li>LoginController.java</li>
</ul>
</li>



<li>entity
<ul class="wp-block-list">
<li>LoginUsers.java</li>
</ul>
</li>



<li>mapper
<ul class="wp-block-list">
<li>LoginUsersMapper.java</li>
</ul>
</li>



<li>service
<ul class="wp-block-list">
<li>LoginUsersDetailsService.java</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>



<li>LoginProjectApplication.java</li>
</ul>
</li>



<li>resources
<ul class="wp-block-list">
<li>com
<ul class="wp-block-list">
<li>example
<ul class="wp-block-list">
<li>loginproject
<ul class="wp-block-list">
<li>mapper
<ul class="wp-block-list">
<li>LoginUsersMapper.xml</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>



<li>static
<ul class="wp-block-list">
<li>css
<ul class="wp-block-list">
<li>style.css</li>
</ul>
</li>



<li>templates
<ul class="wp-block-list">
<li>home.html</li>



<li>login.html</li>
</ul>
</li>
</ul>
</li>



<li>application.yml</li>



<li>schema.sql</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>



<li>pom.xml</li>
</ul>
</li>
</ul>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc3">pom.xml</span></h2>
<p>必要なライブラリを導入するためにpom.xmlを以下のように記述します。</p>


<pre class="wp-block-code xml"><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;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"&gt;
    &lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;
    &lt;parent&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-parent&lt;/artifactId&gt;
        &lt;version&gt;3.4.3&lt;/version&gt;
        &lt;relativePath/&gt; &lt;!-- lookup parent from repository --&gt;
    &lt;/parent&gt;
    &lt;groupId&gt;com.example&lt;/groupId&gt;
    &lt;artifactId&gt;login-project&lt;/artifactId&gt;
    &lt;version&gt;0.0.1-SNAPSHOT&lt;/version&gt;
    &lt;name&gt;login-project&lt;/name&gt;
    &lt;description&gt;login-project&lt;/description&gt;
    &lt;url/&gt;
    &lt;licenses&gt;
        &lt;license/&gt;
    &lt;/licenses&gt;
    &lt;developers&gt;
        &lt;developer/&gt;
    &lt;/developers&gt;
    &lt;scm&gt;
        &lt;connection/&gt;
        &lt;developerConnection/&gt;
        &lt;tag/&gt;
        &lt;url/&gt;
    &lt;/scm&gt;
    &lt;properties&gt;
        &lt;java.version&gt;17&lt;/java.version&gt;
    &lt;/properties&gt;
    &lt;dependencies&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-starter-security&lt;/artifactId&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-starter-thymeleaf&lt;/artifactId&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.mybatis.spring.boot&lt;/groupId&gt;
            &lt;artifactId&gt;mybatis-spring-boot-starter&lt;/artifactId&gt;
            &lt;version&gt;3.0.4&lt;/version&gt;
        &lt;/dependency&gt;

        &lt;dependency&gt;
            &lt;groupId&gt;com.h2database&lt;/groupId&gt;
            &lt;artifactId&gt;h2&lt;/artifactId&gt;
            &lt;scope&gt;runtime&lt;/scope&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.projectlombok&lt;/groupId&gt;
            &lt;artifactId&gt;lombok&lt;/artifactId&gt;
            &lt;optional&gt;true&lt;/optional&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt;
            &lt;scope&gt;test&lt;/scope&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.security&lt;/groupId&gt;
            &lt;artifactId&gt;spring-security-test&lt;/artifactId&gt;
            &lt;scope&gt;test&lt;/scope&gt;
        &lt;/dependency&gt;
    &lt;/dependencies&gt;

    &lt;build&gt;
        &lt;plugins&gt;
            &lt;plugin&gt;
                &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
                &lt;artifactId&gt;maven-compiler-plugin&lt;/artifactId&gt;
                &lt;configuration&gt;
                    &lt;annotationProcessorPaths&gt;
                        &lt;path&gt;
                            &lt;groupId&gt;org.projectlombok&lt;/groupId&gt;
                            &lt;artifactId&gt;lombok&lt;/artifactId&gt;
                        &lt;/path&gt;
                    &lt;/annotationProcessorPaths&gt;
                &lt;/configuration&gt;
            &lt;/plugin&gt;
            &lt;plugin&gt;
                &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
                &lt;artifactId&gt;spring-boot-maven-plugin&lt;/artifactId&gt;
                &lt;configuration&gt;
                    &lt;excludes&gt;
                        &lt;exclude&gt;
                            &lt;groupId&gt;org.projectlombok&lt;/groupId&gt;
                            &lt;artifactId&gt;lombok&lt;/artifactId&gt;
                        &lt;/exclude&gt;
                    &lt;/excludes&gt;
                &lt;/configuration&gt;
            &lt;/plugin&gt;
        &lt;/plugins&gt;
    &lt;/build&gt;

&lt;/project&gt;
</code></pre>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc4">application.ymlの作成</span></h2>


<p>アプリの設定を行うapplication.ymlを以下のように記述します。</p>



<pre class="wp-block-code"><code>spring:
  datasource:
    url: jdbc:h2:mem:testdb
    driver-class-name: org.h2.Driver
    username: sa
    password:
  h2:
    console:
      enabled: true
      path: /h2-console

logging:
  level:
    org.springframework.security: DEBUG

security:
  login-url: /login
  home-url: /home
  permit-urls:
    - /login
    - /css/**
    - /js/**
    - /images/**
  debug-user:
    username: user
    password: password
  h2-console-url: /h2-console/**

mybatis:
  type-aliases-package: com.example.loginproject.entity
  configuration:
    map-underscore-to-camel-case: true</code></pre>



<p>datasourceプレフィックスのpasswordは未入力で大丈夫です。</p>



<p></p>



<p>以下はh2のDBの中身を見れるようにh2-consoleを有効化してます。</p>



<pre class="wp-block-code"><code>h2:
  console:
    enabled: true
    path: /h2-console</code></pre>



<p></p>



<p>以下はデバッグ用にSpringSecurityのログレベルをDEBUGに指定してます。</p>



<pre class="wp-block-preformatted">org.springframework.security: DEBUG</pre>



<p></p>



<p>securityプリフィックス以下にある値は後述で示すSecurityPropertiesクラスで使用してます。</p>



<p>例えばlogin-urlにはログイン用のURLを記載します。</p>



<p>permit-urlsには未認証でもアクセスできるURLを記載します。</p>



<p>CSSやJSや画像などの静的ファイルは未認証でもアクセスできないと使用できないため許可します。</p>



<p><br>type-aliases-packageで指定したパッケージ配下のクラスはパッケージ名を省略してMyBatisのXMLで使用できます。</p>



<p>map-underscore-to-camel-case: trueを指定することでデータベースのカラム名をキャメルケースに自動変換できるようにします。</p>



<pre class="wp-block-code"><code>mybatis:
  type-aliases-package: com.example.loginproject.entity
  configuration:
    map-underscore-to-camel-case: true</code></pre>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc5">schema.sqlの作成</span></h2>


<p>アプリケーション起動時にDBのテーブルが作成されるようにschema.sqlを作成します。</p>



<pre class="wp-block-code sql"><code>CREATE TABLE IF NOT EXISTS login_users (
    username VARCHAR(50) PRIMARY KEY,
    password VARCHAR(100) NOT NULL,
    enabled BOOLEAN NOT NULL
);</code></pre>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc6">application.ymlを扱うクラスの作成</span></h2>
<p>application.ymlにlogin-urlといったログイン用のURLなどを定数として定義しました。</p>
<p>それらの定数を扱うためのクラスであるSecurityPropertiesクラスを作成します。</p>


<pre class="wp-block-code java"><code>package com.example.loginproject.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.List;

@Data
@Component
@ConfigurationProperties(prefix = "security")
public class SecurityProperties {
    private String loginUrl;
    private String homeUrl;
    private List&lt;String&gt; permitUrls;
    private DebugUser debugUser;

    private String h2ConsoleUrl;

    @Data
    public static class DebugUser {
        private String username;
        private String password;
    }
}</code></pre>



<p>lombokのDataアノテーションによってgetterとsetterメソッドを自作しないで自動的に生成されるようにしてます。</p>



<p>ConfigurationPropertiesアノテーションによってapplication.yml上のsecurityプレフィックス配下にあるものをすべてこのクラスの変数に格納するようにしてます。</p>



<p>permit-urlsプレフィックスが以下のようにハイフンを使って複数値を定義しているため、List&lt;String&gt;でpermitUrlsを宣言してます。</p>



<pre class="wp-block-code"><code>  permit-urls:
    - /login
    - /css/**
    - /js/**
    - /images/**</code></pre>



<p>debug-userプレフィックスが複数の項目を持つため、DebugUserというインナークラスを作成しています。</p>



<pre class="wp-block-code"><code>  debug-user:
    username: user
    password: password</code></pre>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc7">SpringSecurityの設定</span></h2>


<p>SpringSecurityの設定を行うクラスを作成します。</p>



<pre class="wp-block-code java"><code>package com.example.loginproject.config;

import lombok.RequiredArgsConstructor;
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.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

@RequiredArgsConstructor
@Configuration
public class SecurityConfig {

    private final SecurityProperties securityProperties;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        final String X_FRAME_OPTIONS = "X-Frame-Options";
        final String X_FRAME_OPTIONS_SAMEORIGIN = "SAMEORIGIN";
        final String X_FRAME_OPTIONS_DENY = "DENY";

        http
                .authorizeHttpRequests(auth -&gt; auth
                        .requestMatchers(securityProperties.getPermitUrls().toArray(String&#91;]::new)).permitAll()
                        .requestMatchers(securityProperties.getH2ConsoleUrl()).permitAll()
                        .anyRequest().authenticated()
                )
                .formLogin(login -&gt; login
                        .loginPage(securityProperties.getLoginUrl())
                        .defaultSuccessUrl(securityProperties.getHomeUrl(), true)
                        .permitAll()
                )
                .csrf(csrf -&gt; csrf
                        .ignoringRequestMatchers(securityProperties.getH2ConsoleUrl())
                )
                .headers(headers -&gt; headers
                        .addHeaderWriter((request, response) -&gt; {
                            if (new AntPathRequestMatcher(securityProperties.getH2ConsoleUrl()).matches(request)) {
                                response.setHeader(X_FRAME_OPTIONS, X_FRAME_OPTIONS_SAMEORIGIN);
                            } else {
                                response.setHeader(X_FRAME_OPTIONS, X_FRAME_OPTIONS_DENY);
                            }
                        })
                );
        return http.build();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}</code></pre>



<p></p>



<p>RequiredArgsConstructorアノテーションはfinal修飾子が付与されたメンバ変数を引数に持つコンストラクタを自動生成します。</p>



<p>これによって以下のようなコンストラクタを作らずともSecurityPropertiesクラスをインジェクションできるようにし、SecurityConfigクラスでSecurityPropertiesクラスを使用可能としてます。</p>



<pre class="wp-block-code java"><code>public SecurityConfig(final SecurityProperties securityProperties) {
    this.securityProperties = securityProperties;
}</code></pre>



<p>以下によってapplication.ymlのpermit-urlsとh2-console-urlプレフィックスで定義したURLについてはログイン認証をせずにアクセスできるようにしてます。</p>



<pre class="wp-block-code java"><code>.requestMatchers(securityProperties.getPermitUrls().toArray(String&#91;]::new)).permitAll()
 .requestMatchers(securityProperties.getH2ConsoleUrl()).permitAll()</code></pre>



<p>以下によってログイン画面はapplication.ymlのlogin-urlプレフィックスに定義したURLであることを指定してます。</p>



<pre class="wp-block-code java"><code>.loginPage(securityProperties.getLoginUrl())</code></pre>



<p>以下によってログイン後の画面はapplication.ymlのhome-urlプレフィックスに定義したURLであることを指定してます。</p>



<pre class="wp-block-code java"><code>.defaultSuccessUrl(securityProperties.getHomeUrl(), true)</code></pre>



<p>以下によってh2-consoleのみcsrfトークンを無効化してます。</p>



<pre class="wp-block-code java"><code>.csrf(csrf -&gt; csrf
        .ignoringRequestMatchers(securityProperties.getH2ConsoleUrl())
)</code></pre>



<p>以下によってh2-consoleのみiframeを使用可能にしてます。</p>



<pre class="wp-block-code java"><code>.headers(headers -&gt; headers
        .addHeaderWriter((request, response) -&gt; {
            if (new AntPathRequestMatcher(securityProperties.getH2ConsoleUrl()).matches(request)) {
                response.setHeader(X_FRAME_OPTIONS, X_FRAME_OPTIONS_SAMEORIGIN);
            } else {
                response.setHeader(X_FRAME_OPTIONS, X_FRAME_OPTIONS_DENY);
            }
        })
);</code></pre>



<p>以下によってパスワードの暗号化にBCryptPasswordEncoderを使用するように指定してます。</p>



<pre class="wp-block-code"><code>@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}</code></pre>



<p></p>



<p>なお、記載内容が多くなったため、記事を2つに分けてます。</p>



<p>後編は以下をご参照ください。</p>



<figure class="wp-block-embed is-type-wp-embed"><div class="wp-block-embed__wrapper">

<a href="https://gogorelaxlifeblog.com/springsecurity6-implementation" title="SpringSecurity6+MyBatisで独自ログイン認証（実装編）" class="blogcard-wrap internal-blogcard-wrap a-wrap cf"><div class="blogcard internal-blogcard ib-left cf"><div class="blogcard-label internal-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail internal-blogcard-thumbnail"><img decoding="async" width="160" height="90" src="https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/15-160x90.png" class="blogcard-thumb-image internal-blogcard-thumb-image wp-post-image" alt="" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/15-160x90.png 160w, https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/15-120x68.png 120w, https://gogorelaxlifeblog.com/wp-content/uploads/2025/03/15-320x180.png 320w" sizes="(max-width: 160px) 100vw, 160px" /></figure><div class="blogcard-content internal-blogcard-content"><div class="blogcard-title internal-blogcard-title">SpringSecurity6+MyBatisで独自ログイン認証（実装編）</div><div class="blogcard-snippet internal-blogcard-snippet">SpringSecurity6系は5.3までと比べるとSecurityConfigの書き方が変わってるので勉強を兼ねて独自のログイン画面を用意し認証機能をjavaで作成してみました。またMyBatisとH2 Databaseを使うことでDB構築をせずとも、手軽にDBと連携できるようにしてみました。</div></div><div class="blogcard-footer internal-blogcard-footer cf"><div class="blogcard-site internal-blogcard-site"><div class="blogcard-favicon internal-blogcard-favicon"><img decoding="async" src="https://www.google.com/s2/favicons?domain=https://gogorelaxlifeblog.com" alt="" class="blogcard-favicon-image internal-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain internal-blogcard-domain">gogorelaxlifeblog.com</div></div><div class="blogcard-date internal-blogcard-date"><div class="blogcard-post-date internal-blogcard-post-date">2025.03.16</div></div></div></div></a>
</div></figure>



<p></p>
]]></content:encoded>
					
					<wfw:commentRss>https://gogorelaxlifeblog.com/springsecurity6-setup/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>ubuntuにapacheをソースからインストール</title>
		<link>https://gogorelaxlifeblog.com/apache/</link>
					<comments>https://gogorelaxlifeblog.com/apache/#respond</comments>
		
		<dc:creator><![CDATA[ごゆるり一族]]></dc:creator>
		<pubDate>Sun, 25 Feb 2024 12:06:26 +0000</pubDate>
				<category><![CDATA[技術]]></category>
		<category><![CDATA[apache]]></category>
		<guid isPermaLink="false">https://gogorelaxlifeblog.com/?p=1487</guid>

					<description><![CDATA[必要ない資材をインストールしたくなかったため、WSL環境のubuntu（Linux）にapacheをソースからインストールしてみました。 開発環境 私が構築した時の開発環境は以下の通りとなります。 aprのインストール  [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>必要ない資材をインストールしたくなかったため、WSL環境のubuntu（Linux）にapacheをソースからインストールしてみました。</p>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc1">開発環境</span></h2>
<p>私が構築した時の開発環境は以下の通りとなります。</p>


<figure class="wp-block-flexible-table-block-table"><table class="has-fixed-layout"><thead><tr><th>環境</th><th>バージョン</th></tr></thead><tbody><tr><td>ubuntu</td><td>22.04.3</td></tr><tr><td>apache</td><td>2.4.57</td></tr></tbody></table></figure>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc2">aprのインストール</span></h2>
<p>apacheをインストールするために必要となるapr（Apache Portable Runtime）をインストールします。</p>


<pre class="wp-block-code bash"><code>sudo apt install gcc
sudo apt install make
cd /usr/local/src
sudo wget https://dlcdn.apache.org//apr/apr-1.7.4.tar.gz
sudo tar xvf apr-1.7.4.tar.gz
cd apr-1.7.4/
sudo ./configure --prefix=/opt/apr/apr-1.7.4
sudo make
sudo make test
sudo make install</code></pre>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc3">apr-utilのインストール</span></h2>
<p>同じくapacheをインストールするために必要となるapr-utilをインストールします。</p>


<pre class="wp-block-code bash"><code>cd /usr/local/src
sudo apt install libexpat1-dev
sudo wget https://dlcdn.apache.org//apr/apr-util-1.6.3.tar.gz
sudo tar xvf apr-util-1.6.3.tar.gz
cd apr-util-1.6.3/
sudo ./configure --prefix=/opt/apr-util/apr-util-1.6.3 --with-apr=/opt/apr/apr-1.7.4
sudo make
sudo make test
sudo make install</code></pre>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc4">apacheのインストール</span></h2>
<p>続いてapacheのインストールをします。</p>


<pre class="wp-block-code bash"><code>cd /usr/local/src
sudo apt install libpcre3-dev
sudo apt install libssl-dev
sudo wget http://archive.apache.org/dist/httpd/httpd-2.4.57.tar.gz
sudo tar xvf httpd-2.4.57.tar.gz
cd httpd-2.4.57/
sudo ./configure --enable-ssl --enable-rewrite --enable-so --with-apr=/opt/apr/apr-1.7.4 --with-apr-util=/opt/apr-util/apr-util-1.6.3
sudo make
sudo make install</code></pre>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc5">mod_sslの導入</span></h2>
<p>https通信ができるようにmod_sslを導入します。</p>


<pre class="wp-block-code bash"><code>cd /usr/local/src/httpd-2.4.57/modules/ssl/
sudo /usr/local/apache2/bin/apxs -c mod_ssl.c
sudo /usr/local/apache2/bin/apxs -i -a -c -I /usr/include/openssl -I ../md -D HAVE_OPENSSL=1 -lcrypto -lssl *.c</code></pre>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc6">mod_rewriteの導入</span></h2>


<p>リダイレクト処理が行えるようにmod_rewriteを導入します。</p>



<pre class="wp-block-code bash"><code>cd /usr/local/src/httpd-2.4.57/modules/mappers
sudo /usr/local/apache2/bin/apxs -i -a -n rewrite mod_rewrite.la</code></pre>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc7">mod_proxyの導入</span></h2>
<p>tomcatと連携できるようにmod_proxyを導入します。</p>


<pre class="wp-block-code bash"><code>cd /usr/local/src/httpd-2.4.57/modules/proxy
sudo /usr/local/apache2/bin/apxs -i -a -c mod_proxy.c proxy_util.c
sudo /usr/local/apache2/bin/apxs -i -a -c mod_proxy_ajp.c ajp_*.c</code></pre>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc8">apacheのサービス起動 </span></h2>
<p>systemctlコマンドを使ってapacheが起動できるようにapache2.serviceを作成します。</p>


<pre class="wp-block-code"><code>sudo vi /usr/lib/systemd/system/apache2.service

&#91;Unit]
Description=Apache 2
After=network.target

&#91;Service]
Type=oneshot
ExecStart=/usr/local/apache2/bin/apachectl start
ExecStop=/usr/local/apache2/bin/apachectl stop
RemainAfterExit=yes
User=root
Group=root

&#91;Install]
WantedBy=multi-user.target</code></pre>



<p>最後にapacheを起動します。</p>



<pre class="wp-block-code"><code>sudo systemctl start apache2</code></pre>



<p>ブラウザ上で「http://localhost」にアクセスし、「It works！」と表示されればapacheは正常に動作してます。</p>



<p></p>



<p></p>
]]></content:encoded>
					
					<wfw:commentRss>https://gogorelaxlifeblog.com/apache/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Spring Batch 5でtaskletジョブの実装</title>
		<link>https://gogorelaxlifeblog.com/springbatch5tasklet/</link>
					<comments>https://gogorelaxlifeblog.com/springbatch5tasklet/#respond</comments>
		
		<dc:creator><![CDATA[ごゆるり一族]]></dc:creator>
		<pubDate>Mon, 05 Feb 2024 10:58:30 +0000</pubDate>
				<category><![CDATA[技術]]></category>
		<category><![CDATA[spring]]></category>
		<guid isPermaLink="false">https://gogorelaxlifeblog.com/?p=1435</guid>

					<description><![CDATA[Spring Batch5でtaskletジョブの実装をしてみました。 Spring Batch4から5でのバージョンアップに伴い、JobBuilderFactoryなどが非推奨となってたので勉強も兼ねて作成してみました [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>Spring Batch5でtaskletジョブの実装をしてみました。</p>



<p>Spring Batch4から5でのバージョンアップに伴い、JobBuilderFactoryなどが非推奨となってたので勉強も兼ねて作成してみました。</p>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc1">開発環境</span></h2>
<p>私が構築した時の開発環境は以下の通りとなります。</p>


<figure class="wp-block-flexible-table-block-table"><table class="has-fixed-layout"><thead><tr><th>環境</th><th>バージョン</th></tr></thead><tbody><tr><td>Java</td><td>17</td></tr><tr><td>SpringBatch</td><td>5.1.0</td></tr></tbody></table></figure>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc2">ソース構成</span></h2>
<p>環境を作った際のソース構成は以下の通りです。</p>


<ul class="wp-block-list is-style-tree">
<li>springBatchSample
<ul class="wp-block-list">
<li>src
<ul class="wp-block-list">
<li>main
<ul class="wp-block-list">
<li>java
<ul class="wp-block-list">
<li>com.example.batchprocessing
<ul class="wp-block-list">
<li>BatchConfiguration.java</li>



<li>JobCompletionNotificationListener.java</li>



<li>Person.java</li>



<li>PersonTasklet.java</li>



<li>SpringBatchSampleApplication.java</li>
</ul>
</li>
</ul>
</li>



<li>resource
<ul class="wp-block-list">
<li>application.yml</li>



<li>sample-data.csv</li>



<li>schema-all.sql</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>



<li>pom.xml</li>
</ul>
</li>
</ul>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc3">pom.xml</span></h2>
<p>必要なライブラリを導入するためにpom.xmlを以下のように記述します。</p>


<pre class="wp-block-code xml"><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"&gt;
    &lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;
    &lt;parent&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-parent&lt;/artifactId&gt;
        &lt;version&gt;3.2.0&lt;/version&gt;
        &lt;relativePath/&gt; &lt;!-- lookup parent from repository --&gt;
    &lt;/parent&gt;
    &lt;groupId&gt;com.example&lt;/groupId&gt;
    &lt;artifactId&gt;springBatchSample&lt;/artifactId&gt;
    &lt;version&gt;0.0.1-SNAPSHOT&lt;/version&gt;
    &lt;name&gt;springBatchSample&lt;/name&gt;
    &lt;description&gt;springBatchSample&lt;/description&gt;
    &lt;properties&gt;
        &lt;java.version&gt;17&lt;/java.version&gt;
    &lt;/properties&gt;
    &lt;dependencies&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-starter-batch&lt;/artifactId&gt;
        &lt;/dependency&gt;

        &lt;dependency&gt;
            &lt;groupId&gt;com.h2database&lt;/groupId&gt;
            &lt;artifactId&gt;h2&lt;/artifactId&gt;
            &lt;scope&gt;runtime&lt;/scope&gt;
        &lt;/dependency&gt;
    &lt;/dependencies&gt;

    &lt;build&gt;
        &lt;plugins&gt;
            &lt;plugin&gt;
                &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
                &lt;artifactId&gt;spring-boot-maven-plugin&lt;/artifactId&gt;
            &lt;/plugin&gt;
        &lt;/plugins&gt;
    &lt;/build&gt;

&lt;/project&gt;
</code></pre>



<p>spring-boot-starter-batchを指定することでSpringBatch5系と必要なライブラリを導入してます。</p>



<p>またデータベースのはh2を使いますのでh2のライブラリを導入してます。</p>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc4">resources配下資材の準備</span></h2>


<p>resources配下資材の作成をします。</p>



<p>アプリの設定を行うapplication.ymlを以下のように記述します。</p>



<pre class="wp-block-code"><code>spring:
  datasource:
    driver-class-name: org.h2.Driver
    url: jdbc:h2:mem:testdb
    username: sa
    password:
sql:
  init:
    schema-locations: classpath:sql/schema-all.sql</code></pre>



<p>passwordは未入力で大丈夫です。</p>



<p>サンプル用のCSVファイルであるsample-data.csvを以下のように記述します。</p>



<pre class="wp-block-code"><code>Jill,Doe
Joe,Doe
Justin,Doe
Jane,Doe
John,Doe</code></pre>



<p>データベース内にテーブルを作成するためのschema-all.sqlを以下のように記述します。</p>



<pre class="wp-block-code sql"><code>DROP TABLE people IF EXISTS;

CREATE TABLE people
(
    person_id  BIGINT AUTO_INCREMENT PRIMARY KEY,
    first_name VARCHAR(20),
    last_name  VARCHAR(20)
);</code></pre>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc5">ビジネスクラスの作成</span></h2>


<p>データを保持するためのクラスであるPersonクラスを作成します。</p>



<pre class="wp-block-code java"><code>package com.example.batchprocessing;

public record Person(String firstName, String lastName) {
}</code></pre>



<p>recordクラスとして作成しているため、コンパイル時は以下のようなコードが生成されるのでコンストラクタやgetterを記述する必要がありません。</p>



<pre class="wp-block-code java"><code>package com.example.batchprocessing;

public record Person(String firstName, String lastName) {
    public Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public String firstName() {
        return this.firstName;
    }

    public String lastName() {
        return this.lastName;
    }
}</code></pre>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc6">taskletクラスの作成</span></h2>


<p>Taskletの実装クラスであるPersonTaskletを作成します。</p>



<pre class="wp-block-code java"><code>package com.example.batchprocessing;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.item.Chunk;
import org.springframework.batch.item.ItemStreamReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.stereotype.Component;

@Component
public class PersonTasklet implements Tasklet {

    private static final Logger log = LoggerFactory.getLogger(PersonTasklet.class);

    final ItemStreamReader&lt;Person&gt; reader;

    final ItemWriter&lt;Person&gt; writer;

    public PersonTasklet(ItemStreamReader&lt;Person&gt; reader, ItemWriter&lt;Person&gt; writer) {
        this.reader = reader;
        this.writer = writer;
    }

    @Override
    public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {

        Person person;

        Chunk&lt;Person&gt; personList = new Chunk&lt;&gt;();

        try {
            reader.open(chunkContext.getStepContext().getStepExecution().getExecutionContext());

            while ((person = reader.read()) != null) {
                String firstName = person.firstName().toUpperCase();
                String lastName = person.lastName().toUpperCase();
                Person transformedPerson = new Person(firstName, lastName);
                personList.add(person);
                log.info("Converting ({}) into ({})", person, transformedPerson);
            }

            writer.write(personList);

        } finally {
            reader.close();
        }
        return RepeatStatus.FINISHED;
    }
}</code></pre>



<p>PersonクラスのfirstNameやlastNameにCSVファイルの各レコードが格納されます。</p>



<p>それらの値をUpperCaseに変換した上でListに格納し、writer.writeに引数として渡します。</p>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc7">ジョブ構成クラスの作成</span></h2>


<p>バッチのジョブを構成するBatchConfigurationクラスを作成します。</p>



<pre class="wp-block-code java"><code>package com.example.batchprocessing;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.item.database.JdbcBatchItemWriter;
import org.springframework.batch.item.database.builder.JdbcBatchItemWriterBuilder;
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;

@Configuration
public class BatchConfiguration {

    @Bean
    public Job job(JobRepository jobRepository, Step step1,
                   JobCompletionNotificationListener listener) {
        return new JobBuilder("taskletJob", jobRepository)
                .listener(listener)
                .start(step1)
                .build();
    }

    @Bean
    public Step step1(JobRepository jobRepository,
                      PlatformTransactionManager transactionManager, PersonTasklet personTasklet) {
        return new StepBuilder("personTasklet", jobRepository)
                .tasklet(personTasklet, transactionManager)
                .build();
    }

    @Bean
    public FlatFileItemReader&lt;Person&gt; reader() {
        return new FlatFileItemReaderBuilder&lt;Person&gt;()
                .name("personItemReader")
                .resource(new ClassPathResource("sample-data.csv"))
                .delimited()
                .names("firstName", "lastName")
                .targetType(Person.class)
                .build();
    }

    @Bean
    public JdbcBatchItemWriter&lt;Person&gt; writer(DataSource dataSource) {
        return new JdbcBatchItemWriterBuilder&lt;Person&gt;()
                .sql("INSERT INTO people (first_name, last_name) VALUES (:firstName, :lastName)")
                .dataSource(dataSource)
                .beanMapped()
                .build();
    }
}</code></pre>



<p>jobメソッドでジョブを構成してます。</p>



<p>startでstep1を指定することで最初にstep1を実行するように指定してます。</p>



<p>またlistenerにJobCompletionNotificationListenerを登録することでジョブ完了後に指定した処理が行えるようにしてます。</p>



<p>step1メソッドではPersonTaskletをtaskletとして実行するように登録を行ってます。</p>



<p>readerメソッドとwriterメソッドはPersonTaskletで使用される処理になります。</p>



<p>readerメソッドはsample-data.csv読み込んでPersonクラスに値を設定します。</p>



<p>writerメソッドはpeopleテーブルのfirst_nameとlast_nameをインサートする処理になります。</p>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc8">ジョブ完了クラスの作成</span></h2>


<p>ジョブ完了後に実行するJobCompletionNotificationListenerを作成します。</p>



<pre class="wp-block-code"><code>package com.example.batchprocessing;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobExecutionListener;
import org.springframework.jdbc.core.DataClassRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

@Component
public class JobCompletionNotificationListener implements JobExecutionListener {

    private static final Logger log = LoggerFactory.getLogger(JobCompletionNotificationListener.class);

    private final JdbcTemplate jdbcTemplate;

    public JobCompletionNotificationListener(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @Override
    public void afterJob(JobExecution jobExecution) {
        if (jobExecution.getStatus() == BatchStatus.COMPLETED) {
            log.info("!!! JOB FINISHED! Time to verify the results");

            jdbcTemplate
                    .query("SELECT first_name, last_name FROM people", new DataClassRowMapper&lt;&gt;(Person.class))
                    .forEach(person -&gt; log.info("Found &lt;{{}}&gt; in the database.", person));
        }
    }
}</code></pre>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc9">SpringApplication実行クラスの作成</span></h2>


<p>最後にSpringBatchを実行するためのSpringBatchSampleApplicationを作成します。</p>



<pre class="wp-block-code"><code>package com.example.batchprocessing;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringBatchSampleApplication {

    public static void main(String&#91;] args) {
        SpringApplication.run(SpringBatchSampleApplication.class, args);
    }

}</code></pre>



<p>実行すると以下の赤枠の通り、名字と名前がUpperCaseに変換されてます。</p>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="464" src="https://gogorelaxlifeblog.com/wp-content/uploads/2024/02/springbatchtasklet-1-1024x464.jpg" alt="" class="wp-image-1476" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2024/02/springbatchtasklet-1-1024x464.jpg 1024w, https://gogorelaxlifeblog.com/wp-content/uploads/2024/02/springbatchtasklet-1-300x136.jpg 300w, https://gogorelaxlifeblog.com/wp-content/uploads/2024/02/springbatchtasklet-1-768x348.jpg 768w, https://gogorelaxlifeblog.com/wp-content/uploads/2024/02/springbatchtasklet-1.jpg 780w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p></p>
]]></content:encoded>
					
					<wfw:commentRss>https://gogorelaxlifeblog.com/springbatch5tasklet/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Playwrightでブラウザ操作からコード自動生成【RPA自動化】</title>
		<link>https://gogorelaxlifeblog.com/playwright_auto_coding/</link>
					<comments>https://gogorelaxlifeblog.com/playwright_auto_coding/#respond</comments>
		
		<dc:creator><![CDATA[ごゆるり一族]]></dc:creator>
		<pubDate>Mon, 10 Jul 2023 14:23:22 +0000</pubDate>
				<category><![CDATA[技術]]></category>
		<category><![CDATA[Playwright]]></category>
		<guid isPermaLink="false">https://gogorelaxlifeblog.com/?p=1134</guid>

					<description><![CDATA[Playwrightを使ってブラウザ操作からJavaとJavaScriptのコードを自動生成してみました。 この記事では手順を説明させていただきます。 Playwrightとは PlaywrightとはWebブラウザを自 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>Playwrightを使ってブラウザ操作からJavaとJavaScriptのコードを自動生成してみました。</p>



<p>この記事では手順を説明させていただきます。</p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc1">Playwrightとは</span></h2>
<p>PlaywrightとはWebブラウザを自動操作するためのライブラリです。</p>
<p>自動操作ができるため、E2Eテストの効率化に用いられます。</p>
<p>Microsoftが開発してますが、EdgeだけでなくChrome、Firefox、Safariでも使用できます。</p>


<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc2">開発環境</span></h2>
<p>後述の操作を行った時の環境は以下の通りとなります。</p>


<figure class="wp-block-flexible-table-block-table"><table class=""><thead><tr><th>環境</th><th>バージョン</th></tr></thead><tbody><tr><td>OS</td><td>Windows 10 64bit</td></tr><tr><td>VSCode</td><td>1.80.0</td></tr><tr><td>VSCodeの拡張機能</td><td>Extension Pack for Java 20.250.022インストール済</td></tr><tr><td>Amazon Corretto</td><td>17.0.6.10</td></tr><tr><td>Git Bash</td><td>2.39.2.windows.1</td></tr><tr><td>npm</td><td>9.6.7</td></tr><tr><td>node.js</td><td>20.3.0</td></tr></tbody></table></figure>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc3">mavenを用意</span></h2>
<p>Javaのコードを自動生成するためにはmavenが必要になるので用意します。</p>
<p>以下のページから「apache-maven-X.X.X-bin.zip」をダウンロードします（私がダウンロードしたのはバージョン3.9.3でした）。</p>

<a rel="noopener" href="https://maven.apache.org/download.cgi" title="Download Apache Maven &#8211; Maven" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img decoding="async" src="https://s.wordpress.com/mshots/v1/https%3A%2F%2Fmaven.apache.org%2Fdownload.cgi?w=160&#038;h=90" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">Download Apache Maven &#8211; Maven</div><div class="blogcard-snippet external-blogcard-snippet"></div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img decoding="async" src="https://www.google.com/s2/favicons?domain=https://maven.apache.org/download.cgi" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">maven.apache.org</div></div></div></div></a>
<p>ダウンロードしたmavenを適当なフォルダに解凍します。</p>
<p>私は以下のように「C:\sample」に解凍しました。</p>
<p><img decoding="async" class="alignnone wp-image-1157 size-full" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/maven.png" alt="" width="638" height="155" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/maven.png 638w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/maven-300x73.png 300w" sizes="(max-width: 638px) 100vw, 638px" /></p>
<p>続いてVSCodeのコンソールからmavenのコマンドが実行できるように環境変数を追加します。</p>
<p>以下のように「Path」にmavenのbinフォルダを追加します。</p>
<p><img decoding="async" class="alignnone wp-image-1164 size-full" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/environment_variable.png" alt="" width="524" height="497" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/environment_variable.png 524w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/environment_variable-300x285.png 300w" sizes="(max-width: 524px) 100vw, 524px" /></p>


<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc4">mavenプロジェクト作成</span></h2>
<p>vscodeを起動してコマンドパレットから「Java:Create Java Project」を選択します。</p>
<p><img decoding="async" class="alignnone wp-image-1167 size-full" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/create_java_project.png" alt="" width="620" height="96" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/create_java_project.png 620w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/create_java_project-300x46.png 300w" sizes="(max-width: 620px) 100vw, 620px" /></p>
<p>「Maven create from archetype」を選択します。</p>
<p><img decoding="async" class="alignnone wp-image-1171 size-full" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/maven_create_from_archetype-e1688912772627.png" alt="" width="601" height="379" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/maven_create_from_archetype-e1688912772627.png 601w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/maven_create_from_archetype-e1688912772627-300x189.png 300w" sizes="(max-width: 601px) 100vw, 601px" /></p>
<p>「maven-archetype-quickstart」を選択します。</p>
<p><img decoding="async" class="alignnone wp-image-1174 size-full" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/maven_archetype_quickstart.png" alt="" width="606" height="374" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/maven_archetype_quickstart.png 606w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/maven_archetype_quickstart-300x185.png 300w" sizes="(max-width: 606px) 100vw, 606px" /></p>
<p>mavenのバージョンを選択します。</p>
<p>私は「1.4」を選択しました。</p>
<p><img decoding="async" class="alignnone wp-image-1177 size-full" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/maven_1.4.png" alt="" width="608" height="285" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/maven_1.4.png 608w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/maven_1.4-300x141.png 300w" sizes="(max-width: 608px) 100vw, 608px" /></p>
<p>パッケージ名を入力します。任意の名称で大丈夫です。</p>
<p>ここでは「com.example」とします。</p>
<p><img decoding="async" class="alignnone wp-image-1179 size-full" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/maven_package_name.png" alt="" width="600" height="127" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/maven_package_name.png 600w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/maven_package_name-300x64.png 300w" sizes="(max-width: 600px) 100vw, 600px" /></p>
<p>プロジェクト名を入力します。任意の名称で大丈夫です。</p>
<p>ここでは「demo」とします。</p>
<p><img decoding="async" class="alignnone wp-image-1181 size-full" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/maven_package_name_demo.png" alt="" width="604" height="135" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/maven_package_name_demo.png 604w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/maven_package_name_demo-300x67.png 300w" sizes="(max-width: 604px) 100vw, 604px" /></p>
<p>次に現在作成中のmavenプロジェクトの保存先を選択します。</p>
<p>ここでは「D:\Project\playwright_sample」とします。</p>
<p>続いてvscodeのターミナル上でコマンドが実行されるので入力を行っていきます。</p>
<p>ターミナル上で入力が必要な箇所のみ抜粋して以下に記載します。</p>


<pre class="wp-block-code plaintext"><code>Define value for property 'version' 1.0-SNAPSHOT: :　<span class="red"><span class="green">← 何も入力せずEnterキー押下。</span></span>
 Y: :　<span class="red"><span class="green">← 何も入力せずEnterキー押下。</span></span></code></pre>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc5">コード自動生成</span></h2>


<p>作成したmavenプロジェクトのフォルダをvscodeで開きます。</p>



<p>pom.xmlのdependenciesタグ内に以下の緑字を追加します。</p>



<p>今回はplaywrightの1.35.1をインストールしてます。</p>



<pre class="wp-block-code plaintext"><code>  &lt;dependencies>
    …
<span class="green">    &lt;dependency>
      &lt;groupId>com.microsoft.playwright&lt;/groupId>
      &lt;artifactId>playwright&lt;/artifactId>
      &lt;version>1.35.1&lt;/version>
    &lt;/dependency></span>
  &lt;/dependencies></code></pre>



<p>vscodeのターミナル上でpom.xmlがある場所に移動します。</p>



<p>その後に以下のコマンドを実行します。</p>



<p>今回は例としてamazonでの操作内容を自動生成するために「www.amazon.co.jp」を指定してます。</p>



<pre class="wp-block-code plaintext"><code>mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="codegen www.amazon.co.jp"</code></pre>


<p>以下のようにブラウザが立ち上がります。</p>
<p>右側に表示されてるPlaywright Inspectorに自動生成されたコードが出力されます。</p>
<p><img decoding="async" class="alignnone wp-image-1218 size-full" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/playwright_start.jpg" alt="" width="1762" height="609" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/playwright_start.jpg 780w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/playwright_start-300x104.jpg 300w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/playwright_start-1024x354.jpg 1024w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/playwright_start-768x265.jpg 768w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/playwright_start-1536x531.jpg 1536w" sizes="(max-width: 1762px) 100vw, 1762px" /></p>

<p>実際にコードの自動生成を行った様子は以下の通りです。</p>
<p>Amazonで「アーマードコア6」を検索してみました😁</p>
<p>Playwright Inspectorに自動生成されたコードが出力されてます。</p>
<p><img decoding="async" class="alignnone wp-image-1226 size-full" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/playwright_auto_coding.gif" alt="" width="1869" height="592" /></p>


<p>自動生成されたコードは以下のものとなります。</p>



<pre class="wp-block-code java"><code>import com.microsoft.playwright.*;
import com.microsoft.playwright.options.*;
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
import java.util.*;

public class Example {
  public static void main(String&#91;] args) {
    try (Playwright playwright = Playwright.create()) {
      Browser browser = playwright.chromium().launch(new BrowserType.LaunchOptions()
        .setHeadless(false));
      BrowserContext context = browser.newContext();
      Page page = context.newPage();
      page.navigate("https://www.amazon.co.jp/");
      page.getByPlaceholder("検索 Amazon.co.jp").click();
      page.getByPlaceholder("検索 Amazon.co.jp").fill("アーマードコア６");
      page.getByPlaceholder("検索 Amazon.co.jp").press("Enter");
      Page page1 = page.waitForPopup(() -> {
        page.getByRole(AriaRole.LINK, new Page.GetByRoleOptions().setName("【PS5】ARMORED CORE Ⅵ FIRES OF RUBICON 【数量限定特典】MELANDER C3 G13 特別仕様「TENDERFOOT」 同梱")).first().click();
      });
      page1.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("今すぐご予約ください")).click();
    }
  }
}</code></pre>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc6">JavaScriptのコード自動生成</span></h2>
<p>JavaScriptの場合はmavenなど何も必要がなく、以下のコマンドを実行するだけです。</p>


<pre class="wp-block-code plaintext"><code>npx playwright codegen www.amazon.co.jp</code></pre>



<p></p>
]]></content:encoded>
					
					<wfw:commentRss>https://gogorelaxlifeblog.com/playwright_auto_coding/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>switchのコントローラーが故障したので自分で修理【スイッチリモコン不具合】</title>
		<link>https://gogorelaxlifeblog.com/joyconrepair/</link>
					<comments>https://gogorelaxlifeblog.com/joyconrepair/#respond</comments>
		
		<dc:creator><![CDATA[ごゆるり一族]]></dc:creator>
		<pubDate>Mon, 03 Jul 2023 13:22:49 +0000</pubDate>
				<category><![CDATA[買ってよかったもの]]></category>
		<guid isPermaLink="false">https://gogorelaxlifeblog.com/?p=997</guid>

					<description><![CDATA[switchのコントローラーが壊れ、マリオが勝手に左に移動するようになりました😲 修理キットを購入して修理したら約1,000円で治せました（任天堂に修理依頼出すと4,400円と高いし時間かかる・・・） この記事ではどのよ [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>switchのコントローラーが壊れ、マリオが勝手に左に移動するようになりました😲</p>



<p>修理キットを購入して修理したら約1,000円で治せました（任天堂に修理依頼出すと4,400円と高いし時間かかる・・・）</p>



<p>この記事ではどのようにして修理したかを紹介しますね😁</p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc1">修理キットの購入</span></h2>
<p>自分で修理するのにドライバーとかを用意するとサイズが合わず再度購入・・・</p>
<p>といったことが目に見えてたので、楽天市場でセット商品を購入しました。</p>
<p>修理するのに必要な道具が全部付いてます👍</p>
<p>楽天の買い回りセールとSPUを使って購入したので、当時は定価1,340円のところポイントが300ポイント程度ついて約1,000円で購入できました。</p>
<p> </p>
<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc2">修理方法</span></h2>
<p>修理キットを使った修理方法を説明しますね😀</p>


<div class="wp-block-media-text is-stacked-on-mobile is-vertically-aligned-center"><figure class="wp-block-media-text__media"><img decoding="async" width="1024" height="768" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/001-1024x768.jpg" alt="" class="wp-image-1032 size-full" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/001-1024x768.jpg 1024w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/001-300x225.jpg 300w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/001-768x576.jpg 768w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/001-1536x1152.jpg 1536w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/001-2048x1536.jpg 2048w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/001-scaled.jpg 780w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure><div class="wp-block-media-text__content">
<p>ドライバが３本付属してますが、一番小さいドライバのみ使用しました。</p>
</div></div>



<div class="wp-block-media-text is-stacked-on-mobile"><figure class="wp-block-media-text__media"><img decoding="async" width="1024" height="768" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/002-scaled-e1688296534147-1024x768.jpg" alt="" class="wp-image-1083 size-full" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/002-scaled-e1688296534147-1024x768.jpg 1024w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/002-scaled-e1688296534147-300x225.jpg 300w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/002-scaled-e1688296534147-768x576.jpg 768w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/002-scaled-e1688296534147-1536x1152.jpg 1536w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/002-scaled-e1688296534147-2048x1536.jpg 2048w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/002-scaled-e1688296534147.jpg 780w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure><div class="wp-block-media-text__content">
<p>ドライバで赤丸4箇所のネジを外します。</p>



<p>ネジは小さいのでティッシュの上などに置いて無くさないようにすると良いです。</p>
</div></div>



<div class="wp-block-media-text is-stacked-on-mobile"><figure class="wp-block-media-text__media"><img decoding="async" width="1024" height="768" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/003-1024x768.jpg" alt="" class="wp-image-1035 size-full" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/003-1024x768.jpg 1024w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/003-300x225.jpg 300w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/003-768x576.jpg 768w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/003-1536x1152.jpg 1536w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/003-2048x1536.jpg 2048w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/003-scaled.jpg 780w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure><div class="wp-block-media-text__content">
<p>手でコントローラーのフタを少し開けます。</p>



<p>あまり空けすぎると折れてしまうので力を入れすぎないように。</p>
</div></div>



<div class="wp-block-media-text is-stacked-on-mobile"><figure class="wp-block-media-text__media"><img decoding="async" width="1024" height="768" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/004-1024x768.jpg" alt="" class="wp-image-1036 size-full" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/004-1024x768.jpg 1024w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/004-300x225.jpg 300w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/004-768x576.jpg 768w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/004-1536x1152.jpg 1536w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/004-2048x1536.jpg 2048w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/004-scaled.jpg 780w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure><div class="wp-block-media-text__content">
<p>隙間に修理キットに付属してるヘラを差し込みます。</p>
</div></div>



<div class="wp-block-media-text is-stacked-on-mobile"><figure class="wp-block-media-text__media"><img decoding="async" width="1024" height="768" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/005-1024x768.jpg" alt="" class="wp-image-1037 size-full" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/005-1024x768.jpg 1024w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/005-300x225.jpg 300w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/005-768x576.jpg 768w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/005-1536x1152.jpg 1536w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/005-2048x1536.jpg 2048w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/005-scaled.jpg 780w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure><div class="wp-block-media-text__content">
<p>ヘラを差し込んだ時の状態を別角度から写してます。</p>
</div></div>



<div class="wp-block-media-text is-stacked-on-mobile"><figure class="wp-block-media-text__media"><img decoding="async" width="1024" height="768" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/006-1024x768.jpg" alt="" class="wp-image-1038 size-full" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/006-1024x768.jpg 1024w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/006-300x225.jpg 300w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/006-768x576.jpg 768w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/006-1536x1152.jpg 1536w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/006-2048x1536.jpg 2048w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/006-scaled.jpg 780w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure><div class="wp-block-media-text__content">
<p>蓋を開けたらリチウム電池を外します。</p>



<p>リチウム電池の隙間にヘラを差し込みます。</p>
</div></div>



<div class="wp-block-media-text is-stacked-on-mobile"><figure class="wp-block-media-text__media"><img decoding="async" width="1024" height="768" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/007-1024x768.jpg" alt="" class="wp-image-1039 size-full" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/007-1024x768.jpg 1024w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/007-300x225.jpg 300w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/007-768x576.jpg 768w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/007-1536x1152.jpg 1536w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/007-2048x1536.jpg 2048w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/007-scaled.jpg 780w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure><div class="wp-block-media-text__content">
<p>ヘラを差し込んだ時の状態を別角度から写してます。</p>
</div></div>



<div class="wp-block-media-text is-stacked-on-mobile"><figure class="wp-block-media-text__media"><img decoding="async" width="1024" height="768" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/0072-scaled-e1688296809857-1024x768.jpg" alt="" class="wp-image-1084 size-full" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/0072-scaled-e1688296809857-1024x768.jpg 1024w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/0072-scaled-e1688296809857-300x225.jpg 300w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/0072-scaled-e1688296809857-768x576.jpg 768w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/0072-scaled-e1688296809857-1536x1152.jpg 1536w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/0072-scaled-e1688296809857-2048x1536.jpg 2048w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/0072-scaled-e1688296809857.jpg 780w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure><div class="wp-block-media-text__content">
<p>ドライバで赤丸5箇所のネジを外します。</p>
</div></div>



<div class="wp-block-media-text is-stacked-on-mobile"><figure class="wp-block-media-text__media"><img decoding="async" width="1024" height="768" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/008-scaled-e1688297844697-1024x768.jpg" alt="" class="wp-image-1090 size-full" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/008-scaled-e1688297844697-1024x768.jpg 1024w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/008-scaled-e1688297844697-300x225.jpg 300w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/008-scaled-e1688297844697-768x576.jpg 768w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/008-scaled-e1688297844697-1536x1152.jpg 1536w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/008-scaled-e1688297844697-2048x1536.jpg 2048w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/008-scaled-e1688297844697.jpg 780w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure><div class="wp-block-media-text__content">
<p>次の画像状態にするために矢印方向（逆時計回り）にパーツを回します。</p>
</div></div>



<div class="wp-block-media-text is-stacked-on-mobile"><figure class="wp-block-media-text__media"><img decoding="async" width="1024" height="768" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/010-1024x768.jpg" alt="" class="wp-image-1042 size-full" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/010-1024x768.jpg 1024w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/010-300x225.jpg 300w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/010-768x576.jpg 768w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/010-1536x1152.jpg 1536w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/010-2048x1536.jpg 2048w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/010-scaled.jpg 780w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure><div class="wp-block-media-text__content">
<p>パーツを回したことで動線がつながった状態で基盤が見えました。</p>
</div></div>



<div class="wp-block-media-text is-stacked-on-mobile"><figure class="wp-block-media-text__media"><img decoding="async" width="1024" height="768" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/011-1024x768.jpg" alt="" class="wp-image-1043 size-full" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/011-1024x768.jpg 1024w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/011-300x225.jpg 300w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/011-768x576.jpg 768w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/011-1536x1152.jpg 1536w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/011-2048x1536.jpg 2048w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/011-scaled.jpg 780w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure><div class="wp-block-media-text__content">
<p>次にピンセットを使います。</p>
</div></div>



<div class="wp-block-media-text is-stacked-on-mobile"><figure class="wp-block-media-text__media"><img decoding="async" width="1024" height="576" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/013-1-1024x576.jpg" alt="" class="wp-image-1096 size-full" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/013-1-1024x576.jpg 1024w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/013-1-300x169.jpg 300w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/013-1-768x432.jpg 768w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/013-1-1536x864.jpg 1536w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/013-1-2048x1152.jpg 2048w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/013-1-120x68.jpg 120w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/013-1-160x90.jpg 160w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/013-1-320x180.jpg 320w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/013-1-scaled.jpg 780w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure><div class="wp-block-media-text__content">
<p>赤丸の灰色部分をピンセットでつまみ、次の画像のように右側に倒します。</p>
</div></div>



<div class="wp-block-media-text is-stacked-on-mobile"><figure class="wp-block-media-text__media"><img decoding="async" width="1024" height="576" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/014-1024x576.jpg" alt="" class="wp-image-1046 size-full" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/014-1024x576.jpg 1024w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/014-300x169.jpg 300w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/014-768x432.jpg 768w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/014-1536x864.jpg 1536w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/014-2048x1152.jpg 2048w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/014-120x68.jpg 120w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/014-160x90.jpg 160w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/014-320x180.jpg 320w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/014-scaled.jpg 780w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure><div class="wp-block-media-text__content">
<p>灰色部分を右側に倒した状態です。</p>
</div></div>



<div class="wp-block-media-text is-stacked-on-mobile"><figure class="wp-block-media-text__media"><img decoding="async" width="1024" height="576" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/015-1024x576.jpg" alt="" class="wp-image-1047 size-full" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/015-1024x576.jpg 1024w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/015-300x169.jpg 300w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/015-768x432.jpg 768w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/015-1536x864.jpg 1536w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/015-2048x1152.jpg 2048w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/015-120x68.jpg 120w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/015-160x90.jpg 160w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/015-320x180.jpg 320w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/015-scaled.jpg 780w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure><div class="wp-block-media-text__content">
<p>ピンセットで銅線をつまんで引き抜きます。</p>
</div></div>



<div class="wp-block-media-text is-stacked-on-mobile"><figure class="wp-block-media-text__media"><img decoding="async" width="1024" height="768" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/018-1-1024x768.jpg" alt="" class="wp-image-1099 size-full" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/018-1-1024x768.jpg 1024w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/018-1-300x225.jpg 300w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/018-1-768x576.jpg 768w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/018-1-1536x1152.jpg 1536w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/018-1-2048x1536.jpg 2048w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/018-1-scaled.jpg 780w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure><div class="wp-block-media-text__content">
<p>今度は赤丸の黒色部分をピンセットでつまみ、左側に倒します。</p>
</div></div>



<div class="wp-block-media-text is-stacked-on-mobile"><figure class="wp-block-media-text__media"><img decoding="async" width="1024" height="768" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/020-1024x768.jpg" alt="" class="wp-image-1052 size-full" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/020-1024x768.jpg 1024w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/020-300x225.jpg 300w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/020-768x576.jpg 768w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/020-1536x1152.jpg 1536w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/020-2048x1536.jpg 2048w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/020-scaled.jpg 780w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure><div class="wp-block-media-text__content">
<p>ピンセットで銅線をつまんで引き抜きます。</p>
</div></div>



<div class="wp-block-media-text is-stacked-on-mobile"><figure class="wp-block-media-text__media"><img decoding="async" width="1024" height="768" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/021-scaled-e1688387962488-1024x768.jpg" alt="" class="wp-image-1102 size-full" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/021-scaled-e1688387962488-1024x768.jpg 1024w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/021-scaled-e1688387962488-300x225.jpg 300w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/021-scaled-e1688387962488-768x576.jpg 768w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/021-scaled-e1688387962488-1536x1152.jpg 1536w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/021-scaled-e1688387962488-2048x1536.jpg 2048w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/021-scaled-e1688387962488.jpg 780w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure><div class="wp-block-media-text__content">
<p>赤丸2箇所のネジを外します。</p>



<p>片方だけ一気に外すと上手く外れないため、交互に2つのネジを回して徐々に外していきます。</p>



<p>なお、左下のネジは銅線を持ち上げながら外してください。</p>
</div></div>



<div class="wp-block-media-text is-stacked-on-mobile"><figure class="wp-block-media-text__media"><img decoding="async" width="1024" height="768" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/0222-1024x768.jpg" alt="" class="wp-image-1105 size-full" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/0222-1024x768.jpg 1024w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/0222-300x225.jpg 300w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/0222-768x576.jpg 768w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/0222-1536x1152.jpg 1536w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/0222-2048x1536.jpg 2048w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/0222-scaled.jpg 780w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure><div class="wp-block-media-text__content">
<p>赤丸がスティック部分になります。これを素手で取り外します。</p>
</div></div>



<div class="wp-block-media-text is-stacked-on-mobile"><figure class="wp-block-media-text__media"><img decoding="async" width="1024" height="768" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/022-1024x768.jpg" alt="" class="wp-image-1056 size-full" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/022-1024x768.jpg 1024w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/022-300x225.jpg 300w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/022-768x576.jpg 768w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/022-1536x1152.jpg 1536w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/022-2048x1536.jpg 2048w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/022-scaled.jpg 780w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure><div class="wp-block-media-text__content">
<p>修理キットに付属している新しいスティックをはめ込みます。</p>
</div></div>



<div class="wp-block-media-text is-stacked-on-mobile"><figure class="wp-block-media-text__media"><img decoding="async" width="1024" height="768" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/023-scaled-e1688388702442-1024x768.jpg" alt="" class="wp-image-1109 size-full" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/023-scaled-e1688388702442-1024x768.jpg 1024w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/023-scaled-e1688388702442-300x225.jpg 300w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/023-scaled-e1688388702442-768x576.jpg 768w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/023-scaled-e1688388702442-1536x1152.jpg 1536w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/023-scaled-e1688388702442-2048x1536.jpg 2048w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/023-scaled-e1688388702442.jpg 780w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure><div class="wp-block-media-text__content">
<p>今度は先程までと逆の手順を踏んでコントローラーを組み立てます。</p>



<p>赤丸箇所のネジをはめます。</p>
</div></div>



<div class="wp-block-media-text is-stacked-on-mobile"><figure class="wp-block-media-text__media"><img decoding="async" width="1024" height="768" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/024-1-1024x768.jpg" alt="" class="wp-image-1110 size-full" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/024-1-1024x768.jpg 1024w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/024-1-300x225.jpg 300w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/024-1-768x576.jpg 768w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/024-1-1536x1152.jpg 1536w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/024-1-2048x1536.jpg 2048w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/024-1-scaled.jpg 780w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure><div class="wp-block-media-text__content">
<p>ピンセットで銅線をつまんで元の位置に差し込みます。</p>
</div></div>



<div class="wp-block-media-text is-stacked-on-mobile"><figure class="wp-block-media-text__media"><img decoding="async" width="1024" height="768" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/018-1-1024x768.jpg" alt="" class="wp-image-1099 size-full" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/018-1-1024x768.jpg 1024w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/018-1-300x225.jpg 300w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/018-1-768x576.jpg 768w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/018-1-1536x1152.jpg 1536w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/018-1-2048x1536.jpg 2048w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/018-1-scaled.jpg 780w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure><div class="wp-block-media-text__content">
<p>赤丸の黒色部分をピンセットでつまみ、右側に倒してロックします。</p>
</div></div>



<div class="wp-block-media-text is-stacked-on-mobile"><figure class="wp-block-media-text__media"><img decoding="async" width="1024" height="576" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/027-1024x576.jpg" alt="" class="wp-image-1059 size-full" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/027-1024x576.jpg 1024w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/027-300x169.jpg 300w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/027-768x432.jpg 768w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/027-1536x864.jpg 1536w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/027-2048x1152.jpg 2048w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/027-120x68.jpg 120w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/027-160x90.jpg 160w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/027-320x180.jpg 320w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/027-scaled.jpg 780w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure><div class="wp-block-media-text__content">
<p>こちらの銅線もつまんで元の位置に差し込みます。</p>
</div></div>



<div class="wp-block-media-text is-stacked-on-mobile"><figure class="wp-block-media-text__media"><img decoding="async" width="1024" height="576" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/013-1-1024x576.jpg" alt="" class="wp-image-1096 size-full" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/013-1-1024x576.jpg 1024w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/013-1-300x169.jpg 300w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/013-1-768x432.jpg 768w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/013-1-1536x864.jpg 1536w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/013-1-2048x1152.jpg 2048w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/013-1-120x68.jpg 120w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/013-1-160x90.jpg 160w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/013-1-320x180.jpg 320w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/013-1-scaled.jpg 780w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure><div class="wp-block-media-text__content">
<p>赤丸の灰色部分をピンセットでつまみ、左側に倒してロックします。</p>
</div></div>



<div class="wp-block-media-text is-stacked-on-mobile"><figure class="wp-block-media-text__media"><img decoding="async" width="1024" height="768" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/028-1024x768.jpg" alt="" class="wp-image-1060 size-full" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/028-1024x768.jpg 1024w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/028-300x225.jpg 300w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/028-768x576.jpg 768w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/028-1536x1152.jpg 1536w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/028-2048x1536.jpg 2048w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/028-scaled.jpg 780w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure><div class="wp-block-media-text__content">
<p>もし画像のようにL（R）ボタンが外れてしまった時についても説明します。</p>
</div></div>



<div class="wp-block-media-text is-stacked-on-mobile"><figure class="wp-block-media-text__media"><img decoding="async" width="1024" height="768" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/029-scaled-e1688389316420-1024x768.jpg" alt="" class="wp-image-1113 size-full" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/029-scaled-e1688389316420-1024x768.jpg 1024w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/029-scaled-e1688389316420-300x225.jpg 300w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/029-scaled-e1688389316420-768x576.jpg 768w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/029-scaled-e1688389316420-1536x1152.jpg 1536w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/029-scaled-e1688389316420-2048x1536.jpg 2048w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/029-scaled-e1688389316420.jpg 780w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure><div class="wp-block-media-text__content">
<p>赤丸箇所にバネをはめ込みます。</p>



<p>また、青丸箇所のようにL（R）ボタンの突起箇所をコントローラー本体部分に差し込んでロックするようにします。</p>
</div></div>



<div class="wp-block-media-text is-stacked-on-mobile"><figure class="wp-block-media-text__media"><img decoding="async" width="1024" height="768" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/031-scaled-e1688389647720-1024x768.jpg" alt="" class="wp-image-1117 size-full" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/031-scaled-e1688389647720-1024x768.jpg 1024w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/031-scaled-e1688389647720-300x225.jpg 300w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/031-scaled-e1688389647720-768x576.jpg 768w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/031-scaled-e1688389647720-1536x1152.jpg 1536w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/031-scaled-e1688389647720-2048x1536.jpg 2048w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/031-scaled-e1688389647720.jpg 780w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure><div class="wp-block-media-text__content">
<p>矢印方向（時計回り）にパーツを回します。</p>
</div></div>



<div class="wp-block-media-text is-stacked-on-mobile"><figure class="wp-block-media-text__media"><img decoding="async" width="1024" height="768" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/032-scaled-e1688389796614-1024x768.jpg" alt="" class="wp-image-1120 size-full" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/032-scaled-e1688389796614-1024x768.jpg 1024w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/032-scaled-e1688389796614-300x225.jpg 300w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/032-scaled-e1688389796614-768x576.jpg 768w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/032-scaled-e1688389796614-1536x1152.jpg 1536w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/032-scaled-e1688389796614-2048x1536.jpg 2048w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/032-scaled-e1688389796614.jpg 780w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure><div class="wp-block-media-text__content">
<p>パーツを元の位置に戻したら、赤丸5箇所のネジをはめます。</p>
</div></div>



<div class="wp-block-media-text is-stacked-on-mobile"><figure class="wp-block-media-text__media"><img decoding="async" width="1024" height="768" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/036-1024x768.jpg" alt="" class="wp-image-1068 size-full" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/036-1024x768.jpg 1024w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/036-300x225.jpg 300w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/036-768x576.jpg 768w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/036-1536x1152.jpg 1536w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/036-2048x1536.jpg 2048w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/036-scaled.jpg 780w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure><div class="wp-block-media-text__content">
<p>リチウム電池を元の位置に戻します。</p>
</div></div>



<div class="wp-block-media-text is-stacked-on-mobile"><figure class="wp-block-media-text__media"><img decoding="async" width="1024" height="768" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/037-scaled-e1688390139993-1024x768.jpg" alt="" class="wp-image-1124 size-full" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/037-scaled-e1688390139993-1024x768.jpg 1024w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/037-scaled-e1688390139993-300x225.jpg 300w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/037-scaled-e1688390139993-768x576.jpg 768w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/037-scaled-e1688390139993-1536x1152.jpg 1536w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/037-scaled-e1688390139993-2048x1536.jpg 2048w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/07/037-scaled-e1688390139993.jpg 780w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure><div class="wp-block-media-text__content">
<p>赤丸4箇所のネジをはめたら終了です。</p>



<p>あとはSwitchで動作確認をしましょう。</p>
</div></div>



<p>なお、私は当時以下の動画を見ながら修理しましたので参考までに貼っておきますね😀</p>



<iframe loading="lazy" width="1280" height="720" src="https://www.youtube.com/embed/erDcczGgA4g" title="★ジョイコン(L/左)スティック安全な交換修理の方法を詳しく解説/Fix Joy-con Drift" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>



<p></p>
]]></content:encoded>
					
					<wfw:commentRss>https://gogorelaxlifeblog.com/joyconrepair/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>楽天銀行で子供の口座開設した【必要書類にマイナンバーカード不要】</title>
		<link>https://gogorelaxlifeblog.com/rakutenbankjunior/</link>
					<comments>https://gogorelaxlifeblog.com/rakutenbankjunior/#respond</comments>
		
		<dc:creator><![CDATA[ごゆるり一族]]></dc:creator>
		<pubDate>Fri, 23 Jun 2023 07:37:44 +0000</pubDate>
				<category><![CDATA[家計見直し]]></category>
		<category><![CDATA[楽天]]></category>
		<category><![CDATA[貯蓄]]></category>
		<guid isPermaLink="false">https://gogorelaxlifeblog.com/?p=867</guid>

					<description><![CDATA[ジュニアNISAを始めるために子供の楽天銀行口座を開設しました。 生まれたばかりでマイナンバーカードもなかったため、子供の保険証と医療証でスマホアプリから口座開設をしました。 この記事ではその時の手順を説明していきたいと [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>ジュニアNISAを始めるために子供の楽天銀行口座を開設しました。</p>



<p>生まれたばかりでマイナンバーカードもなかったため、子供の保険証と医療証でスマホアプリから口座開設をしました。</p>



<p>この記事ではその時の手順を説明していきたいと思います。</p>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc1">口座開設の申し込みページ表示</span></h2>
<p>まずはWebページから口座開設の申し込みを行う必要があります。</p>
<p>12歳以下の子供限定ですが、以下のページから申し込みし、開設した口座に入金をすると1,000円もらえるのでオススメです！</p>

<a rel="noopener" href="https://www.rakuten-bank.co.jp/account/campaign/kids/" title="https://www.rakuten-bank.co.jp/account/campaign/kids/" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img decoding="async" src="https://s.wordpress.com/mshots/v1/https%3A%2F%2Fwww.rakuten-bank.co.jp%2Faccount%2Fcampaign%2Fkids%2F?w=160&#038;h=90" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">https://www.rakuten-bank.co.jp/account/campaign/kids/</div><div class="blogcard-snippet external-blogcard-snippet"></div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img decoding="async" src="https://www.google.com/s2/favicons?domain=https://www.rakuten-bank.co.jp/account/campaign/kids/" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">www.rakuten-bank.co.jp</div></div></div></div></a>
<p>上記ページから申し込みボタンを押すと口座開設申込情報入力画面が表示されます。</p>
<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc2">口座開設申込情報で何を入力したか</span></h2>
<p>12歳以下の子供の口座を申し込みの場合は、「お客さま情報」と「その他お客さま情報」に子供の情報を入力します。</p>
<p>余談ですが我が家の息子は当時0歳児だったので無職を選択しました😀。</p>
<p>次にキャッシュカードの選択ですが、16歳未満の場合はデビットカードとクレジットカードは作れません。そのため楽天銀行キャッシュカード一択となります。</p>
<p>16歳以上の場合はデビットカード機能付きのものが作成できます。</p>
<p>18歳以上の場合はクレジットカード機能付きのものが作成できます。</p>
<p>しかし、デビットカードやクレジットカード機能付きの楽天銀行カードを保持した状態で、楽天カードや楽天プレミアムカードを作成したり、家族カードを作成したりすることができません。</p>
<p>自由度が低くなるため、16歳以上の場合でも楽天銀行キャッシュカードを作成することをオススメします。</p>
<p>以下のページに楽天銀行カード（楽天カード一体型）を保持した状態で楽天カードを2枚目のカードとして持てないことが書いてます。</p>

<a rel="noopener" href="https://help.rakuten-bank.net/faq/show/13967?site_domain=individual" title="https://help.rakuten-bank.net/faq/show/13967?site_domain=individual" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img decoding="async" src="https://s.wordpress.com/mshots/v1/https%3A%2F%2Fhelp.rakuten-bank.net%2Ffaq%2Fshow%2F13967%3Fsite_domain%3Dindividual?w=160&#038;h=90" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">https://help.rakuten-bank.net/faq/show/13967?site_domain=individual</div><div class="blogcard-snippet external-blogcard-snippet"></div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img decoding="async" src="https://www.google.com/s2/favicons?domain=https://help.rakuten-bank.net/faq/show/13967?site_domain=individual" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">help.rakuten-bank.net</div></div></div></div></a>
<p>続いて「本人確認書類と提出方法の選択」で提出書類を選択します。</p>
<p>「住民票写しの原本」を選択すると郵送のみ可能です。</p>
<p>子供の本人確認書類は以下のものから2点選択します。</p>
<ul>
<li>マイナンバーカード</li>
<li>パスポート</li>
<li>住民基本台帳カード</li>
<li>カード型健康保険証</li>
<li>各種福祉手帳</li>
</ul>
<p>申し込みページの更新が追いついてないのか、医療証の選択欄が存在せず「あれ？」と思いますが、医療証を本人確認書類として使用したい場合は「各種福祉手帳」を選択してもらえれば大丈夫です。</p>
<p>私は「カード型健康保険証」と「各種福祉手帳」を選択して「カード型健康保険証」と「医療証」の写真を提出しました。</p>
<p>なお、以下のページ上でも医療証は本人確認書類として使えることが書かれてます。</p>

<a rel="noopener" href="https://www.rakuten-bank.co.jp/account/id.html" title="口座開設 本人確認書類｜楽天銀行" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img decoding="async" src="https://www.rakuten-bank.co.jp/shared/images/contents-mod/ogp-img-01.gif" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">口座開設 本人確認書類｜楽天銀行</div><div class="blogcard-snippet external-blogcard-snippet">楽天銀行の口座開設お申込後、本人確認書類の提出は「楽天銀行アプリ」から行うのがおすすめ。本人確認書類を撮影して送信するだけ！郵送での提出より約5日も早く口座開設できるので、すぐに取引を始めたいかたはぜひご利用ください。</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img decoding="async" src="https://www.google.com/s2/favicons?domain=https://www.rakuten-bank.co.jp/account/id.html" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">www.rakuten-bank.co.jp</div></div></div></div></a>
<p>親権者の本人確認書類は以下のものから2点選択します。</p>
<ul>
<li>運転免許証/運転履歴証明書</li>
<li>マイナンバーカード</li>
<li>パスポート</li>
<li>住民基本台帳カード</li>
<li>カード型健康保険証</li>
<li>各種年金手帳/各種福祉手帳</li>
<li>公共料金の領収書/国税又は地方税の領収書等</li>
</ul>
<p>私は「<span style="text-align: -webkit-match-parent; font-family: var(--cocoon-default-font); font-size: var(--cocoon-default-text-size);">運転免許証/運転履歴証明書</span>」と「マイナンバーカード」を選択して「運転免許証」と「マイナンバーカード」の写真を提出しました。</p>
<p>書類提出方法の選択は「スマートフォンアプリで送付する」を選択しました。</p>
<p>「スマートフォンアプリで送付する」を選択した場合、楽天銀行アプリをインストールする必要があります。</p>

<a rel="noopener" href="https://www.rakuten-bank.co.jp/lp/app/" title="楽天銀行アプリで残高照会や振込がもっと簡単に｜楽天銀行" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img decoding="async" src="https://www.rakuten-bank.co.jp/shared/images/contents-mod/ogp-img-53.png" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">楽天銀行アプリで残高照会や振込がもっと簡単に｜楽天銀行</div><div class="blogcard-snippet external-blogcard-snippet">楽天銀行アプリは振込手続きが２４時間可能！スマートフォンアプリなら、楽天銀行をもっと便利に使えます。楽天銀行（旧イーバンク銀行）</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img decoding="async" src="https://www.google.com/s2/favicons?domain=https://www.rakuten-bank.co.jp/lp/app/" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">www.rakuten-bank.co.jp</div></div></div></div></a>
<p>なお、子供が12歳以下の場合は「スマートフォンアプリで送付する」か「郵送で提出する」のいずれかになります。</p>
<p>13歳以上の場合は、上記2つの提出方法に加え、「配達員へ提示する」方法があります。</p>
<p>入力を全て行い、申込完了まで進むと、楽天銀行から申込受付を知らせるメールが届きます。</p>
<p>そのメール内に登録番号とアクセスキーが書いてるのですが、楽天銀行アプリから必要書類を提出する際に使うので無くさないでくださいね💦</p>
<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc3">楽天銀行アプリから必要書類を提出</span></h2>
<p>スマホから楽天銀行アプリを起動します。</p>
<p>ログイン画面を表示し、画面中央の「本人確認書類などを送る」をタップします。</p>
<p><img decoding="async" class="alignnone wp-image-931 size-large" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/rakutenbank_Login-473x1024.png" alt="" width="473" height="1024" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/rakutenbank_Login-473x1024.png 473w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/rakutenbank_Login-139x300.png 139w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/rakutenbank_Login-768x1663.png 768w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/rakutenbank_Login-709x1536.png 709w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/rakutenbank_Login-946x2048.png 946w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/rakutenbank_Login.png 1125w" sizes="(max-width: 473px) 100vw, 473px" /></p>
<p>画面中央の「口座開設」をタップします。</p>
<p><img decoding="async" class="alignnone wp-image-932 size-large" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/rakutenbank_menu-473x1024.jpg" alt="" width="473" height="1024" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/rakutenbank_menu-473x1024.jpg 473w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/rakutenbank_menu-139x300.jpg 139w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/rakutenbank_menu-768x1663.jpg 768w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/rakutenbank_menu-709x1536.jpg 709w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/rakutenbank_menu-946x2048.jpg 946w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/rakutenbank_menu.jpg 780w" sizes="(max-width: 473px) 100vw, 473px" /></p>
<p>楽天銀行からのメールに記載がある登録番号とアクセスキー、子供の生年月日を入力します。</p>
<p><img decoding="async" class="alignnone wp-image-933 size-large" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/rakutenbank_inputform-473x1024.jpg" alt="" width="473" height="1024" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/rakutenbank_inputform-473x1024.jpg 473w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/rakutenbank_inputform-139x300.jpg 139w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/rakutenbank_inputform-768x1663.jpg 768w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/rakutenbank_inputform-709x1536.jpg 709w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/rakutenbank_inputform-946x2048.jpg 946w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/rakutenbank_inputform.jpg 780w" sizes="(max-width: 473px) 100vw, 473px" /></p>
<p>次に口座申込時に選択した本人確認書類を選択します。</p>
<p>ここでも申し込みページと同じく更新が追いついてないのか、医療証の選択欄が存在しません。</p>
<p>医療証を子供の本人確認書類として使用したい場合は「各種福祉手帳」を選択してもらえれば大丈夫です。</p>
<p>私は子供の本人確認書類として「カード型健康保険証」と「各種福祉手帳」を選択し、「カード型健康保険証」と「医療証」の写真を提出しました。</p>
<p>親権者の本人確認書類は「<span style="text-align: -webkit-match-parent; font-family: var(--cocoon-default-font); font-size: var(--cocoon-default-text-size);">運転免許証/運転履歴証明書</span>」と「マイナンバーカード」を選択し、「運転免許証」と「マイナンバーカード」の写真を提出しました。</p>
<p>なお、「カード型健康保険証」は裏面の住所に現住所が記載されてる必要があります。</p>
<p>その他注意事項は以下のページに記載してるのでご参照ください。</p>

<a rel="noopener" href="https://www.rakuten-bank.co.jp/account/id.html" title="口座開設 本人確認書類｜楽天銀行" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img decoding="async" src="https://www.rakuten-bank.co.jp/shared/images/contents-mod/ogp-img-01.gif" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">口座開設 本人確認書類｜楽天銀行</div><div class="blogcard-snippet external-blogcard-snippet">楽天銀行の口座開設お申込後、本人確認書類の提出は「楽天銀行アプリ」から行うのがおすすめ。本人確認書類を撮影して送信するだけ！郵送での提出より約5日も早く口座開設できるので、すぐに取引を始めたいかたはぜひご利用ください。</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img decoding="async" src="https://www.google.com/s2/favicons?domain=https://www.rakuten-bank.co.jp/account/id.html" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">www.rakuten-bank.co.jp</div></div></div></div></a>
<p>その後に書類の写真撮影を行い、提出することで必要書類の提出は完了となります。</p>
<p>申し込み内容と提出書類に問題なければ約1週間程度で口座情報が記載された書類とキャッシュカードが簡易書留で届きます👍</p>


<p></p>
]]></content:encoded>
					
					<wfw:commentRss>https://gogorelaxlifeblog.com/rakutenbankjunior/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>VSCodeでWebpackを使ってReactをデバッグする方法</title>
		<link>https://gogorelaxlifeblog.com/reactdebug/</link>
					<comments>https://gogorelaxlifeblog.com/reactdebug/#respond</comments>
		
		<dc:creator><![CDATA[ごゆるり一族]]></dc:creator>
		<pubDate>Wed, 21 Jun 2023 15:49:24 +0000</pubDate>
				<category><![CDATA[技術]]></category>
		<category><![CDATA[react]]></category>
		<guid isPermaLink="false">https://gogorelaxlifeblog.com/?p=766</guid>

					<description><![CDATA[最初は「バインドされていないブレークポイント」となりデバッグできなかったです・・・ しかし、Reactの勉強をするにあたりデバッグできると理解が早まるのでVSCodeでデバッグできるようにしました。 そのため、同じ環境を [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>最初は「バインドされていないブレークポイント」となりデバッグできなかったです・・・</p>
<p>しかし、Reactの勉強をするにあたりデバッグできると理解が早まるのでVSCodeでデバッグできるようにしました。</p>
<p>そのため、同じ環境を作ろうとしてる方の手助けになれば幸いです🙇</p>


<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc1">開発環境</span></h2>
<p>私が構築した時の開発環境は以下の通りとなります。</p>


<figure class="wp-block-flexible-table-block-table"><table class="has-fixed-layout"><thead><tr><th>環境</th><th>バージョン</th></tr></thead><tbody><tr><td>OS</td><td>Windows 10 64bit</td></tr><tr><td>VSCode</td><td>1.79.2</td></tr><tr><td>Git Bash</td><td>2.39.2.windows.1</td></tr><tr><td>npm</td><td>9.6.7</td></tr><tr><td>node.js</td><td>20.3.0</td></tr><tr><td>webpack</td><td>5.86.0</td></tr><tr><td>babel</td><td>7.22.5</td></tr><tr><td>React</td><td>18.2.0</td></tr></tbody></table></figure>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc2">ソース構成</span></h2>
<p>デバッグ環境を作った際のソース構成は以下の通りです。</p>


<ul class="wp-block-list is-style-tree">
<li>reactsample
<ul class="wp-block-list">
<li>.vscode
<ul class="wp-block-list">
<li>launch.json</li>
</ul>
</li>



<li>node_modules</li>



<li>src
<ul class="wp-block-list">
<li>js
<ul class="wp-block-list">
<li>components
<ul class="wp-block-list">
<li>Header
<ul class="wp-block-list">
<li>Title.js</li>
</ul>
</li>



<li>Footer.js</li>



<li>Header.js</li>



<li>Layout.js</li>
</ul>
</li>



<li>main.js</li>
</ul>
</li>



<li>index.html</li>



<li>main.min.js</li>
</ul>
</li>



<li>package-lock.json</li>



<li>package.json</li>



<li>webpack.config.js</li>
</ul>
</li>
</ul>



<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc3">webpack.config.js</span></h2>
<p>Reactは実行時にトランスパイルされるため、コンパイル前のソースにブレークポイントを配置してもデバッグできません。</p>
<p>そのため、SourceMapを使ってコンパイル前のソースでもデバッグできるようにします。</p>
<p>SourceMapはコンパイル前とコンパイル後の対応関係を示したものになります。</p>
<p>SourceMapが使えるようにwebpack.config.jsへ以下を追加します。</p>


<pre class="wp-block-code json"><code>devtool: 'inline-source-map'</code></pre>


<p>なお、上記の設定を入れるとブラウザの開発者ツール上で「webpack://」配下に「webpack」というフォルダが作成されます。</p>
<p>・設定前</p>
<p><img decoding="async" class="alignnone wp-image-801 size-full" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/sourcemap_before.png" alt="" width="545" height="361" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/sourcemap_before.png 545w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/sourcemap_before-300x199.png 300w" sizes="(max-width: 545px) 100vw, 545px" /></p>
<p>・設定後</p>
<p><img decoding="async" class="alignnone wp-image-802 size-full" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/sourcemap_after.png" alt="" width="548" height="378" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/sourcemap_after.png 548w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/sourcemap_after-300x207.png 300w" sizes="(max-width: 548px) 100vw, 548px" /></p>


<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc4">launch.json</span></h2>
<p>VSCodeでデバッグ実行するために必要となるlaunch.jsonを作成します。</p>
<p>「実行とデバッグ」から「launch.jsonファイルを作成します。」のリンクをクリックします。</p>
<p><img decoding="async" class="alignnone size-medium wp-image-819" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/launch.json_create-e1687359066449-271x300.png" alt="" width="271" height="300" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/launch.json_create-e1687359066449-271x300.png 271w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/launch.json_create-e1687359066449.png 365w" sizes="(max-width: 271px) 100vw, 271px" /></p>
<p>「Webアプリ（Chrome）」を選択します。</p>
<p><img decoding="async" class="alignnone wp-image-823 size-full" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/launch.json_create2.png" alt="" width="601" height="169" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/launch.json_create2.png 601w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/launch.json_create2-300x84.png 300w" sizes="(max-width: 601px) 100vw, 601px" /></p>
<p>launch.jsonが作成されるので以下の緑文字を追加します。</p>


<pre class="wp-block-code plaintext"><code>{
    "version": "0.2.0",
    "configurations": &#91;
        {
            "type": "chrome",
            "request": "launch",
            "name": "localhost に対して Chrome を起動する",
            "url": "http://localhost:8080",
            "webRoot": "${workspaceFolder}<span class="green">/src</span>",
<span class="green">            "sourceMapPathOverrides": {
                "webpack:///*": "${webRoot}/*",
            }</span>
        }
    ]
}</code></pre>


<p>webRootはプロジェクト上のどこにソースファイルがあるかを指定する箇所になります。</p>
<p>今回のソース構成はsrcフォルダ配下にデバッグ対象のファイルがあるため、</p>
<p>webRootを「 &#8220;${workspaceFolder}/src&#8221;」としてます。</p>
<p>sourceMapPathOverridesはSourceMapとVSCode用のソースの場所を紐づける箇所です。</p>
<p>SourceMapは以下の画像の通り、「webpack://」配下にあるため「&#8221;webpack:///*&#8221;: &#8220;${webRoot}/*&#8221;」としてます。</p>
<p><img decoding="async" class="alignnone wp-image-802 size-full" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/sourcemap_after.png" alt="" width="548" height="378" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/sourcemap_after.png 548w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/sourcemap_after-300x207.png 300w" sizes="(max-width: 548px) 100vw, 548px" /></p>


<p></p>


<h2 class="wp-block-heading has-cocoon-white-color has-deep-orange-background-color has-text-color has-background"><span id="toc5">デバッグ実行例</span></h2>
<p>それではデバッグしてみましょう。</p>
<p>なお、以前は「Debugger for Chrome」という拡張機能がありましたが、今のVSCodeには標準でJavaScript Debuggerが入ってるためデバッグのために拡張機能を入れる必要はないです。</p>
<p>以下のコマンドを実行して開発モードでサーバを起動します。</p>


<pre class="wp-block-code plaintext"><code>webpack serve --mode development</code></pre>


<p>ブレークポイントを配置して「F5」キーでデバッグ実行します。</p>
<p>ブラウザ上で画面操作をすると以下の例のようにブレークポイントで停止するようになります。</p>
<p><img decoding="async" class="alignnone wp-image-855 size-full" src="https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/breakpoint.png" alt="" width="1105" height="487" srcset="https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/breakpoint.png 1105w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/breakpoint-300x132.png 300w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/breakpoint-1024x451.png 1024w, https://gogorelaxlifeblog.com/wp-content/uploads/2023/06/breakpoint-768x338.png 768w" sizes="(max-width: 1105px) 100vw, 1105px" /></p>
<p> </p>


<p></p>
]]></content:encoded>
					
					<wfw:commentRss>https://gogorelaxlifeblog.com/reactdebug/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
