03-3.Spring Security 5の設定方法
概要
Spring Securityの設定方法を見ていきます。
ここでは、Spring Security5.0についての説明になります。
特にPasswordEncoderのお話は重要かと思いますのでお読みいただければと思います。
ここでは設定のみを記述します。先立って、通信シーケンスを記述したSpringSecurityの概要を読んでおいてもらえると理解しやすいです。
内部動作を知っておきたい場合は、こちらをご覧ください。
【補足】
このブログ全体に言えるのですが、Springの設定についてはXMLの形式で記述します。
というのは、最近はJavaConfigの記事が多くなりXMLの設定の記事が少なくなったことと、
XMLの記述から類推してJavaConfigは書けると思われるからです。
目標
まず、以下のサンプルの目標(ゴール)を示します。
ゴールはWEB上でログイン認証とアクセス制御を実現することです。
ログイン画面からID/PWを入力し、認証NGのときは認証エラー画面へ、認証OKのときはトップ画面へ遷移する。
そんな動作をするようにします。
※このサンプルでは*.htmlをSpringMVCの対象に設定しています。拡張子から何のフレームワークか推定できないようにするためです。
注意点(脆弱性cve-2018-1199)について
Spring Security は最新版を使用することをおすすめします。
Spring Security5.0.0は、認証を回避できる脆弱性(cve-2018-1199)が存在します。
現状では、この脆弱性に対処した5.0.1以上をお使いください。SpringFrameworkのバージョンは、5.0.3以上だそうです。
バージョン4以下についても同様の脆弱性があるようですので、各バージョンの最新版をご利用される方が良いかと思います。
<補足>
この脆弱性にSpring Sceurityがどのように対処したかというと、SpringSecurityがもともと持っているFirewallクラスを拡張し、拡張したクラスをデフォルトで
使用するようにしたようです。デフォルトになったクラスは、StrictHttpFirewall クラスです。
もし、SpringSecurityのFirewallの設定を変更するときは、脆弱性がデグレしないようにお気を付けください。
特に、この脆弱性は、ダブルスラッシュ(//)や親ディレクトリ指定(..)などを使用するようで、StrictHttpFirewallはデフォルトで
これを禁止してエラーにしています。
<ブログ>
https://spring.io/blog/2018/01/30/cve-2018-1199-spring-security-5-0-1-4-2-4-4-1-5-released
設定サンプル
<サンプルWEB設定ファイル: web.xml>
【web.xml】
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:/META-INF/spring/web/applicationContext.xml
classpath:/META-INF/spring/web/spring-security.xml
</param-value>
</context-param>
<!-- SpringSecurityのフィルター設定です -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>sample</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>sample</servlet-name>
<url-pattern>*.html</url-pattern>
<url-pattern>/echo</url-pattern>
</servlet-mapping>
<設定内容>
Spring Securityはフィルタですのでweb.xmlに設定します。
web.xmlにはフィルタ呼び出し用のフィルタを設定するところです。
実際の処理を記述したフィルタはSpringの設定ファイルに記述しておき、呼び出し用フィルタが記述したフィルタを呼び出します。
あとSpringSecurityと関係ないお話ですが、URLですが、*.htmlをSpring MVCのControllerの対象に設定しています。
<サンプルSpring設定ファイル: /WEB-INF/applicationContext.xml>
【applicationContext.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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop ="http://www.springframework.org/schema/aop"
xmlns:tx ="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- Jstl のviewResolver
Controllerを介さず、そのままjspを実行するURLパスのための設定。
-->
<mvc:view-controller path="/*.html" />
<bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
SpringSecurityとしては特に重要なファイルではありません。
jspファイルを*.htmlにマッピングする最低限のサンプルを載せておきます。
<サンプルSecurity設定ファイル: /WEB-INF/spring-security.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:util="http://www.springframework.org/schema/util"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:sec="http://www.springframework.org/schema/security"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<!-- JSPタグでSpringSecurity式を使用したい場合に以下を定義する -->
<bean id="webexpressionHandler"
class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler"/>
<!-- SpringSecurity5のメイン設定 -->
<sec:http request-matcher="ant" auto-config="true" use-expressions="false" >
<!-- 認可処理(閲覧権限)でエラーになったときに表示するエラー画面 -->
<sec:access-denied-handler error-page="/error/403.html"/>
<!-- 認証なしで表示する画面の設定(これらを設定しないとどこにもアクセスできないWEBになります) -->
<sec:intercept-url pattern="/error/**" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<sec:intercept-url pattern="/login.html" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<sec:intercept-url pattern="/include/**" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<!-- 認証後(ログイン後)に表示する画面のロール設定 -->
<sec:intercept-url pattern="/top.html" access="ROLE_ADMIN,ROLE_USER"/>
<sec:intercept-url pattern="/user/**" access="ROLE_ADMIN,ROLE_USER"/>
<sec:intercept-url pattern="/**" access="ROLE_ADMIN"/>
<!-- ログイン設定 -->
<sec:form-login login-processing-url="/j_spring_security_check"
login-page="/login.html" default-target-url="/top.html"
always-use-default-target="false"
authentication-failure-url="/login.html?error=true" />
<!-- ログアウト設定 -->
<sec:logout logout-url="/logout" invalidate-session="true"/>
<sec:anonymous granted-authority="ROLE_ANONYMOUS"/>
<!-- CSRF対策用のトークンを発行することを宣言します(SprinSecurity3.2以降の機能です) -->
<sec:csrf request-matcher-ref="csrfTarget"/>
<!-- HTTPヘッダで可能なセキュリティ対策
ここで設定できる内容は基本的にはApacheの設定ファイルの記述でも解決可能なことです。
アプリ側で対策するか、Apache側で対策するかは決めの問題です。
-->
<sec:headers>
<!-- クリックジャッキング対策 -->
<sec:frame-options policy="DENY"/>
<!-- XSS攻撃対策 -->
<sec:xss-protection />
</sec:headers>
</sec:http>
<!--
認証手形発行所の設定。ログインID/PWの情報をメモリから取得する方法です。SQL文を指定してDBから取得する方法もあります。
PasswordEncoderに DelegatingPasswordEncoderを設定したので、パスワードの先頭に識別子{}を付けています。
DBに保存するPWにもつける必要があります。
-->
<sec:user-service>
<sec:user name="taro" password="{noop}taro" authorities="ROLE_ADMIN"/>
<sec:user name="suzuki" password="{bcrypt}$2a$10$OqRpk8pT5OxqxzhrhFWQPeiS/NU7AI18F/w89I2cXCb3C66Q4FGBO" authorities="ROLE_USER"/>
</sec:user-service>
<!--
SpringSecurity5からPasswordEncoderが必須になったらしい。
ここで設定したFactoryは、メジャーなEncoderを設定したDelegatingPasswordEncoderを返します。
-->
<bean id="passwordEncoder" class="org.springframework.security.crypto.factory.PasswordEncoderFactories" factory-method="createDelegatingPasswordEncoder"/>
<!--
CSRFトークンチェックを行う画面を指定する。できれば全てが望ましいが、ここでは完了画面(**_complete.html)だけにした 。
注意点としてはURLのパス名のルールを決めておかないと以下のようなAntパス形式で指定できない点である。
【補足】
ログイン画面のCSRF対策はログイン後のコンテンツの内容によってはあまり意味のないものだが、念のためしておいた方がよいと思います。
以下で設定しています(/j_spring_security_check)。
-->
<bean id="csrfTarget" class="org.springframework.security.web.util.matcher.OrRequestMatcher">
<constructor-arg>
<list>
<bean class="org.springframework.security.web.util.matcher.AntPathRequestMatcher">
<constructor-arg value="/**_complete.html" />
</bean>
<bean class="org.springframework.security.web.util.matcher.AntPathRequestMatcher">
<constructor-arg value="/j_spring_security_check" />
</bean>
</list>
</constructor-arg>
</bean>
</beans>
httpタグなどの設定はほとんどSpringSecurity4から変わっていません。
concurrent-session-controlタグは、session-managementタグの配下に記述するように変更されたりと、
タグの記述位置が一部変わりましたが、そのような変更もそれほど多くないのでバージョン4の記述方法がおおよそそのまま使用できます。
【SpringSecurity5からの注意点】
実は、上記の設定で「passwordEncoder」の設定が必須になりました。
これは重要なことですので、こちらの記事に記述していますのでお読みいただければ幸いです。
<ログイン画面サンプル: /WEB-INF/jsp/login.jsp>
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" %>
<%@ page session="false" %><!-- 勝手にSessionオブジェクトを生成しないようにする -->
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>ログイン</title>
</head>
<body>
<h1>ログイン画面</h1>
<c:if test="${param.error eq 'true'}">
<font color="red"> 入力されたユーザID、パスワードは不正です。<br></font>
</c:if>
<br>
<form id="f" action="<c:url value='/j_spring_security_check'/>" method="post">
<table class="login">
<tr>
<th>ログインID</th>
<td><input type="text" name="username" ></td>
</tr>
<tr>
<th>ログインPW</th>
<td><input type="text" id="passwd" name="password" ></td>
</tr>
</table>
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}">
<input type="submit" name="login" value="ログイン" >
</form>
</body>
</html>
特筆することはないですが、赤字の部分のパラメタ名等は決まっていますのでご注意ください。
【補足】
ちなみに、ID/PWのパラメタ名を変えたり、認証成功後に遷移するURLをパラメタで指定することは可能です。
遷移後のURLをパラメタ指定したい場合は、UsernamePasswordAuthenticationFilterのSuccessHandlerプロパティをご確認ください。
<権限エラー画面サンプル: /WEB-INF/jsp/error/403.jsp>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-31j">
<title>Insert title here</title>
</head>
<body>
閲覧権限がありません。<br>
<input type="button" onclick="javascript: history.back();" value="戻る">
</body>
</html>
<トップ画面サンプル: /WEB-INF/jsp/top.jsp>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-31j">
<title>Insert title here</title>
</head>
<body>
トップページです。<br>
ログイン認証成功おめでとうございます。
</body>
</html>
上記のサンプルを起動するとログイン画面が表示されます。
ログイン画面で ID/PW として、 taro/taro を入力してサブミットするとトップページに遷移します。
設定ファイルのタグのそれぞれの意味は記述しましたで、読んでいただくとある程度自分がやりたいことができるのではないでしょうか。
内部動作の記事も参考にしていただければ拡張するポイントが把握しやすくなると思います(拡張のサンプル)。
Created Date: 2018/08/18