サーブレット&JSPの基本|ひとこと広場のメイン画面とログアウト機能を作成する

ログイン中の利用者だけがメイン画面へ進める。セッション確認とログアウト処理で画面遷移を完成させよう

前回の記事では、ひとこと広場のログイン機能を作成しました。

トップ画面index.jspにログインフォームを追加し、Login.javaで利用者名とパスワードを受け取り、LoginLogic.javaでログイン判定を行いました。そして、ログインに成功した場合は、UserインスタンスをloginUserという属性名でセッションスコープに保存しました。

今回の記事では、ログイン結果画面からメイン画面へ進めるようにします。

メイン画面は、ログインした利用者がひとことを投稿したり、ほかの利用者の投稿を閲覧したりする中心画面です。ただし、今回の記事では、投稿フォームや投稿一覧の表示はまだ作成しません。まずは、ログイン中の利用者名をメイン画面に表示できるところまで作ります。

さらに、メイン画面からログアウトできる機能も追加します。

ログアウトでは、セッションスコープを破棄します。セッションスコープにはログイン中のUserインスタンスが保存されているため、セッションスコープを破棄することで、ログイン状態を終了できます。

この記事では、Main.java、main.jsp、Logout.java、logout.jspを作成し、ログイン済みの場合はメイン画面を表示し、未ログインの場合はトップ画面へ戻す流れを確認します。

今回作成する機能

今回作成する内容は、大きく分けると2つです。

作成する機能

機能内容
メイン画面表示機能ログイン済みの利用者だけがメイン画面を表示できる
ログアウト機能セッションスコープを破棄してログイン状態を終了する

メイン画面表示機能では、セッションスコープからloginUserを取得します。

loginUserが取得できた場合は、ログイン済みと判断してmain.jspへフォワードします。

loginUserが取得できなかった場合は、未ログインと判断してトップ画面index.jspへリダイレクトします。

ログアウト機能では、Logout.javaでセッションスコープを破棄し、logout.jspへフォワードします。

今回作成・修正するファイル

今回の記事では、次のファイルを作成または修正します。

ファイル一覧

ファイル種類役割
Main.javaコントローラメイン画面表示に関するリクエストを処理する
main.jspビューログイン中の利用者名を表示する
Logout.javaコントローラログアウト処理を行う
main.jspビュー 修正ログアウトリンクを追加する
logout.jspビューログアウト完了画面を表示する

main.jspは、最初にログイン中の利用者名を表示する画面として作成します。

その後、ログアウト機能を追加するときに、同じmain.jspへログアウトリンクを追加します。

メイン画面表示の流れ

ログイン結果画面loginResult.jspには、前回の記事で次のリンクを作成しました。

ひとこと投稿・閲覧へ

このリンクをクリックすると、MainというURLパターンへGETリクエストが送られます。

今回作成するMain.javaは、このGETリクエストを受け取り、メイン画面を表示するか、トップ画面へ戻すかを判断します。

メイン画面表示で確認すること

確認する内容処理
ひとことリストがあるかアプリケーションスコープから取得し、なければ新規作成する
ログインしているかセッションスコープからloginUserを取得する
ログイン済みの場合main.jspへフォワードする
未ログインの場合index.jspへリダイレクトする

メイン画面を表示するだけなら、セッションスコープの確認だけでもよさそうに見えます。

しかし、ひとこと広場では、後の記事で投稿一覧を表示します。その投稿一覧は、アプリケーションスコープに保存する予定です。

そのため、Main.javaでは、メイン画面を表示する前に、アプリケーションスコープにひとことリストが存在するかを確認します。まだ存在しない場合は、空のリストを作成して保存しておきます。

図1:メイン画面表示の流れ

この図から分かること

ログイン結果画面からひとこと投稿・閲覧へリンクをクリックすると、Main.javaへGETリクエストが送られます。

Main.javaでは、まずアプリケーションスコープからmessageListを取得します。messageListがまだ存在しない場合は、新しいArrayListを作成してアプリケーションスコープに保存します。

その後、セッションスコープからloginUserを取得します。loginUserが取得できればログイン済みと判断し、main.jspへフォワードします。

main.jspでは、セッションスコープからloginUserを取得し、ログイン中の利用者名を表示します。

ひとことリストを先に用意する理由

今回の記事では、まだひとこと投稿機能は作成しません。

それでも、Main.javaではアプリケーションスコープにmessageListがあるかを確認します。

