Spring Securityを使ったWebアプリケーションの基本①　〜ログイン機能の実装〜

<h2>はじめに</h2>
この記事はSpring Securityを使って、ログイン機能の実装やユーザ情報の管理などWebアプリケーションでよく使われている権限についての説明をしていきたいと思います。

<details>
<summary>ファイル構成</summary>
<pre>
<code>
.
├── .gradle
├── .idea
├── build
├── gradle
└── src
     ├── main
     |      ├── java
     |      |      └── com
     |      |           └── example
     |      |                └── practice 
     |      |                  ├── config
     |      |                  |      └── SecurityConfig.java
     |      |                  ├── web 
     |      |                  |    ├── order
     |      |                  |    |    ├── OrderForm.java
     |      |                  |    |    └── OrderController.java
     |      |                  |    └── IndexController.java
     |      |                  └── domain
     |      |                        └── order 
     |      |                             ├── OrderEntity.java
     |      |                             ├── OrderService.java
     |      |                             └── OrderRepository.java
     |      └── resources
     |            ├── static
     |            ├── templates
     |            |     ├── order
     |            |     |     ├── delete_confirmation.html
     |            |     |     ├── detail.html 
     |            |     |     ├── form.html 
     |            |     |     └── list.html
     |            |     └── index.html
     |            ├── schema.sql
     |            ├── data.sql 
     |            └── application.properties
     └── test
.gitignore
build.gradle
gradlew.bat
HELP.md
settings.gradle
</code>
</pre>
</details>

今回は<a href="https://github.com/tamakawa-ryuji/business_management_application/tree/149736c2c699579341d1a278335e8b57bf7c05e8">GitHub</a>に事前にコードを配布しているので、そちらをまずはダウンロードをしてから始めてください。

<h2>セキュリティの重要性</h2>
Webアプリケーションやオンラインサービスを提供する際には、ユーザーのプライバシーと情報の保護やシステムの安定性を確保するために必要なものです。
まずはセキュリティの大切さについて説明をしていきたいと思います。

<h3>ユーザープライバシーの保護</h3>
今回は業務用アプリケーションにログイン機能とユーザー情報の管理を実装します。
例えば、営業部では売上原価を元に利益率を考えることがあり、元の仕入原価を知らされていないケースを想定してみてください。
仕入原価を管理しているのが他部署（例えば購買部）だとすると、営業部には「仕入原価」を教えたくないのに、その情報が適切に保護されないと、誰でもその情報を取得できるようになってしまいます。
そして、個人情報漏洩が発生すると、ユーザーの信頼を失い、法的な問題や損害賠償請求のリスクが生じる可能性があります。セキュリティを考慮しないシステムでは、ユーザープライバシーの保護が十分になされていないことになります。

<h3>データ漏洩のリスク</h3>
次に想定して欲しいのは、機密情報や重要なデータが不正にアクセスされることによって、漏洩されるリスクがあることです。
例えば、競争企業があなたの企業の売値や仕入れ値を事前に把握されてしまったら、深刻な損害につながってしまいます。
また、クレジットカード情報、医療記録、企業機密などのデータが漏洩すれば、個人や組織に深刻な損害をもたらす可能性があります。

<h3>データ改ざんや破壊の防止</h3>
セキュリティが確保されていないシステムは、不正なアクセスによって、データが改ざんされたり、破壊されたりするリスクが高まります。
データの信頼性を保つためには、適切なアクセス制御や暗号化の対策が必要です。

<Br>
これらは一例ですが、他にもセキュリティを考慮することで、システムの信頼性を向上させることができます。

<h2>ログイン機能</h2>
これから、ログイン機能の実装を始めますが、まず、ログイン機能についての説明をしていきたいと思います。
ログイン機能は、Webアプリケーションやシステムにアクセスするユーザーが、正当なユーザーであることを確認し、アクセス権限を付与するための機能です。
以下にログイン機能に関する詳細を説明します。

<h3>ログイン機能の主な目的</h3>

<b>セキュリティ確保</b>
ログイン機能は、システムやアプリケーションのセキュリティを確保するために重要で、正当なユーザー以外はアクセスできないようにすることで、データや機密性を守ることができます。

<b>個別のアクセス権限の管理</b>
ユーザーごとに異なるアクセス権限を設定できるため、情報を適切に制御することができます。
管理者、一般ユーザー、ゲストなど、異なる権限レベルを持つユーザーに対して適切な権限を与えることができます。

<b>ユーザーエクスペリエンスの向上</b>
ログイン機能を導入することで、ユーザーは自分のアカウントにアクセスし、個人設定やデータを保持することができます。
これによって、ユーザーエクスペリエンスが向上します。

<h3>ログインプロセスの一般的な流れ</h3>

<b>1.ログインフォームの表示</b>
ユーザーはログインページにアクセスし、ユーザー名とパスワードを入力するためのログインフォームが表示されます。

