サーブレットフィルターとは
Servletの前後に処理を挟むための機能を提供するクラス
⇒ ログイン認証や文字化け防止など
Filterをimplementsする
@WebFilter() アノテーションで、対応するURLを指定できる
FilterServletプロジェクト
FilterServletプロジェクト 内に以下のプログラムを作成。
ID は「jsp」パスワードは「servlet」と設定。
IDとパスワードの 判定は「 LoginService 」プログラムにておこなう。
正しいIDとパスワードの入力があれば会員専用ページへ。
入力なし、入力間違いそれぞれのエラーメッセージを表示。
パスワードは●●で表示されるが「表示」ボタン押下でテキスト表示されるscriptプログラムを追加。
会員専用ページURLで直接ページへ飛べないようにサーブレットフィルターによってログイン済か否かのチェックをおこなうプログラムを作成した。
controllerパッケージ | ||||
---|---|---|---|---|
クラス名 | URL | メソッド | jsp | 内容 |
IndexServlet | /index | doGet | index.jsp | ログインしたユーザーの専用ページ |
LoginServlet | /login | doGet doPost | login.jsp | |
LogoutServlet | /logout | doGet | indexへリダイレクト | |
serviceパッケージ | ||||
LoginService | 正しいIDとパスワードか判定 | |||
filterパッケージ | ||||
AuthFilter | 対応URL: /* | ログイン済みか判定 ⇒ していない場合、loginにリダイレクト | ||
viewフォルダ | jspファイル | |||
WEB-INF/view | index.jsp | |||
login.jsp |
各ファイルの役割
IndexServlet
「index.jsp」を表示する役割。
「index.jsp」 はログインしているユーザーしか訪れることができない画面。
IndexServlet 自体にログイン済か否かを記述することもできる。
今回はfilterを用意しログイン状態の判断をおこなう。firterでログイン済と判断された場合に IndexServlet にリクエストが渡る。
LoginServlet
・ログインIDとパスワード を入力してログインボタンを押した際に
・ログインIDとパスワードが入力されているか(未入力か否か)のチェック。
・入力されている場合➡ LoginService を使って正しい組み合わせかどうかのチェック。
・問題がなければ index.jsp へ遷移(リダイレクト)する。
・問題があれば再度ログインフォームに戻る。
AuthFilter
indexServlet にリクエストが届く前にフィルターでログインしているか否かをチェックしてログインしていない場合はURLを変更する「リダイレクト」という処理をおこなう。
.jspファイルでJSTLを利用するため、WEB-INF/lib フォルダの中にjarファイルをコピーしておきます。
IndexServlet
- 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 IndexServlet
- */
- @WebServlet(“/index”)
- public class IndexServlet extends HttpServlet {
- private static final long serialVersionUID = 1L;
- /**
- *@see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
- */
- protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
- request.getRequestDispatcher(“/WEB-INF/view/index.jsp”).forward(request, response);
- }
- }
LoginServlet
- 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;import service.LoginService;
- /**
- *Servlet implementation class LoginServlet
- */
- @WebServlet(“/login”)
- public class LoginServlet extends HttpServlet {
- private static final long serialVersionUID = 1L;
- /**
- *@see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
- */
- protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
- request.getRequestDispatcher(“/WEB-INF/view/login.jsp”).forward(request, response);
- }
- /**
- *@see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
- */
- protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
- // 入力値の取得
- String id = request.getParameter(“id”);
- String pass = request.getParameter(“pass”);
- // 入力チェック
- boolean isValidated = true;
- String error = “”;
- if(id.isBlank() || pass.isBlank()) {
- isValidated = false;
- error = “未入力です”;
- }
- // 正しいID・パスワードかチェック
- if(isValidated && new LoginService().checkIdAndPass(id, pass) == false) {
- isValidated = false;
- error = “不正なIDパスワードです”;
- }
- if(isValidated) {
- // ログイン済みの証明
- request.getSession().setAttribute(“id”, id);
- response.sendRedirect(“index”);
- }
- else {
- request.setAttribute(“error”, error);
- request.getRequestDispatcher(“/WEB-INF/view/login.jsp”).forward(request, response);
- }
- }
- }
LoginServlet の解説
- // 入力値の取得
- String id = request.getParameter(“id”);
- String pass = request.getParameter(“pass”);
入力値の取得は request.getParameter メソッドでおこないます。(”id”)と(”pass”)と引き数を指定することでlogin.jspで指定したname=”id” name=”pass”と紐づきます。
login.jsp から取得した “id” と “pass” を String 型の変数に格納します。
- // 入力チェック
- boolean isValidated = true;
- String error = “”;
- if(id.isBlank() || pass.isBlank()) {
- isValidated = false;
- error = “未入力です”;
入力に問題があるか否かを判断する boolean 型の変数を用意します。
boolean 型の isValidated という変数にあらかじめ true を入れておき,入力値チェックの if 文でひっかかったらこの isValidated が false になるという仕組みにします。
if 文 の中身は .isBlank() で中身が未入力であるということを示します。 .isBlank() をつかってidとpassの中身の有無を判定します。
この if 文 にひっかかった場合(未入力ではなかった場合) isValidated を false にします。
String error = “”;
で String 型の error 変数を用意し、入力がされていない場合は「未入力です」と表示されるようにします。
- // 正しいID・パスワードかチェック
- if (isValidated && new LoginService().checkIdAndPass(id, pass) == false) {
- isValidated = false;
- error = “不正なIDパスワードです”;
- }
isValidated が true であるかどうかの判定をします。
この時点で「未入力」である場合( false )はこの if 文の中には入らず「未入力です」が表示できます。
isValidated && を記述しないと未入力の場合もこの if 文の中に入ってしまいます。
new LoginService().checkIdAndPass(id, pass)
で LoginService クラスをインスタンス化して checkIdAndPass メソッドの引数を受け取ります。LoginService クラスからはIDとパスワードが不正解の場合に false が返されるので == false とします。
そして isValidated 変数の中に false を代入します。
そしてエラーメッセージに「不正な ID かパスワードです」と表示されるようにします。
- if (isValidated) {
- // ログイン済みの証明
- request.getSession().setAttribute(“id”, id);
- response.sendRedirect(“index”);
- } else {
- request.setAttribute(“error”, error);
- request.getRequestDispatcher(“/WEB-INF/view/login.jsp”).forward(request, response);
会員専用ページに進むにあたって自分がログイン済であることを証明する必要があります。ログイン済である証を持ってindex.jsp へと進むために証を用意します。
request.getSession().setAttribute(“id”, id);
request.getSession()でセッションを取得して第一引数(セッションに預けるときの名前)に “id” 第二引き数に実際に預けたいデータの id を記述します。今回は jsp というデータを預けます。
1つめの if 文で「未入力か否か」を判定し、2つめの if 文で ID とパスワードの判定をおこないました。その2つの if 文から抜けてきた isValidated の中身は true のままとなります。その場合はリダイレクトの処理が実行され index.jsp へと遷移します。
else以降
LoginServlet で設定された変数 error を login.jsp でも利用できるように
request.setAttribute(“error”, error);
でセットします。
そしてそれ以外の場合(なんらかのエラーがあった場合)はエラーメッセージを表示して再度 login.jsp へと遷移します。
MVCの利用
LoginServlet にリクエストが送られてきます。 LoginServlet は全ての処理が終わってから画面表示のみを login.jsp に依頼することもできますが負担の軽減を図るために処理の一部を他のクラスに依頼することがあります。今回は LoginService というクラスを作って正しいIDとパスワードの組み合わせかどうかのチェックを依頼します。そして LoginServlet の方ではその結果のみを受け取るかたちにします。 LoginService クラスはデータベースと連携してその中にIDとパスワード が入っているかを判定する役割を持ちます。
このようにリクエストを受け取るcontrollerを「C」表示を担当するviewを「V」それ以外のmodelを「M」とする役割分担の仕組みをMVCといいます。
LogoutServlet
- 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 LogoutServlet
- */
- @WebServlet(“/logout”)
- public class LogoutServlet extends HttpServlet {
- private static final long serialVersionUID = 1L; /**
- *@see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
- */
- protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
- request.getSession().removeAttribute(“id”);
- response.sendRedirect(“login”);
- }
- }
LogoutServletコード解説
セッションに格納された loginID の情報を削除することが LogoutServlet の処理内容になります。
- request.getSession().removeAttribute(“id”);
- response.sendRedirect(“login”);
情報の破棄のしかたは2種類あります。
inbalidate
全ての情報を破棄する
removeAttribute
個別に破棄する
“id” の中の情報が破棄されたら login へリダイレクトします。
LoginService
- package service;
- public class LoginService {
- public boolean checkIdAndPass(String id, String pass) {
- if(id.equals(“jsp”) && pass.equals(“servlet”)) {
- return true;
- }
- else {
- return false;
- }
- }
- }
LoginServiceコード解説
- public boolean checkIdAndPass(String id, String pass) {
- if(id.equals(“jsp”) && pass.equals(“servlet”)) {
- return true;
- }
checkIdAndPass メソッドを用意します。
引数は文字列型の id と pass にし、戻り値は boolean 型とします。
このメソッドの中で if 文を使って判定をおこないます。
id.equals(“jsp”) && pass.equals(“servlet”)
id が “jsp”でかつ pass が”servlet” の場合に true を返します。
そしてそれ以外の場合に false を返します。
AuthFilter
サーブレットフィルタの作成
Eclipseでは「新規」➡「フィルター」でサーブレットフィルターを作成します。
- package filter;
- import java.io.IOException;
- import javax.servlet.Filter;
- import javax.servlet.FilterChain;
- import javax.servlet.FilterConfig;
- import javax.servlet.ServletException;
- import javax.servlet.ServletRequest;
- import javax.servlet.ServletResponse;
- import javax.servlet.annotation.WebFilter;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- /**
- *Servlet Filter implementation class AuthFilter
- */
- @WebFilter(“/*”)
- public class AuthFilter implements Filter {
- /**
- *Default constructor.
- */
- public AuthFilter() {
- // TODO Auto-generated constructor stub
- }
- /**
- *@see Filter#destroy()
- */
- public void destroy() {
- // TODO Auto-generated method stub
- }
- /**
- *@see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
- */
- public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
- // キャストによってHttpServletRequestとHttpServletResponseを取得
- var req = (HttpServletRequest) request;
- var res = (HttpServletResponse) response;
- String path = req.getServletPath();
- System.out.println(path);
- if(!path.equals(“/login”) &&
- !path.startsWith(“/css”)) {
- var obj = req.getSession().getAttribute(“id”);
- if(obj == null) {
- res.sendRedirect(“login”);
- return;
- }
- }
- // pass the request along the filter chain
- chain.doFilter(request, response);
- }
- /**
- *@see Filter#init(FilterConfig)
- */
- public void init(FilterConfig fConfig) throws ServletException {
- // TODO Auto-generated method stub
- }
- }
AuthFilterコード解説
- @WebFilter(“/*”)
「/*」 でこのフィルターが全てのURLに対して有効になるようにします。
({ “/index”, “/about”})←のように対応させたいものを記述しても可です。
- // キャストによってHttpServletRequestとHttpServletResponseを取得
- var req = (HttpServletRequest) request;
- var res = (HttpServletResponse) response;
doFilterメソッドの戻り値は「ServletRequest」型になっています。
Servlet ではdoGetやdoPostのリクエストの引数が「HttpServletRequest」型になっているので「HttpServletRequest」型へのキャストが必要になります。
サーブレットの引数と同じ型にキャストすることによって
変数 req を使って getSession することができ
変数 res を使って sendRedirect ができるようになります。
- String path = req.getServletPath();
- System.out.println(path);
- if(!path.equals(“/login”) &&
- !path.startsWith(“/css”)) {
@WebFilter(“/*”)
で全てのURLに対応しているので除外したいURLの情報を得るためにString 型の変数 path に個別にURL情報を取得して代入します。
- if(!path.equals(“/login”) &&
- !path.startsWith(“/css”)) {
URLがloginと「.startsWith」でcssで始まる場合に「!」でリダイレクトの処理をしないとなります。
URL情報の取得
URL情報の取得
http://localhost:8080①/MySystem②/MyServlet③/foo?④a=1&b=2
① コンテキストパスの取得:request.getContextPath()
② サーブレットパスの取得:request.getServletPath()
③ 拡張パス情報の取得:request.getPathInfo()
④ クエリ文字列の取得:request.getQueryString()
今回は②の情報を取得したいので「getServletPath()」を利用します。
- var obj = req.getSession().getAttribute(“id”);
セッションスコープに格納されている ” id ” を参照します。
戻り値はオブジェクト型となります。
(「jsp」 が入力されているかどうかを確認します)
- if(obj == null) {
- res.sendRedirect(“login”);
- return;
- }
obj == null はセッションの中に証明となる ” id ” が入っていない➡ログインしていない状態となります。
したがって null の場合は
res.sendRedirect(“login”)
で強制的にURLを ” login ” に変更します。
return
で処理を中断します。
index.jsp
- <%@ page pageEncoding=”UTF-8″%>
- <%@ taglib prefix=”c” uri=”http://java.sun.com/jsp/jstl/core” %>
- <%@ taglib prefix=”fmt” uri=”http://java.sun.com/jsp/jstl/fmt” %>
- <headの内容>
- <body>
- <h1>ようこそ<c:out value=”${id}” />さん</h1>
- <p><a href=”logout”>ログアウト</a></p>
- </body>
- </html>
index.jspコード解説
- <h1>ようこそ<c:out value=”${id}” />さん</h1>
<p>ようこそゲストさん<p>
を
<p>ようこそ<c:out value=” ${ id }” />さん<p>
と記述することで入力したIDを表示させることができます。
login.jsp
- <%@ page pageEncoding=”UTF-8″%>
- <%@ taglib prefix=”c” uri=”http://java.sun.com/jsp/jstl/core” %>
- <%@ taglib prefix=”fmt” uri=”http://java.sun.com/jsp/jstl/fmt” %>
- <headの内容>
- <body>
- <c:if test=”${!empty error}”>
- <p style=”color:red;”><c:out value=”${error}” /><p>
- </c:if>
- <form action=”” method=”post”>
- <p>ログインID
- <input type=”text” name=”id”>
- </p>
- <p>パスワード
- <input type=”password” name=”pass”>
- <button id=”show”>表示</button>
- </p>
- </form>
- <input type=”submit” value=”ログイン”>
- </form>
- <script
- src=”https://code.jquery.com/jquery-3.6.0.min.js”
- integrity=”sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=”
- crossorigin=”anonymous”></script>
- <script>
- $(document).ready(function(){
- $(“#show”).click(function(){
- if($(this).text() === “表示”) {
- $(this).prev().attr(“type”, “text”);
- $(this).text(“非表示”);
- }
- else {
- $(this).prev().attr(“type”, “password”);
- $(this).text(“表示”);
- }
- // 送信を防ぐ
- return false;
- });//click
- });//ready
- </body>
- </html>
login.jsp コード解説
- <c:if test=”${!empty error}”>
- <p><c:out value=”${error}” /><p>
- </c:if>
<c:if test=”${!empty error}”>の中の
${!empty error}
でエラーメッセージがある場合は……となります。
エラーメッセージがある場合は<p>の中でエラーメッセージを表示させます。
<c:out value=”error”/>
⇑ errorを表示する。
22行目以降のjQueryプログラムでボタンをクリックした際のパスワードテキストの表示/非表示が設定されています。