これは、後の記事で投稿一覧を表示するときに、必ずmessageListが存在する状態にしておくためです。

messageListを用意する理由

理由内容
投稿一覧で使うため投稿されたひとことをリストで管理する
全ユーザーで共有するためアプリケーションスコープに保存する
初回アクセスでも安全にするためまだ存在しない場合は空のリストを作成する
次の記事につなげるため投稿機能を追加しやすくする

ひとこと広場では、すべての利用者の投稿を同じ一覧に表示します。

そのため、投稿リストはユーザーごとのセッションスコープではなく、Webアプリケーション全体で共有できるアプリケーションスコープに保存します。

Main.javaで行う処理

Main.javaは、メイン画面に関するリクエストを処理するコントローラです。

今回のMain.javaでは、GETリクエストを受け取るdoGetメソッドを作成します。

Main.javaの処理

順番処理内容
1ServletContextを取得するアプリケーションスコープを操作する準備をする
2messageListを取得するアプリケーションスコープからひとことリストを取り出す
3messageListがnullなら作成する初回アクセス時に空のArrayListを用意する
4sessionを取得するセッションスコープを操作する準備をする
5loginUserを取得するログイン中の利用者情報を確認する
6loginUserがnullならリダイレクト未ログインとしてトップ画面へ戻す
7loginUserがあるならフォワードログイン済みとしてmain.jspを表示する

Main.javaでは、アプリケーションスコープとセッションスコープの両方を使います。

アプリケーションスコープは投稿リストの準備に使い、セッションスコープはログイン状態の確認に使います。

ひとことに関するリクエストを処理するコントローラを作成する

Main.javaは、servletパッケージに作成します。

URLパターンは/Mainです。

ひとことに関するリクエストを処理するコントローラの例

ファイル名: Main.java

package servlet;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import model.ShortMessage;
import model.User;

@WebServlet("/Main")
public class Main extends HttpServlet {
    private static final long serialVersionUID = 1L;

    @SuppressWarnings("unchecked")
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response)
            throws ServletException, IOException {

        // ひとことリストをアプリケーションスコープから取得
        ServletContext application = this.getServletContext();

        List<ShortMessage> messageList =
                (List<ShortMessage>) application.getAttribute("messageList");

        // 取得できなかった場合は、ひとことリストを新規作成して
        // アプリケーションスコープに保存
        if (messageList == null) {
            messageList = new ArrayList<>();
            application.setAttribute("messageList", messageList);
        }

        // ログインしているか確認するため、
        // セッションスコープからユーザー情報を取得
        HttpSession session = request.getSession();

        User loginUser =
                (User) session.getAttribute("loginUser");

        if (loginUser == null) {
            // ログインしていない場合はトップ画面へリダイレクト
            response.sendRedirect("index.jsp");
            return;

        } else {
            // ログイン済みの場合はメイン画面へフォワード
            RequestDispatcher dispatcher =
                    request.getRequestDispatcher(
                            "WEB-INF/jsp/main.jsp");
            dispatcher.forward(request, response);
        }
    }
}

このサーブレットでは、doGetメソッドを使っています。

loginResult.jspのひとこと投稿・閲覧へリンクをクリックすると、GETリクエストでMain.javaが呼び出されるためです。

Main.javaのポイント

Main.javaでは、アプリケーションスコープとセッションスコープを使い分けています。

使っているスコープ

スコープ保存・取得するもの目的
アプリケーションスコープmessageList全ユーザーのひとことを共有するため
セッションスコープloginUserログイン中の利用者を確認するため

messageListは、全ユーザーで共有する投稿一覧です。

loginUserは、現在ログインしている利用者の情報です。

この2つは性質が違います。全ユーザーで共有したいmessageListはアプリケーションスコープ、ユーザーごとに管理したいloginUserはセッションスコープに保存します。

messageListがnullだった場合の処理

アプリケーションサーバを起動して最初にMain.javaへアクセスしたとき、アプリケーションスコープにはまだmessageListが保存されていません。

その状態でapplication.getAttribute("messageList")を実行すると、nullが返されます。

そこで、Main.javaでは次のように、messageListがnullかどうかを確認しています。

if (messageList == null) {
messageList = new ArrayList<>();
application.setAttribute("messageList", messageList);
}

nullの場合に行うこと

状態処理
messageListが取得できた既存のリストを使う
messageListがnull新しいArrayListを作成して保存する

