スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

Spring MVC ログインユーザー権限によって表示を切り替える

前回の記事で検索一覧画面から編集画面を呼び出せるように対応し、過去の記事にて、Spring Securityを使ってログイン画面も作成した。

今回は上記を踏まえて、ログインユーザーの権限によって、必要な機能のみ使用できるよう表示を切り替えてみたいと思う。

ログインユーザーと使用可能な機能については下記のようにする。
 ①ユーザー名:testadmin 検索、新規登録、編集
 ②ユーザー名:testuser 検索のみ

なお、今回の記事でベースとなっている過去の記事は以下の通り
■前回の記事
Spring MVC 一覧から各行の編集画面を呼び出す

■Spring Securityに関する過去の記事
Spring MVC Spring Securityを使ってみる(その2)

◎動作検証にあたっての各バージョンは以下の通り
  • SpringFramework 3.2.8.RELEASE
  • Java 1.7
  • Tomcat 7.0

1.security-db.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:sec="http://www.springframework.org/schema/security"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

<sec:http auto-config="true">
<sec:intercept-url pattern="/book/list/**" access="ROLE_ADMIN,ROLE_USER"/>
<sec:intercept-url pattern="/book/edit/**" access="ROLE_ADMIN"/>
<sec:form-login login-page="/login.jsp" default-target-url="/book"
authentication-failure-url="/login.jsp?error=true"/>
<sec:logout logout-url="/logout" logout-success-url="/login.jsp" />
<sec:access-denied-handler error-page="/error.jsp" />
</sec:http>

<sec:authentication-manager>
<sec:authentication-provider>
<sec:jdbc-user-service data-source-ref="dataSource"/>
</sec:authentication-provider>
</sec:authentication-manager>

<bean id="webExpressionHandler"
class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler" />

</beans>