<b>2.認証の実行</b>
ユーザーがユーザー名とパスワードを入力すると、システムはこれを検証し、ユーザーが正当なものであるかどうかを認証します。

<b>3.認可の確認</b>
認証が成功すると、システムはユーザーのアクセス権限を確認し、適切なリソースや機能にアクセスできるかどうかを確認します。

<b>4.セッションの開始</b>
認証と認可が成功した場合、システムはユーザーのためにセッションを開始します。セッションは一定の時間内にユーザーがアクティブであることを保持し、ユーザーエクスペリエンスを向上させるために使用されます。

<b>5.リダイレクトまたはアクセス許可</b>
 認証と認可が成功したら、システムは適切なページにリダイレクトするか、必要なアクセス許可を付与してアクセスを許可します。

これらの流れを通じて、ログイン機能を実装していきたいと思います。

<h2>ログイン機能の実装</h2>
まずは、Spring Securityを使用できるようにの依存関係を追加します。

以下の依存関係をbuild.gradleに追加しましょう。

```gradle:build.gradle
dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	developmentOnly 'org.springframework.boot:spring-boot-devtools'
	compileOnly 'org.projectlombok:lombok'
	annotationProcessor 'org.projectlombok:lombok'
	implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.2.0'
	runtimeOnly 'com.h2database:h2'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	implementation 'org.springframework.boot:spring-boot-starter-validation'
	implementation 'org.springframework.boot:spring-boot-starter-security'
	implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5'
	testImplementation 'org.springframework.security:spring-security-test'
}

```

依存関係を追加し、デバック機能で実行をすると、以下のログのパスワードが表示されます。

```
Using generated security password: #####################
```

そして、「<a>http://localhost:8080</a>」でログインをすると、以下のような画面が表示されるので、Usernameを「user」で、passwordを「ログで出力されたパスワード」を入力してください。

![スクリーンショット 2023-08-23 7.34.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2782718/1f5a2a44-aeca-5a55-3b1f-8940f9afd94b.png)

そうすると、「業務管理アプリケーション」のトップページに遷移されます。


![スクリーンショット 2023-08-23 7.37.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2782718/3c75b581-cd35-470e-444c-3a8907ca2663.png)

これらの一連の流れがSpring  Securityでデフォルト値で設定されている画面になります。

<h2>カスタムログイン機能の実装</h2>
では、次にカスタムでログイン機能を実装していきます。

簡単に流れの説明をします。
まず、Spring Securityに未ログインのSpring Securityにログインページのパスを教えます。
そして、未ログインの状態でログインが必要なページにアクセスした時に、ログインページに遷移させます。その時に、/loginは認証不要なこととそれ以外のページは認証が必要であることを念頭においてください。
Spring Securityにログインページのパスを教えることで、遷移が実現できるようになる。

まずは、以下のコードのように、「/login」のページのViewを実装していきましょう。
```html:login.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>トップページ | 業務用アプリケーション</title>
  <!-- Bootstrap CSS link -->
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
</head>
<body>
<div class="container mt-4">
  <h1 class="mb-3"><a href="../index.html" th:href="@{/}" style="color: inherit; text-decoration: none;">業務管理アプリケーション</a></h1>
  <p>これはログインページです。</p>
</div>
</body>
</html>
```

「/login」のコントローラーを設定します。

```java:indexController
package com.example.practice.web;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class indexController {
    @GetMapping
    public String index() {
        return "index";
    }

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

SecurityConfig は、Spring SecurityをカスタマイズするためのJavaクラスであり、アプリケーションのセキュリティ関連の設定を定義するために使用されるので、その実装をしていきます。

```java:SecurityConfig
package com.example.practice.config;

import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;


//WebSecurity関連の設定であるということを示すアノテーション
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .mvcMatchers("/login/**").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login").permitAll();
    }
}
```

.mvcMatchers("/login/**").permitAll(): /login/ で始まるURLに対しては、どのユーザーでもアクセスできるように許可します。これにより、ログインページへのアクセスが制限されなくなります。

.anyRequest().authenticated(): 他の全てのリクエストに対しては、ユーザーが認証済み（ログインしている）である必要があります。つまり、ログインしていないユーザーは他のページにアクセスできません。

.formLogin(): フォームベースのログインを設定します。

.loginPage("/login").permitAll(): ログインページのURLを /login に設定し、どのユーザーでもアクセスできるように許可します。これにより、ログインページへのアクセスが制限されず、誰でもログインページにアクセスできます。

つまり、この設定は、ログインページ以外の全てのページにアクセスする際にはユーザーが認証済みである必要があり、ログインページにはログインしていないユーザーもアクセスできるようになるという動作を実現します。


<h3>終わりに</h3>
セキュリティを考慮したシステムは信頼性が向上し、ユーザーのプライバシーとデータの保護が確保されます。
セキュリティは必要不可欠であることを説明し、Spring Securityを使ったログイン機能の実装方法について理解しました。