この処理により、メイン画面を表示する時点で、必ずアプリケーションスコープ内にmessageListが用意された状態になります。

次の記事で投稿機能を追加するとき、このmessageListにShortMessageインスタンスを追加していきます。

未ログインの場合はトップ画面へリダイレクトする

Main.javaでは、セッションスコープからloginUserを取得します。

User loginUser =
(User) session.getAttribute("loginUser");

loginUserがnullの場合は、ログインしていないと判断します。

未ログインの状態でメイン画面を表示してしまうと、誰が投稿しているのか分からなくなります。また、ログインしていない利用者にメイン画面を見せることにもなってしまいます。

そのため、loginUserがnullの場合は、トップ画面index.jspへリダイレクトします。

未ログイン時の処理

状態処理
loginUserが取得できるmain.jspへフォワードする
loginUserがnullindex.jspへリダイレクトする

ここではフォワードではなくリダイレクトを使います。

リダイレクトを使うと、ブラウザのアドレスバーも転送先のURLに変わります。

図2:未ログインでMainへアクセスしたときの流れ

この図から分かること

未ログインの状態でMainへ直接アクセスすると、Main.javaはセッションスコープからloginUserを取得しようとします。

しかし、ログインしていないため、loginUserは取得できません。

その場合、Main.javaは未ログインと判断し、index.jspへリダイレクトします。

リダイレクトを使うことで、ブラウザのアドレスバーもトップ画面のURLに変わります。表示されている画面とURLが一致するため、利用者にとって自然な画面遷移になります。

フォワードではなくリダイレクトを使う理由

未ログイン時にトップ画面へ戻すだけなら、フォワードでも表示自体はできます。

しかし、フォワードを使うと、表示される画面はトップ画面なのに、ブラウザのアドレスバーにはMainのURLが残ります。

たとえば、次のような状態になります。

フォワードを使った場合の問題

項目状態
アドレスバーhttp://localhost:8080/Hiroba/Main
表示される画面トップ画面
問題点URLはメイン画面なのに、画面はトップ画面になる

このように、URLと表示内容にズレが生じます。

一方、リダイレクトではブラウザが改めてindex.jspへリクエストします。そのため、アドレスバーもトップ画面のURLになります。

リダイレクトを使った場合

項目状態
アドレスバーhttp://localhost:8080/Hiroba/index.jsp
表示される画面トップ画面
よい点URLと画面が一致する

未ログインでメイン画面へ直接アクセスされた場合は、トップ画面からログインし直してもらう流れが自然です。

そのため、ここではリダイレクトを使います。

メイン画面を作成する

次に、メイン画面を表示するmain.jspを作成します。

現時点では、まだ投稿フォームや投稿一覧は表示しません。

まずは、セッションスコープに保存されたloginUserを取得し、利用者名を画面に表示します。

メイン画面に表示する内容

表示項目内容
見出しひとこと広場メイン
ログイン中の表示利用者名さん、ログイン中

main.jspは、WEB-INF/jspディレクトリに作成します。

WEB-INF内のJSPは、ブラウザから直接アクセスできません。Main.javaからフォワードして表示します。

メイン画面を出力するビューを作成する

main.jspは、src/main/webapp/WEB-INF/jspディレクトリに作成します。

メイン画面を出力するビューの例

ファイル名: main.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8" %>
<%@ page import="model.User" %>
<%
    // セッションスコープに保存されたユーザー情報を取得
    User loginUser =
            (User) session.getAttribute("loginUser");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>ひとこと広場</title>
</head>
<body>
<h1>ひとこと広場メイン</h1>

<p>
<%= loginUser.getName() %>さん、ログイン中
</p>

</body>
</html>

main.jspでは、session.getAttribute("loginUser")を使って、セッションスコープからUserインスタンスを取得しています。

取得したUserインスタンスを使い、loginUser.getNameで利用者名を表示します。

main.jspのポイント

main.jspでは、JSPの暗黙オブジェクトsessionを使っています。

サーブレットではrequest.getSessionでHttpSessionインスタンスを取得しましたが、JSPではsessionをそのまま使えます。

main.jspで行っていること

処理内容
page import="model.User"JSPでUser型を使えるようにする
session.getAttribute("loginUser")セッションスコープからUserインスタンスを取得する
(User)Object型からUser型へキャストする
loginUser.getName利用者名を取得して表示する

Main.javaでログイン済みかどうかを確認してからmain.jspへフォワードしているため、main.jspではloginUserが取得できる前提で画面を作成しています。

