Springなどのフレームワークを使用してログイン機能を実装する機会が多く、中身がよく分かっていなかったので学習の一環として自力で実装すること まずプロジェクトを作成し、それっぽいログイン画面を作成します。
動的webプロジェクトを作成し、index.jspファイルを作成します。 index.jsp 続いて、Login処理を行うLoginServletクラスを作成します。 MVC構成で作成するので、controllerパッケージを作成し、その直下に作成します。 LoginServlet doGetメソッドに記載。 ユーザーID、パスワードのinputタグ内のname属性値が取得されるのでgetParameterの引数にname属性の値を記述。 ログイン後遷移するtop.jspにforwardする。 次にtop.jspを作成します。 top.jsp 遷移が正常に行われるか確認します。
コンソール
それでは続いてデータの連携を行なっていきます。 今回、DB接続は行わずデータのやり取りはSessionを利用します。 まずはデータを格納するBeanクラスを作成します。 Javaソースの直下にdomainパッケージを作成し、UserBeanクラスを作成します。 UserBean 次に、Daoを作成します。
Javaソース直下にDaoパッケージを作成し、UserDaoクラスを作成します。 それらしいデータをBeanクラスにセットします。 ※住所などの個人情報はジェネレータを使用しています。 続いて、ログインロジックを実装します。 そして、LoginServletに下記のようにチェックロジックを追加します。 さらに、ログイン失敗後の遷移画面も作っていきます。 LoginServletのパスにlogin-failed.jspという名称を入れているのでそこに合わせたファイル名のJSPを作成します。 設定したパスワード・ユーザーIDでログインしたときは成功画面へ、それ以外のパスワード・ユーザーIDでログインしたときは失敗画面へ遷移すればOKです。 続いて、Userの情報を画面に表示させていきます。 まずは、LoginServletに以下を追記します。 これで、userBeanオブジェクトとuserBeanパラメータが紐づきました。 続いて画面上にデータを表示させていきます。
今回はJSTLを使用します。 ※EL式を利用する場合はXSS(クロスサイトスクリプティング)の脆弱性に注意してください。 こちらのサイトからzipファイルをダウンロードし、WEB-INF配下のlibフォルダ直下に配置します。
タグリブを使用する場合は、使用するJSPファイルに宣言を追加してください。 トップ画面に以下の記述を追加し、ログインした際にユーザー情報が反映されていることを確認します。
login-failed.jspにも似たような形でデータを表示させます。 こちらはステータスに応じてメッセージを分岐させています。 これでSessionのデータを画面に表示させる実装が完了しました。 [余談]
JSTLを使用することで悪意のあるスクリプトが埋め込まれた時にタグを反映させないc:outなどを使用することで対策が可能です。
EL式ではタグがそのまま反映されてしまうため、XSS攻撃に対して脆弱性があると言われています。 フィルターは共通処理を行う仕組み。
JSPやサーブレットを呼び出す前後で⾃動的に呼び出されるので認証・認可の実装などに用いられます。 それではこれまでの流れに沿ってtop.jspから会員のみ閲覧可能なalbum.jspを作成していきます。
(jspファイルなどの実装工程は冗長なので省略。GitHubのソースをご参照ください) 変更点としては三つで です。 それではフィルターの実装を行なっていきます。
画像の項目を選択し、LoginFilterクラスを作成。 以下のようにFilterの設定を行います。urlPatternsのパスにフィルターの対象とするServletを記述します。 次にdoFilterメソッドに処理を記述します。 ここまでで、AlbumSevletに直接アクセスしようとするとログイン画面にリダイレクトされて、コンソールに「未ログイン」の文字が表示されればOKです。 Sessionが残っている場合、AlbumServletにログイン処理を行わなくともアクセス出来てしまう状態なのでSessionを破棄するように実装していきます。 LogoutServletを作成し、以下のように処理を記述します。 top.jspにログアウト用のリンクを作成し、 ログイン→ログアウトリンクを押下→AlbumServletに直接アクセス の手順を踏んだ時にログイン画面に遷移されればOKです。 以上長くなりましたがこれで完了です。気が向いた頃に認可処理も実装してみようと思います。
ありがとうございました。目的
前提
環境
実装
プロジェクト作成~ログイン遷移
<%@ page language="java" contentType="text/html;
charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>ログイン画面</title>
<link rel="stylesheet" type="text/css" href="css/login.css">
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1"
crossorigin="anonymous">
</head>
<body>
<div class="container">
<div class="card card-container">
<img id="profile-img" class="profile-img-card"
src="//ssl.gstatic.com/accounts/ui/avatar_2x.png" />
<p id="profile-name" class="profile-name-card"></p>
<form class="form-signin" method="post" action="LoginServlet">
<span id="reauth-email" class="reauth-email"></span>
<input
type="email" id="inputEmail" class="form-control"
placeholder="Email address" name="user-id" required autofocus>
<input
type="password" id="inputPassword" class="form-control"
placeholder="Password" name="password" required>
<button class="btn btn-lg btn-primary btn-block btn-signin"
type="submit" value="signin">ログイン</button>
</form>
<!-- /form -->
<a href="#" class="forgot-password"> パスワードを忘れた方はこちら </a>
</div>
<!-- /card-container -->
</div>
<!-- /container -->
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.bundle.min.js"
integrity="sha384-ygbV9kiqUc6oa4msXn9868pTtWMgiQaeYH7/t7LECLbyPA2x65Kgf80OJFdroafW"
crossorigin="anonymous"></script>
</body>
</html>
package controller;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class LoginServlet
*/
@WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public LoginServlet() {
super();
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//JSPから、ユーザーIDとパスワードの取得
String user_id = request.getParameter("user-id");
String password = request.getParameter("password");
//ログ
System.out.println("ユーザーID:" + user_id);
System.out.println("パスワード:" + password);
//ログイン完了ページへ転送
request.getRequestDispatcher("/WEB-INF/top.jsp")
.forward(request, response);
return;
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>ログインに成功しました。</h1>
</body>
</html>
データ連携〜ログインロジックの作成
package domain;
import java.io.Serializable;
public class UserBean implements Serializable {
private static final long serialVersionUID = 1L;
private String user_id;
private String user_name;
private String password;
private String phonenumber;
private String address;
private String role; // 権限
private int status;// OK:0,未登録:1,パスワード不一致:2
// setter/getterは省略
}
package dao;
import domain.UserBean;
public class UserDao {
/**
* ユーザーIDからUserBeanを取得
*
* @param user_id ユーザーID
* @return UserBean ユーザーBean
*/
public UserBean getUser(String user_id) {
UserBean userBean = new UserBean();
switch (user_id) {
case "001":// user_id=001の場合
userBean.setUser_id(user_id);
userBean.setUser_name("前田");
userBean.setPassword("pass");
userBean.setPhonenumber("090-0000-1111");
userBean.setAddress("佐賀県杵島郡大町町福母3-2-2 パークハイツ福母 9F");
userBean.setRole("member");
break;
case "002":// user_id=002の場合
userBean.setUser_id(user_id);
userBean.setUser_name("鈴木");
userBean.setPassword("pass2");
userBean.setPhonenumber("090-5561-1174 ");
userBean.setAddress("三重県四日市市前田町9-1-9");
userBean.setRole("member");
break;
case "100":// user_id=100の場合
userBean.setUser_id(user_id);
userBean.setUser_name("佐藤");
userBean.setPassword("pass3");
userBean.setPhonenumber("070-2968-0125 ");
userBean.setAddress("香川県綾歌郡綾川町萱原8-3-3");
userBean.setRole("admin");
break;
default:// ユーザーIDが存在しない場合
userBean.setUser_id(user_id);
userBean.setStatus(1);
}
return userBean;
}
}
package model;
import dao.UserDao;
import domain.UserBean;
public class LoginService {
/**
* @param user_id ユーザーID
* @param password パスワード
* @return UserBean
*/
public UserBean checkLogin(String user_id, String password) {
UserDao userDao = new UserDao();
UserBean userBean = new UserBean();
// UserBeanの取得
userBean = userDao.getUser(user_id);
// ID存在チェック
// status: 0:OK, 1:不在, 2:NG
if (userBean.getStatus() == 1) {
return userBean;
} else {
// パスワードのチェック
if (userBean.getPassword().equals(password)) {
userBean.setStatus(0);
} else {
userBean.setStatus(2);
}
return userBean;
}
}
}
LoginService loginService = new LoginService();
// UserBeanの取得
UserBean userBean = loginService.checkLogin(user_id, password);
// ログイン失敗の場合は、失敗ページへ
if (userBean.getStatus() != 0) {
// 失敗ページの転送
request.getRequestDispatcher("/WEB-INF/login-failed.jsp").forward(request, response);
return;
}
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1 style="color: red;">Sign in failed</h1>
</body>
</html>
SessionにUserBeanのデータを格納し画面に表示する
//UserBeanをセッションにセット
HttpSession session = request.getSession(true);
session.setAttribute("userBean", userBean);
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<div>
ユーザーID:
<c:out value="${userBean.user_id}" />
</div>
<div>
ユーザー名:
<c:out value="${userBean.user_name}" />
</div>
<body>
<h1 style="color: red;">Sign in failed</h1>
<!-- 判定フラグ -->
<c:if test="${userBean.status == 1}" var="flg" />
<%--IDが存在しない時(ステータス1の場合) --%>
<c:if test="${flg}">
<p>
ユーザーID: <strong><c:out value="${userBean.user_id}" /></strong>
は存在しません。<br />
</p>
</c:if>
<%-- パスワード間違いの時(ステータス2の場合) --%>
<c:if test="${!flg}">
<p>
<strong>パスワードが間違っています。</strong><br />
</p>
</c:if>
<a href="javascript:history.back();">戻る</a>
</body>
フィルターの実装
/* フィルターの設定 */
@WebFilter(filterName = "LoginFilter", urlPatterns = { "/AlbumServlet" })
public class LoginFilter implements Filter {
//省略
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// HttpServletに変換
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
// セッションから情報取得
HttpSession session = req.getSession();
UserBean userBean = (UserBean) session.getAttribute("userBean");
// 未ログインの場合、ログイン画面にリダイレクト
if (session == null || userBean == null) {
System.out.println("未ログイン");
res.sendRedirect("index.jsp");
return;
}
// pass the request along the filter chain
chain.doFilter(request, response);
}
ログアウト処理の実装
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//セッション取得
HttpSession session = request.getSession();
//セッション破棄
session.invalidate();
//ページ転送
request.getRequestDispatcher("index.jsp")
.forward(request, response);
}
<p class="logout">
<a href="LogoutServlet">Logout</a>
</p>
参考