Spring Boot で書籍の貸出状況確認・貸出申請する Web アプリケーションを作る ( その33 )( 貸出申請画面の作成4 )
概要
Spring Boot で書籍の貸出状況確認・貸出申請する Web アプリケーションを作る ( その32 )( 貸出申請画面の作成3 ) の続きです。
- 今回の手順で確認できるのは以下の内容です。
- 貸出申請画面の作成
- 最後に表示した貸出申請の ID を Cookie に保存し、次回ログイン時に前回表示していた貸出申請画面を表示する機能の実装
- 貸出申請画面の作成
参照したサイト・書籍
Class CookieGenerator
http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/util/CookieGenerator.htmlTERASOLUNA Server Framework for Java (5.x) Development Guideline 5.0.1.RELEASE documentation - 4.4.1.4.9. Cookieから値を取得する
http://terasolunaorg.github.io/guideline/5.0.1.RELEASE/ja/ImplementationAtEachLayer/ApplicationLayer.html#cookieTERASOLUNA Server Framework for Java (5.x) Development Guideline 5.0.1.RELEASE documentation - 4.4.1.4.10. Cookieに値を書き込む
http://terasolunaorg.github.io/guideline/5.0.1.RELEASE/ja/ImplementationAtEachLayer/ApplicationLayer.html#controller-method-argument-cookiewrite-labelWhat are the differences between Helper and Utility classes?
http://stackoverflow.com/questions/12192050/what-are-the-differences-between-helper-and-utility-classes- helper クラスと utility クラスを使い分けるルールのようなものがあるのか調べた時に参照しました。
目次
- Spring Boot に Cookie 用のクラスがあるのか?
- Cookie 用クラス CookieLastLendingAppId の作成
- ユーティリティクラス CookieUtils の作成
- LendingappController クラスの変更
- UrlAfterLoginHelper クラスの変更
- RoleAwareAuthenticationSuccessHandler クラスの変更
- 動作確認
- 次回は。。。
- メモ書き
手順
Spring Boot に Cookie 用のクラスがあるのか?
Spring Framework に org.springframework.web.util.CookieGenerator というクラスが用意されており、このクラスを継承して Cookie 用のクラスを作成すればよさそうです。ただし CookieGenerator の Javadoc には使用例として org.springframework.web.servlet.i18n.CookieLocaleResolver, org.springframework.web.servlet.theme.CookieThemeResolver が記載されているのですが、Google で検索するとほとんど使用例が見つかりませんでした。CookieGenerator の実装を見た感じでは普通に使えそうに思えるのですが、使われていないのでしょうか?
Spring Framework に用意されているのであれば利用したいので、今回は CookieGenerator クラスを継承して Cookie 用クラスを作成することにします。以下の仕様で実装します。
- 貸出申請画面を表示した時に未申請の状態であれば URLパラメータに指定された貸出申請ID を LastLendingAppId Cookie に保存します。
- 「申請」ボタンを押して申請完了したら LastLendingAppId Cookie を削除します。
- URL を指定した場合ではなく普通にログイン画面を表示してログインした時に LastLendingAppId Cookie が存在したら貸出申請画面を表示するようにします。
- LastLendingAppId Cookie の保存期間は3日間にします。
Cookie 用クラス CookieLastLendingAppId の作成
src/main/java/ksbysample/webapp/lending の下に cookie パッケージを作成します。
src/main/java/ksbysample/webapp/lending/cookie の下に CookieLastLendingAppId.java を作成します。作成後、リンク先の内容 に変更します。
ユーティリティクラス CookieUtils の作成
CookieGenerator の継承クラスに @Component アノテーションを付加したくないのと、CookieGenerator の継承クラスを使用するのに都度インスタンスを生成する処理を記述するのを避けたいので、ユーティリティクラスを作成します。
src/main/java/ksbysample/webapp/lending/util の下に cookie パッケージを作成します。
src/main/java/ksbysample/webapp/lending/util/cookie の下に CookieUtils.java を作成します。作成後、リンク先の内容 に変更します。
LendingappController クラスの変更
- src/main/java/ksbysample/webapp/lending/web/lendingapp の下の LendingappController.java を リンク先の内容 に変更します。
UrlAfterLoginHelper クラスの変更
src/main/java/ksbysample/webapp/lending/config の下の Constant.java を リンク先の内容 に変更します。
src/main/java/ksbysample/webapp/lending/helper/url の下の UrlAfterLoginHelper.java を リンク先の内容 に変更します。
RoleAwareAuthenticationSuccessHandler クラスの変更
- src/main/java/ksbysample/webapp/lending/security の下の RoleAwareAuthenticationSuccessHandler.java を リンク先の内容 に変更します。
動作確認
動作確認します。データは Spring Boot で書籍の貸出状況確認・貸出申請する Web アプリケーションを作る ( その31 )( 貸出申請画面の作成2 ) で作成した貸出申請ID = 105 のデータを使用します。lending_app.status の値が 3 になっている場合には 2 に変更しておきます。
また今回は Cookie の作成状況を確認したいので IE ではなく Chrome を使用します。
Gradle projects View から bootRun タスクを実行して Tomcat を起動します。
最初に貸出申請画面にアクセスした時に未申請の場合には LastLendingAppId Cookie が作成されることを確認します。
ブラウザを起動し http://localhost:8080/lendingapp?lendingAppId=105 へアクセスします。最初はログイン画面が表示されますので ID に "tanaka.taro@sample.com"、Password に "taro" を入力して、「次回から自動的にログインする」をチェックせずに「ログイン」ボタンをクリックします。
貸出申請画面が表示され、LastLendingAppId Cookie が有効期限付きで作成されていることが確認できます。
次にログアウトしてから単にログイン画面からログインした場合に最後にアクセスした貸出申請画面が表示されることを確認します。
画面右上の「ログアウト」メニューをクリックしてログアウトします。その後ログインします。
ログイン後に http://localhost:8080/lendingapp?lendingAppId=105 の URL にリダイレクトし、貸出申請画面が表示されることを確認します。
「申請」ボタンをクリックして申請完了したら LastLendingAppId Cookie が削除されることを確認します。
上の画面の状態からいくつかで「申請する」を選択して申請理由を記入した後、「申請」ボタンをクリックします。
LastLendingAppId Cookie が削除されていることを確認します。
最後に LastLendingAppId Cookie がない状態でログインした場合には貸出申請画面へ遷移しないことを確認します。
画面右上の「ログアウト」メニューをクリックしてログアウトします。その後ログインします。
"tanaka.taro@sample.com" でログインすると ROLE_ADMIN が付与されているので検索対象図書館登録画面が表示されることが確認できます。
Ctrl+F2 を押して Tomcat を停止します。
一旦 commit します。
次回は。。。
「一時保存」ボタンクリック時の処理を実装する予定です。
メモ書き
helper クラスと utility クラスを使い分けるルールのようなものがあるのか?
今回も CookieUtils クラスを作成する時に helper クラスにするか utility クラスにするか迷ったので調べてみました。ついでに自分なりの基準を1度決めておきたいと思います。
自分が見つけた Web ページでは What are the differences between Helper and Utility classes? の回答が一番分かりやすそうに思えました。回答では特に基準はないとも書かれていましたが、このページの内容を元に今後は以下の基準で作成することにしたいと思います。
- utility クラス
- helper クラス
上の基準だと VelocityUtils は helper クラスにするのが妥当なので、後で変更したいと思います。
ソースコード
CookieLastLendingAppId.java
package ksbysample.webapp.lending.cookie; import org.springframework.web.util.CookieGenerator; public class CookieLastLendingAppId extends CookieGenerator { public static final String COOKIE_NAME = "LastLendingAppId"; private static final Integer COOKIE_MAX_AGE = 24 * 60 * 60 * 3; // 3日間 public CookieLastLendingAppId() { setCookieName(COOKIE_NAME); setCookieMaxAge(COOKIE_MAX_AGE); } }
CookieUtils.java
package ksbysample.webapp.lending.util.cookie; import org.apache.commons.lang3.StringUtils; import org.springframework.web.util.CookieGenerator; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Arrays; import java.util.Optional; public class CookieUtils { public static <T extends CookieGenerator> void addCookie(Class<T> clazz, HttpServletResponse response, String cookieValue) { try { T cookieGenerator = clazz.newInstance(); cookieGenerator.addCookie(response, cookieValue); } catch (InstantiationException | IllegalAccessException e) { throw new RuntimeException(e); } } public static <T extends CookieGenerator> void removeCookie(Class<T> clazz, HttpServletResponse response) { try { T cookieGenerator = clazz.newInstance(); cookieGenerator.removeCookie(response); } catch (InstantiationException | IllegalAccessException e) { throw new RuntimeException(e); } } public static String getCookieValue(String cookieName, HttpServletRequest request) { Optional<String> result = Optional.empty(); if (request != null) { Cookie[] cookies = request.getCookies(); result = Arrays.asList(cookies).stream() .filter(cookie -> StringUtils.equals(cookie.getName(), cookieName)) .map(cookie -> cookie.getValue()) .findFirst(); } return result.orElse(null); } }
LendingappController.java
package ksbysample.webapp.lending.web.lendingapp; import ksbysample.webapp.lending.cookie.CookieLastLendingAppId; import ksbysample.webapp.lending.entity.LendingApp; import ksbysample.webapp.lending.entity.LendingBook; import ksbysample.webapp.lending.exception.WebApplicationRuntimeException; import ksbysample.webapp.lending.helper.message.MessagesPropertiesHelper; import ksbysample.webapp.lending.util.cookie.CookieUtils; import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import javax.servlet.http.HttpServletResponse; import java.util.List; import static ksbysample.webapp.lending.values.LendingAppStatusValues.UNAPPLIED; @Controller @RequestMapping("/lendingapp") public class LendingappController { @Autowired private LendingappService lendingappService; @Autowired private MessagesPropertiesHelper messagesPropertiesHelper; @Autowired private LendingappFormValidator lendingappFormValidator; @InitBinder(value = "lendingappForm") public void initBinder(WebDataBinder binder) { binder.addValidators(lendingappFormValidator); } @RequestMapping public String index(@Validated LendingappParamForm lendingappParamForm , BindingResult bindingResultForLendingappParamForm , LendingappForm lendingappForm , HttpServletResponse response) { if (bindingResultForLendingappParamForm.hasErrors()) { throw new WebApplicationRuntimeException( messagesPropertiesHelper.getMessage("LendingappForm.lendingAppId.emptyerr", null)); } // 画面に表示するデータを取得する setDispData(lendingappParamForm.getLendingAppId(), lendingappForm); // 未申請の場合には LastLendingAppId Cookie に貸出申請ID をセットする if (StringUtils.equals(lendingappForm.getLendingApp().getStatus(), UNAPPLIED.getValue())) { CookieUtils.addCookie(CookieLastLendingAppId.class , response, String.valueOf(lendingappParamForm.getLendingAppId())); } return "lendingapp/lendingapp"; } @RequestMapping(value = "/apply", method = RequestMethod.POST) public String apply(@Validated LendingappForm lendingappForm , BindingResult bindingResult , HttpServletResponse response) { if (bindingResult.hasErrors()) { return "lendingapp/lendingapp"; } // 入力された内容で申請する lendingappService.apply(lendingappForm); // 画面に表示するデータを取得する setDispData(lendingappForm.getLendingApp().getLendingAppId(), lendingappForm); // LastLendingAppId Cookie を削除する CookieUtils.removeCookie(CookieLastLendingAppId.class, response); return "lendingapp/lendingapp"; } @RequestMapping(value = "/temporarySave", method = RequestMethod.POST) public String temporarySave() { return "lendingapp/lendingapp"; } private void setDispData(Long lendingAppId, LendingappForm lendingappForm) { LendingApp lendingApp = lendingappService.getLendingApp(lendingAppId); List<LendingBook> lendingBookList = lendingappService.getLendingBookList(lendingAppId); lendingappForm.setLendingApp(lendingApp); lendingappForm.setLendingBookList(lendingBookList); } }
- index メソッドに、未申請の場合には LastLendingAppId Cookie に貸出申請ID をセットする処理を追加します。
- apply メソッドに、LastLendingAppId Cookie を削除する処理を追加します。
Constant.java
package ksbysample.webapp.lending.config; public class Constant { /* * RabbitMQ Queue一覧 */ public static final String QUEUE_NAME_INQUIRING_STATUSOFBOOK = "InquiringStatusOfBookQueue"; /* * URL一覧 */ public static final String URL_ADMIN_LIBRARY = "/admin/library"; public static final String URL_LENDINGAPP = "/lendingapp"; /* * ログイン後ページのURL */ public static final String URL_AFTER_LOGIN_FOR_ROLE_ADMIN = URL_ADMIN_LIBRARY; }
- 定数 URL_LENDINGAPP を追加します。
UrlAfterLoginHelper.java
package ksbysample.webapp.lending.helper.url; import ksbysample.webapp.lending.config.Constant; import ksbysample.webapp.lending.config.WebSecurityConfig; import ksbysample.webapp.lending.cookie.CookieLastLendingAppId; import ksbysample.webapp.lending.util.cookie.CookieUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import javax.servlet.http.HttpServletRequest; public class UrlAfterLoginHelper { public static String getUrlAfterLogin(Authentication authentication) { return getUrlAfterLogin(authentication, null); } public static String getUrlAfterLogin(Authentication authentication, HttpServletRequest request) { String targetUrl = WebSecurityConfig.DEFAULT_SUCCESS_URL; // 特定の権限を持っている場合には対応するURLへリダイレクトする GrantedAuthority roleAdmin = new SimpleGrantedAuthority("ROLE_ADMIN"); if (authentication.getAuthorities().contains(roleAdmin)) { // 管理権限 ( ROLE_ADMIN ) を持っている場合には検索対象図書館登録画面へ遷移させる targetUrl = Constant.URL_AFTER_LOGIN_FOR_ROLE_ADMIN; } // LastLendingAppId Cookie に貸出申請ID をセットされている場合には貸出申請画面へリダイレクトさせる String cookieLastLendingAppId = CookieUtils.getCookieValue(CookieLastLendingAppId.COOKIE_NAME, request); if (StringUtils.isNotBlank(cookieLastLendingAppId)) { targetUrl = String.format("%s?lendingAppId=%s", Constant.URL_LENDINGAPP, cookieLastLendingAppId); } return targetUrl; } }
public static String getUrlAfterLogin(Authentication authentication, HttpServletRequest request)
メソッドを追加します。public static String getUrlAfterLogin(Authentication authentication)
メソッドの処理をこちらへ移動した後、LastLendingAppId Cookie に貸出申請ID をセットされている場合には貸出申請画面へリダイレクトさせる処理を追加します。public static String getUrlAfterLogin(Authentication authentication)
メソッドはpublic static String getUrlAfterLogin(Authentication authentication, HttpServletRequest request)
を第2引数は null で呼び出すように変更します。
RoleAwareAuthenticationSuccessHandler.java
package ksbysample.webapp.lending.security; import ksbysample.webapp.lending.cookie.CookieLastLendingAppId; import ksbysample.webapp.lending.helper.url.UrlAfterLoginHelper; import ksbysample.webapp.lending.util.cookie.CookieUtils; import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; import org.springframework.security.web.savedrequest.HttpSessionRequestCache; import org.springframework.security.web.savedrequest.RequestCache; import org.springframework.security.web.savedrequest.SavedRequest; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class RoleAwareAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { private RequestCache requestCache = new HttpSessionRequestCache(); @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException { // ログイン画面以外のURLを指定してアクセスされていた場合には、処理を SavedRequestAwareAuthenticationSuccessHandler へ委譲する SavedRequest savedRequest = requestCache.getRequest(request, response); if (savedRequest != null) { super.onAuthenticationSuccess(request, response, authentication); return; } String targetUrl = UrlAfterLoginHelper.getUrlAfterLogin(authentication, request); clearAuthenticationAttributes(request); getRedirectStrategy().sendRedirect(request, response, targetUrl); } }
UrlAfterLoginHelper.getUrlAfterLogin
の第2引数にrequest
を追加します。
履歴
2015/11/29
初版発行。
2015/12/03
* 「helper クラスと utility クラスを使い分けるルールのようなものがあるのか?」の内容をもう少し判断しやすい基準に変更しました。