メイン画面表示の動作確認

ここまで作成できたら、ログイン時と未ログイン時の動作を確認します。

ログイン時の確認手順

手順内容
1ブラウザでhttp://localhost:8080/Hiroba/へアクセスする
2任意の利用者名とパスワード1234でログインする
3ログイン結果画面でひとこと投稿・閲覧へをクリックする
4メイン画面が表示される
5利用者名さん、ログイン中と表示されることを確認する

ログインに成功している場合は、セッションスコープにloginUserが保存されています。

そのため、Main.javaはログイン済みと判断し、main.jspへフォワードします。

ブラウザの表示例

ログイン結果画面でひとこと投稿・閲覧へをクリックします。

利用者名さん、ログイン中と表示されます。

未ログイン時の確認手順

手順内容
1ブラウザをすべて閉じる
2ブラウザでhttp://localhost:8080/Hiroba/Mainへ直接アクセスする
3トップ画面が表示されることを確認する

未ログインの場合は、セッションスコープにloginUserがありません。

そのため、Main.javaはindex.jspへリダイレクトします。

セッションスコープの状態が残っていると確認しづらいので、未ログイン時の動作を確認するときは、ブラウザをすべて閉じてから行うと分かりやすくなります。

ブラウザの表示例

ログアウト機能の流れ

メイン画面が表示できるようになったら、次にログアウト機能を作成します。

ログアウトは、ログイン状態を終了するための機能です。

ひとこと広場では、ログイン中の利用者情報をセッションスコープに保存しています。そのため、ログアウト時にはセッションスコープを破棄します。

ログアウトで行うこと

項目内容
ログアウトリンクmain.jspからLogout.javaへGETリクエストを送る
セッション破棄session.invalidateでセッションスコープを破棄する
画面表示logout.jspへフォワードする
結果ログアウトしましたと表示する

セッションスコープを破棄すると、その中に保存されていたloginUserも利用できなくなります。

つまり、ログイン状態が解除されます。

図3:ログアウト機能の流れ

この図から分かること

メイン画面のログアウトリンクをクリックすると、Logout.javaへGETリクエストが送られます。

Logout.javaでは、session.invalidateを実行してセッションスコープを破棄します。

セッションスコープが破棄されると、その中に保存されていたloginUserも使えなくなります。これにより、ログイン状態が終了します。

その後、logout.jspへフォワードし、ログアウトしましたという画面を表示します。

ログアウトに関するリクエストを処理するコントローラを作成する

Logout.javaは、ログアウト処理を担当するサーブレットです。

servletパッケージに作成します。

URLパターンは/Logoutです。

ログアウトに関するリクエストを処理するコントローラの例

ファイル名: Logout.java

package servlet;

import java.io.IOException;

import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;

@WebServlet("/Logout")
public class Logout extends HttpServlet {
    private static final long serialVersionUID = 1L;

    protected void doGet(HttpServletRequest request,
            HttpServletResponse response)
            throws ServletException, IOException {

        // セッションスコープを破棄
        HttpSession session = request.getSession();
        session.invalidate();

        // ログアウト画面にフォワード
        RequestDispatcher dispatcher =
                request.getRequestDispatcher(
                        "WEB-INF/jsp/logout.jsp");
        dispatcher.forward(request, response);
    }
}

Logout.javaでは、request.getSessionでHttpSessionインスタンスを取得し、session.invalidateでセッションスコープを破棄しています。

その後、logout.jspへフォワードします。

session.invalidateの役割

session.invalidateは、セッションスコープそのものを破棄するための処理です。

セッションスコープを破棄すると、その中に保存されていたインスタンスも利用できなくなります。

session.invalidateで起こること

処理内容
セッションスコープを破棄する現在のHttpSessionを無効にする
loginUserが消えるログイン中のUserインスタンスが利用できなくなる
ログイン状態が終わる次にMainへアクセスしても未ログイン扱いになる

ログアウト処理では、ログイン中の利用者情報を残しておく必要がありません。

そのため、セッションスコープ全体を破棄して、ログイン状態を終了します。

メイン画面にログアウトリンクを追加する

次に、main.jspにログアウト用のリンクを追加します。

ログアウトリンクのリンク先はLogoutです。

このリンクをクリックすると、Logout.javaへGETリクエストが送られます。

追加するリンク

リンク文字href属性呼び出されるサーブレット
ログアウトLogoutLogout.java