今回のログインユーザー権限による画面切り替えにあたり、Spring Securityの設定ファイルにてアクセス可能なURLを設定する。
14行目の「pattern="/book/list/**"」は一覧と検索に該当するURLで、ROLE_ADMIN権限またはROLE_USER権限を持ったユーザーがアクセス可能となる。一方15行目の「pattern="/book/edit/**」は、新規登録、編集に該当するURLで、ROLE_ADMIN権限のユーザーのみアクセス可能となる。「pattern="/book/edit/**」にROLE_USER権限ユーザーがアクセスした場合は、19行目の「error-page="/error.jsp"」にてエラーページに遷移する。ワイルドカード「*」を使用することにより、すべてのURLを列挙するのではなく、「list」配下と「edit」配下にまとめている。また、本設定をしない場合、画面にリンクが表示されなくても、直にURLをたたけば遷移してしまうため本設定は必要となる。

2.ログインユーザー情報

CREATE TABLE USERS (
USERNAME VARCHAR(10) NOT NULL,
PASSWORD VARCHAR(32) NOT NULL,
ENABLED SMALLINT,
PRIMARY KEY(USERNAME)
);

CREATE TABLE AUTHORITIES (
USERNAME VARCHAR(10) NOT NULL,
AUTHORITY VARCHAR(10) NOT NULL,
FOREIGN KEY(USERNAME) REFERENCES USERS
);

INSERT INTO USERS (USERNAME,PASSWORD,ENABLED) VALUES ('testadmin','db1',1);
INSERT INTO USERS (USERNAME,PASSWORD,ENABLED) VALUES ('testuser','db2',1);
INSERT INTO USERS (USERNAME,PASSWORD,ENABLED) VALUES ('testguest','db3',1);

INSERT INTO AUTHORITIES (USERNAME,AUTHORITY) VALUES ('testadmin','ROLE_ADMIN');
INSERT INTO AUTHORITIES (USERNAME,AUTHORITY) VALUES ('testadmin','ROLE_USER');
INSERT INTO AUTHORITIES (USERNAME,AUTHORITY) VALUES ('testuser','ROLE_USER');
INSERT INTO AUTHORITIES (USERNAME,AUTHORITY) VALUES ('testguest','ROLE_GUEST');

18,19行目で、testadminユーザーにROLE_ADMIN権限とROLE_USER権限を付与している。このように、ひとつのユーザーに複数権限を付与することが可能である。また、21行目にtestguestを設定しているが、こちらはログインの段階でエラーとなるユーザーである。

3.BookController.java(関連箇所のみ掲載)

@Controller
@RequestMapping("/book")
public class BookController {

//変数定義省略
  
//初期
@RequestMapping(method = RequestMethod.GET)
public String index() {
//省略
}

//一覧
@RequestMapping(value = "/list", method = RequestMethod.GET)
public String list(BookSearchForm form, Model model) {
//省略
}

//検索
@RequestMapping(value = "/list/search", method = RequestMethod.GET)
public String search(BookSearchForm form, Model model) {
//省略
}

//新規登録
@RequestMapping(value = "/edit/new", method = RequestMethod.GET)
public String add (BookForm form) {
//省略
}

//編集
@RequestMapping(value = "/edit/{id}", method = RequestMethod.GET)
public String edit (@PathVariable("id") long bookId, BookForm form, Model model) {
//省略
}

//更新
@RequestMapping(value = "/edit/update", method = RequestMethod.POST)
public String update(@Valid BookForm form, BindingResult result, Model model,
@RequestParam(value="uploadFile",required=false) MultipartFile file) {
//省略
}

//画像取得
@RequestMapping(value = "/edit/cover/{id}", method = RequestMethod.GET)
@ResponseBody
public byte[] getCover (@PathVariable("id") long bookId) {
//省略
}
}

「1.security-db.xml」にて説明した通り、URLは「list」または「edit」のどちらかに属するように変更している。

4.mvc-config.xml(関連箇所のみ掲載)

<mvc:resources mapping="/css/**" location="/WEB-INF/css/" />
<mvc:resources mapping="/js/**" location="/WEB-INF/js/" />
<mvc:resources mapping="/img/**" location="/WEB-INF/img/" />

今回、画面表示切替の他にもうひとつ、改善を行う。画像ファイルのアップロードは任意としているため、ファイルが存在しない場合、画面上「×」で表示される。見栄えが良くないため、未設定の場合はこちらで用意した「No Image」画像を表示するよう対応する。そのために、3行目を追加している。詳細については、下記を参考にしてほしい。

■リソースファイル使用に関する過去の記事
Spring MVCのJSPにJavaScriptやCSSを適用する

5.list.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<%@ taglib uri="http://www.springframework.org/security/tags" prefix="sec"%>
<link href="<c:url value="/css/bootstrap.min.css" />" rel="stylesheet">
<link href="<c:url value="/css/bootstrap-theme.min.css" />" rel="stylesheet">
<script src="<c:url value="/js/bootstrap.min.js" />"></script>
<title>list</title>
</head>
<body>
<spring:url value="/book/edit/cover" var="varBookCoverUrl"/>
<spring:url value="/book/edit/new" var="varBookAddUrl"/>
<spring:url value="/book/edit" var="varBookEditUrl"/>
<spring:message code="bookId" var="varBookId"/>
<spring:message code="bookName" var="varBookName"/>
<spring:message code="price" var="varPrice"/>
<spring:message code="cover" var="varCover"/>
<spring:message code="button.search" var="varButtonSearch"/>
<spring:message code="link.add" var="varLinkAdd"/>
<spring:message code="link.edit" var="varLinkEdit"/>

<div class="container-fluid">
<div class="row">
<form:form
action="${pageContext.request.contextPath}/book/list/search"
method="get" modelAttribute="bookSearchForm">
<form:label path="bookName" class="col-sm-3 text-right">${varBookName}</form:label>
<form:input path="bookName" class="col-sm-6"/>
<div class="col-sm-3">
<input type="submit" value="${varButtonSearch}" class="btn btn-info btn-sm"/>
</div>
</form:form>
</div>
<hr/>
<div class="row">
<div class="col-sm-9"></div>
<div class="col-sm-3 text-right">
<!-- 1.ROLE_ADMIN と ROLE_USERの両方必要 → testadminのみ表示 -->
<sec:authorize ifAllGranted="ROLE_ADMIN,ROLE_USER">
<a href="${varBookAddUrl}">${varLinkAdd}</a>
</sec:authorize>

<!-- 2.ROLE_ADMIN と ROLE_USERのどちらか必要 → testadminとtestuser表示 -->
<!--
<sec:authorize ifAnyGranted="ROLE_ADMIN,ROLE_USER">
<a href="${varBookAddUrl}">${varLinkAdd}</a>
</sec:authorize>
-->
<!-- 3.ROLE_ADMIN以外 → testuserのみ表示 -->
<!--
<sec:authorize ifNotGranted="ROLE_ADMIN">
<a href="${varBookAddUrl}">${varLinkAdd}</a>
</sec:authorize>
-->
</div>
</div>
<table class="table table-striped table-bordered">
<tr>
<th>${varBookId}</th>
<th>${varCover}</th>
<th>${varBookName}</th>
<th>${varPrice}</th>
<th></th>
</tr>
<c:forEach items="${books}" var="book">
<tr>
<td>${book.bookId}</td>
<c:set value="${fn:length(book.cover)}" var="varCoverLen"/>
<c:if test="${varCoverLen > 0}">
<td><img src="${varBookCoverUrl}/${book.bookId}"></td>
</c:if>
<c:if test="${varCoverLen == 0}">
<td><img src="<c:url value="/img/blank.jpg" />"/></td>
</c:if>
<td>${book.bookName}</td>
<td>${book.price}</td>
<td>
<sec:authorize ifAllGranted="ROLE_ADMIN,ROLE_USER">
<a href="${varBookEditUrl}/${book.bookId}">${varLinkEdit}</a>
</sec:authorize>
</td>
</tr>
</c:forEach>
</table>
</div>
</body>
</html>

前回も全掲載したが、今回も載せておく。
URLの変更にあわせて、18,19行目の「spring:url」タグのvalue属性値を変更している。
46行目、85行目については、新規登録、編集の表示判定を行っている。ともに「sec:authorize ifAllGranted="ROLE_ADMIN,ROLE_USER"」としているが、これはROLE_ADMIN権限とROLE_USER権限の両方を持ったユーザーのみリンクが表示可能となる。今回の場合、testadminユーザーとなる。また、コメント化しているが、「ifAllGranted」の他に「ifAnyGranted」と「ifNotGranted」がある。仮に「sec:authorize ifAnyGranted="ROLE_ADMIN,ROLE_USER"」とした場合、ROLE_ADMIN権限とROLE_USER権限のどちらかを持ったユーザーでよいため、testadmin、testuserのどちらでも表示される。「sec:authorize ifNotGranted="ROLE_ADMIN"」とした場合は、お分かりだと思うが、testuserのみリンクが表示されることになる。

もうひとつの課題である「No Image」画像表示については、75行目にて「c:set value="${fn:length(book.cover)}"」として、配列長を取得し、変数varCoverLenに代入している。「fn:length」を使用するために、8行目「taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"」を追加している。「c:if test="${not Empty book.cover}"」を使用してみたが、nullか空文字かの判定となるため、空配列の判定には適切ではなく、配列長で判定することにした。ちなみに空配列の場合、0となる。変数に代入した後は、0か否かを判定するだけとなる。補足として、79行目を見ると分かるようにイコールは「 == 」で判定する。「=」ではない。

6.画面
①検索画面(testadminの場合)
ADMIN_TOP.png

「新規登録」と「編集」リンクが表示されている。


②検索画面(testuserの場合)
USER_TOP.png

「新規登録」と「編集」リンクが表示されていない。


③エラー(testuserが編集のURLを直接設定した場合)
USER_ERROR.png


今回の対応で、結果的にURLを考える良い機会となった。

■過去のSpring関連記事
Spring関連記事 Index

スポンサーサイト

コメントの投稿

非公開コメント

プロフィール

bookmount8

Author:bookmount8
システムエンジニア。サーバーサイドでjavaを扱うことが多い。最近は、ミドルやフロント周りも関心あり。

最新記事
カテゴリ
検索フォーム
最新コメント
月別アーカイブ
これまでの訪問者数
ブロとも申請フォーム

この人とブロともになる

RSSリンクの表示
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。