main.jspを次のように修正します。

メイン画面を出力するビューを修正する

ここでは、先ほど作成したmain.jspにログアウトリンクを追加します。

メイン画面を出力するビューを修正した例

ファイル名: main.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8" %>
<%@ page import="model.User" %>
<%
    // セッションスコープに保存されたユーザー情報を取得
    User loginUser =
            (User) session.getAttribute("loginUser");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>ひとこと広場</title>
</head>
<body>
<h1>ひとこと広場メイン</h1>

<p>
<%= loginUser.getName() %>さん、ログイン中
<a href="Logout">ログアウト</a>
</p>

</body>
</html>

ログアウトリンクをクリックすると、Logout.javaへリクエストが送られます。

Logout.javaでセッションスコープが破棄され、logout.jspへフォワードされます。

ログアウト画面を作成する

最後に、ログアウト完了を表示するlogout.jspを作成します。

logout.jspでは、ログアウトしましたというメッセージと、トップ画面へ戻るリンクを表示します。

ログアウト画面に表示する内容

表示項目内容
見出しひとこと広場ログアウト
メッセージログアウトしました
リンクトップへ

logout.jspも、src/main/webapp/WEB-INF/jspディレクトリに作成します。

ブラウザから直接アクセスする画面ではなく、Logout.javaからフォワードして表示する画面です。

ログアウト画面を出力するビューを作成する

logout.jspを次のように作成します。

ログアウト画面を出力するビューの例

ファイル名: logout.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>ひとこと広場</title>
</head>
<body>
<h1>ひとこと広場ログアウト</h1>
<p>ログアウトしました</p>
<a href="index.jsp">トップへ</a>
</body>
</html>

このJSPでは、ログアウト完了のメッセージを表示し、トップ画面へ戻るためのリンクを用意しています。

トップへリンクをクリックすると、index.jspへ移動します。

ログアウト機能の動作確認

ここまで作成できたら、ログアウト機能を確認します。

ログアウト時の確認手順

手順内容
1ブラウザでhttp://localhost:8080/Hiroba/へアクセスする
2任意の利用者名とパスワード1234でログインする
3ログイン結果画面からひとこと投稿・閲覧へをクリックする
4メイン画面が表示される
5ログアウトリンクをクリックする
6ログアウト画面が表示される
7ログアウトしましたと表示される
8トップへリンクをクリックしてトップ画面へ戻る

ログアウト後は、セッションスコープが破棄されています。

そのため、再度Mainへ直接アクセスすると、loginUserを取得できず、トップ画面へリダイレクトされます。

ブラウザの表示例:ログアウト画面

ログアウトリンクをクリックします。

ログアウト画面が表示されます。

ログアウト後の確認

ログアウトが正しく動作しているかを確認するには、ログアウト後にMainへ直接アクセスしてみると分かりやすいです。

ログアウト後の確認手順

手順内容
1ログアウト画面を表示する
2ブラウザでhttp://localhost:8080/Hiroba/Mainへアクセスする
3トップ画面へ戻されることを確認する

ログアウトによってセッションスコープが破棄されていれば、Main.javaでloginUserを取得できません。

その結果、未ログインと判断され、index.jspへリダイレクトされます。

ブラウザの表示例:index.jspへリダイレクトされる

今回作成したファイル

今回の記事では、メイン画面表示とログアウト機能を作成しました。

作成・修正したファイル

ファイル保存場所内容
Main.javasrc/main/java/servletメイン画面表示とログイン確認を行う
main.jspsrc/main/webapp/WEB-INF/jspログイン中の利用者名を表示する
Logout.javasrc/main/java/servletセッションスコープを破棄する
main.jspsrc/main/webapp/WEB-INF/jspログアウトリンクを追加する
logout.jspsrc/main/webapp/WEB-INF/jspログアウト完了画面を表示する

今回のポイントは、Main.javaでログイン状態を確認することです。

セッションスコープからloginUserを取得できる場合だけ、メイン画面を表示します。取得できない場合は、未ログインと判断してトップ画面へリダイレクトします。

また、Logout.javaではsession.invalidateを使い、セッションスコープを破棄しました。これにより、ログイン状態を終了できます。

次の記事では、メイン画面にひとこと投稿フォームと投稿一覧を追加します。投稿されたひとことは、アプリケーションスコープに保存したmessageListへ追加し、全ユーザーで閲覧できるようにします。