6.3. 認証¶
Caution
本バージョンの内容は既に古くなっています。最新のガイドラインはこちらからご参照ください。
目次
6.3.1. Overview¶
本節では、Spring Securityで提供している認証機能を説明する。
Spring Securityでは、設定ファイルの記述のみで、ユーザ認証を実装することができる。 Spring Securityで提供している認証方式として、DB認証、LDAP認証、CAS認証、JAAS認証、X509認証、Basic認証がサポートされているが、本ガイドラインでは、DB認証についてのみ説明する。
Tip
DB認証以外の詳細は、各認証方式の公式ドキュメントを参照されたい。
6.3.1.1. Login¶
Spring Securityによるログイン処理の流れを以下に示す。
- 認証処理を指定したリクエストを受信すると、認証フィルタが起動する。
- 認証フィルタは、リクエストからユーザ、パスワードを抽出し、認証情報を生成する。 生成した認証情報をパラメータとし、認証マネージャの認証処理を実行する。
- 認証マネージャは、指定された認証プロバイダの認証処理を実行する。 認証プロバイダは、データソース(DBやLDAP)からユーザ情報を取得し、パスワード照合等のユーザ認証を行う。 認証成功時には、認証済みの情報を保持する認証情報を作成し、 認証マネージャに返す。認証失敗の場合は、認証失敗例外を送出する。
- 認証マネージャは、受け取った認証情報を認証フィルタに返す。
- 認証フィルタは、受け取った認証情報(認証済み)をセッションに格納する。
- 認証成功時は、認証前のセッション情報を初期化し、新たにセッション情報を作成する。
- 指定された認証成功/失敗時のパスへリダイレクトする。セッションIDをクライアントに返却する。
6.3.1.2. Logout¶
Spring Securityによるログアウト処理の流れを以下に示す。
- 指定されたログアウト処理へのリクエストを受信すると、ログアウトフィルタが起動する。
- ログアウトフィルタはセッション情報を破棄する。 また、クライアントのクッキー(図中のCookie)を破棄するようなレスポンスを設定する。
- 指定されたログアウト時のパスへ、リダイレクトする。
Note
ログアウト後、残存するセッション情報が第三者に利用されることによるなりすましを防ぐため、 セッション情報は、ログアウト時に
org.springframework.security.web.session.ConcurrentSessionFilter
で破棄される。
6.3.2. How to use¶
6.3.2.1. <sec:http>
要素の設定¶
<http>
要素のauto-config
属性をtrue
とすることで、<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" use-expressions="true"> <!-- (1) -->
<!-- omitted -->
</sec:http>
</beans>
項番 | 説明 |
---|---|
(1)
|
auto-config="true" と設定することで、<form-login> 、<http-basic> 、<logout> 要素を設定しなくても有効になる。 |
Note
<form-login>
、<http-basic>
、<logout>
要素について説明する。
要素名 説明 <form-login>
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
が有効になる。UsernamePasswordAuthenticationFilterは、ユーザ名、パスワードをPOST時に、リクエストから取り出し、認証を行うFilterである。詳細は、<sec:form-login>要素の設定を参照されたい。<http-basic>
org.springframework.security.web.authentication.www.BasicAuthenticationFilter
が有効になる。BasicAuthenticationFilterは、Basic認証の処理を実施するFilterであり、RFC1945に準拠して実装されている。詳細な利用方法は、BasicAuthenticationFilter JavaDocを参照されたい。<logout>
org.springframework.security.web.authentication.logout.LogoutFilter
,org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler
が有効になる。LogoutFilterは、ログアウト時に呼ばれるFilterであり、org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices
(Cookieの削除) や、SecurityContextLogoutHandler(セッションの無効化)を呼び出している。詳細は、<sec:logout>要素の設定を参照されたい。
6.3.2.2. <sec:form-login>
要素の設定¶
<sec:form-login>
要素の設定方法を説明する。spring-security.xml
<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" use-expressions="true">
<sec:form-login login-page="/login"
default-target-url="/"
login-processing-url="/authentication"
always-use-default-target="false"
authentication-failure-url="/login?error=true"
authentication-failure-handler-ref="authenticationFailureHandler"
authentication-success-handler-ref="authenticationSuccessHandler" /> <!-- 属性の指定順番で(1)~(7) -->
</sec:http>
</beans>
項番 | 説明 |
---|---|
(1)
|
login-page 属性にログインフォーム画面のパスを指定する。「未認証ユーザ」が「認証ユーザ」しかアクセスできないページにアクセスした際に、
強制リダイレクトさせるパス。
|
(2)
|
default-target-url 属性に認証成功時の遷移先パスを指定する。指定がない場合、”/”が、デフォルトのパスになる。 |
(3)
|
login-processing-url 属性に認証処理を行うパスを指定する。指定がない場合、「j_spring_security_check」がデフォルトのパスになる。本ガイドラインでは、上記のデフォルト値「j_spring_security_check」を使用せず、システム独自の値に変更することを推奨する。この例では”/authentication”を指定している。
|
(4)
|
ログイン成功後に
default-target-url に指定したパスに常に遷移するかどうかをalways-use-default-target 属性に設定する。デフォルトは、
false である。false に設定されている場合、認証成功のハンドラの基底クラスであるorg.springframework.security.web.authentication.AbstractAuthenticationTargetUrlRequestHandler でリダイレクト先が指定されていれば、指定先に遷移する。指定がない場合、
default-target-url に指定したパスに遷移する。 |
(5)
|
authentication-failure-url に認証失敗時の遷移先を設定する。authentication-failure-handler-ref 属性の指定がない場合、認証エラーの種別を問わず、一律、本設定の遷移先に遷移する。 |
(6)
|
default-target-url 属性に認証失敗時に呼ばれる、ハンドラクラスを指定する。詳細は、認証エラー時のハンドラクラスの設定を参照されたい。
|
(7)
|
default-target-url 属性に認証成功時に呼ばれる、ハンドラクラスを指定する。 |
上記以外の属性については、Spring Securityのマニュアルを参照されたい。
Warning
Spring Security のデフォルト値「j_spring_security_check」の使用を推奨しない理由
デフォルト値を使用している場合、そのアプリケーションが、Spring Securityを使用していることについて、露見してしまう。 そのため、Spring Securityの脆弱性が発見された場合、脆弱性をついた攻撃を受けるリスクが高くなる。 前述のリスクを避けるためにも、デフォルト値を使用しないことを推奨する。
6.3.2.2.1. ログインフォームの作成¶
src/main/webapp/WEB-INF/views/login.jsp
<form:form action="${pageContext.request.contextPath}/authentication" method="post"><!-- (1) --> <!-- omitted --> <input type="text" id="username" name="j_username"><!-- (2) --> <input type="password" id="password" name="j_password"><!-- (5) --> <input type="submit" value="Login"> </form:form>
項番 説明 (1)formのaction属性に認証処理を行うための遷移先を指定する。遷移先のパスはlogin-processing-url属性で指定した、/authentication を指定すること。${pageContext.request.contextPath}/authenticationにアクセスすることで認証処理が実行される。HTTPメソッドは、「POST」を指定すること。(4)認証処理において、「ユーザID」として扱われる要素。name属性には、Spring Securityのデフォルト値である「j_username」を指定すること。(5)認証処理において、「パスワード」として扱われる要素。name属性には、Spring Securityのデフォルト値である「j_password」を指定すること。認証エラーメッセージを表示する場合は以下の追加する
<c:if test="${param.error}"><!-- (1) --> <t:messagesPanel messagesAttributeName="SPRING_SECURITY_LAST_EXCEPTION"/><!-- (2) --> </c:if>
項番 説明 (1)リクエストパラメータに設定されたエラーメッセージの判定を行う。form-login要素のauthentication-failure-url属性に設定された値や、認証エラーハンドラの”defaultFailureUrl”に設定された値によって、判定処理を変更する必要があるので注意すること。本例では、authentication-failure-url=”/login?error=true”のような設定がある場合の、例を示している。(2)認証エラー時に出力させる例外メッセージを出力する。共通ライブラリで提供しているorg.terasoluna.gfw.web.message.MessagesPanelTag
を指定して出力させることを推奨する。「<t:messagesPanel>
」タグの使用方法は、メッセージ管理を参照されたい。spring-mvc.xml
ログインフォームを表示するControllerを定義する。
<mvc:view-controller path="/login" view-name="login" /><!-- (1) -->
項番 説明 (1)“/login”にアクセスされたら、view名として”login”を返却するだけのControllerを定義する。InternalResourceViewResolver
によってsrc/main/webapp/WEB-INF/views/login.jspが出力される。この単純なコントローラはJavaによる実装が不要である。Note
上記の設定は次のControllerと同義である。
@Controller @RequestMapping("/login") public class LoginController { @RequestMapping public String index() { return "login"; } }
単純にview名を返すだけのメソッドが一つだけあるControllerが必要であれば、
<mvc:view-controller>
を使用すればよい。ログインフォームをController経由で表示するメリットは、CSRFトークンを自動で埋め込める点にある。Controllerを経由しない場合は、 Spring Securtityチュートリアルで実施したようにjspに直接CSRFトークンを埋め込む必要がある。
チュートリアルではController作成の説明を省くために、ログインフォームの表示はControllerを経由していない。CSRF対策の詳細はCSRF対策を参照されたい。
6.3.2.2.2. ログインフォームの属性名変更¶
「j_username」、「j_password」は、Spring Securityのデフォルト値である。<form-login>
要素の設定で、任意の値に変更することができる。
spring-security.xml
username
、password
の属性<sec:http auto-config="true" use-expressions="true"> <sec:form-login username-parameter="username" password-parameter="password" /> <!-- 属性の指定順番で(1)~(2) --> <!-- omitted --> </sec:http>
項番 説明 (1)username-parameter
属性でusername
の入力フィールドのname
属性を、「username」に変更している。(2)password-parameter
属性でpassword
の入力フィールドのname
属性を、「password」に変更している。
6.3.2.3. 認証処理の設定¶
Spring Securityにおける認証処理の設定はAuthenticationProvider
とUserDetailsService
の設定が
AuthenticationProvider
は、次の役割を担う。
- 認証に成功した場合、認証ユーザー情報を返却する
- 認証に失敗した場合、例外をスローする
UserDetailsService
は、認証ユーザー情報を永続化層から取得する役割を担う。
それぞれデフォルトで用意されているものを使用してもよいし、独自拡張して使用しても良い。 組み合わせも自由である。
6.3.2.3.1. AuthenticationProvider
クラスの設定¶
AuthenticationProvider
の実装として、DB認証を行うためのプロバイダorg.springframework.security.authentication.dao.DaoAuthenticationProvider
を使用する方法を説明する。spring-security.xml
<sec:authentication-manager><!-- (1) --> <sec:authentication-provider user-service-ref="userDetailsService"><!-- (2) --> <sec:password-encoder ref="passwordEncoder" /><!-- (3) --> </sec:authentication-provider> </sec:authentication-manager>
項番 説明 (1)<sec:authentication-manager>
要素内に<sec:authentication-provider>
要素を定義する。複数指定して、認証方法を組み合わせることが可能であるが、ここでは説明しない。(2)<sec:authentication-provider>
要素でAuthenticationProvider
を定義する。デフォルトで、DaoAuthenticationProvider
が有効になる。これ以外のAuthenticationProvider
を指定する場合はref属性で、対象のAuthenticationProviderのBean IDを指定する。user-service-ref
属性に、認証ユーザ情報を取得するUserDetailsService
のBean Idを指定する。DaoAuthenticationProvider
を使用する場合、この設定は必須である。詳細は、UserDetailsServiceクラスの設定を参照されたい。(3)パスワード照合時に、フォームから入力されたパスワードのエンコードを行うクラスのBean IDを指定する。指定がない場合に、「平文」でパスワードが扱われる。詳細は、パスワードハッシュ化を参照されたい。
DaoAuthenticationProvider
を使用すれば良い。UserDetailsService
で決める。6.3.2.3.2. UserDetailsService
クラスの設定¶
AuthenticationProvider
のuserDetailsService
プロパティに指定したBeanを設定する。UserDetailsService
は次のメソッドをもつインタフェースである。
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
このインタフェースを実装すれば、任意の保存場所から認証ユーザー情報を取得することができる。
ここでは、JDBCを使用して、DBからユーザ情報を取得する org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl
を説明する。
JdbcDaoImpl
を使用するにはspring-security.xmlに以下のBean定義を行えば良い。
<!-- omitted -->
<bean id="userDetailsService"
class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
JdbcDaoImpl
は、認証ユーザー情報と認可情報を取得するためのデフォルトSQLを定義しており、これらに対応したテーブルが用意されていることが前提となっている。前提としているテーブル定義はSpring Securityのマニュアルを参照されたい。ユーザ情報取得クエリに合致するテーブルを作成することで、後述する設定ファイルへのクエリ指定が不要となる。「username」、「password」、「enabled」フィールドは必須であるが、後述する設定ファイルへのクエリ指定で、別名を付与することにより、テーブル名、カラム名が一致しなくても問題ない。例えば次のようなSQLを設定すれば「email」カラムを「username」として使用することができ、「enabled」は常にtrue
となる。SELECT email AS username, pwd AS password, true AS enabled FROM customer WHERE email = ?ログインフォームの作成で前述した、「ユーザID」がクエリのパラメータに指定される。
-
ユーザに対する認可情報を取得するクエリである。
-
ユーザーが所属するグループの認可情報を取得するクエリである。グループ権限はデフォルトでは無効になっており、本ガイドラインでも扱わない。
テーブル名: account
論理名 | 物理名 | 型 | 説明 |
---|---|---|---|
ユーザID | username | 文字列 | ユーザを一意に識別するためのユーザID。 |
パスワード | password | 文字列 | ユーザパスワード。ハッシュ化された状態で格納する。 |
有効フラグ | enabled | 真偽値 | 無効ユーザ、有効ユーザを表すフラグ。「false」に設定されたユーザは無効ユーザとして、認証エラーとなる。 |
権限名 | authority | 文字列 | 認可機能を必要としない場合は不要。 |
JdbcDaoImpl
をカスタマイズして設定する例を以下に示す。
<!-- omitted -->
<bean id="userDetailsService"
class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
<property name="rolePrefix" value="ROLE_" /><!-- (1) -->
<property name="dataSource" ref="dataSource" />
<property name="enableGroups" value="false" /><!-- (2) -->
<property name="usersByUsernameQuery"
value="SELECT username, password, enabled FROM account WHERE username = ?" /><!-- (3) -->
<property name="authoritiesByUsernameQuery"
value="SELECT username, authority FROM account WHERE username = ?" /><!-- (4) -->
</bean>
項番 | 説明 |
---|---|
(1)
|
権限名のprefixを指定する。DB上に格納されている権限名が”USER”の場合、この認証ユーザーオブジェクトが持つ権限名は”ROLE_USER”になる。
認可機能と命名規則を合わせて設定する必要がある。認可機能の詳細は、認可を参照されたい。
|
(2)
|
認可機能において、「グループ権限」の概念を用いる場合に指定する。
本ガイドラインでは扱わない。
|
(3)
|
ユーザ情報を取得するクエリを設定する。取得するデータは、「ユーザID」、「パスワード」、「有効フラグ」の順とする。
「有効フラグ」による認証判定を行わない場合には、「有効フラグ」のSELECT結果を「true」固定とする。
なお、ユーザを一意に取得できるクエリを記述すること。複数件数取得された場合には、1件目のレコードがユーザとして使われる。
|
(4)
|
ユーザの権限を取得するクエリを設定する。取得するデータは、「ユーザID」、「権限ID」の順とする。
認可の機能を使用しない場合は、「権限ID」は任意の固定値でよい。
|
Note
クエリを変更するだけでは実現できない認証を行う場合、UserDetailsService
を拡張して実現する必要がある。
拡張方法については、UserDetailsServiceの拡張を参照されたい。
6.3.2.4. UserDetails
クラスの利用方法¶
UserDetailsService
が作成したUserDetails
の利用方法について、説明する。6.3.2.4.1. JavaクラスでUserDetails
オブジェクトを利用する¶
UserDetails
クラスはorg.springframework.security.core.context.SecurityContextHolder
に格納される。SecurityContextHolder
からUserDetails
を取得する例を示す。
public static String getUsername() {
Authentication authentication = SecurityContextHolder.getContext()
.getAuthentication(); // (1)
if (authentication != null) {
Object principal = authentication.getPrincipal(); // (2)
if (principal instanceof UserDetails) {
return ((UserDetails) principal).getUsername(); // (3)
}
return (String) principal.toString();
}
return null;
}
項番 | 説明 |
---|---|
(1)
|
SecurityContextHolder からorg.springframework.security.core.Authentication オブジェクトを取得する。 |
(2)
|
Authentication オブジェクトからUserDetails オブジェクトを取得する。 |
(3)
|
UserDetails オブジェクトから、ユーザ名を取得する。 |
SecurityContextHolder
からUserDetails
オブジェクトを取得する方法は、どこからでもstaticメソッドで利用可能であり、
便利な反面、モジュール結合度を高めてしまう。テストも実施しづらい。
UserDetails
オブジェクトはjava.security.Principal
オブジェクトからも取得可能であるため、Spring MVCのController内では以下のようにSecurityContextHolder
を使用せずにUserDetails
オブジェクトを取得できる。
@RequestMapping(method = RequestMethod.GET)
public String view(Principal principal, Model model) {
// get Authentication
Authentication authentication = (Authentication) principal; // (1)
// get UserDetails
UserDetails userDetails = (UserDetails) authentication.getPrincipal(); // (2)
// omitted ...
}
項番 | 説明 |
---|---|
(1)
|
java.security.Principal オブジェクトをorg.springframework.security.core.Authentication クラスにキャストする。 |
(2)
|
Authentication オブジェクトからUserDetails オブジェクトを取得する。 |
Controller内でUserDetailsオブジェクトにアクセスする場合はこちらの方法を推奨する。
Note
ServiceクラスではControllerが取得したUserDetails
オブジェクトの情報を使用し、SecurityContextHolder
は使用しないことを推奨する。
SecurityContextHolder
はjava.security.Principal
オブジェクトにアクセスできないメソッド内でのみ利用することが望ましい。
6.3.2.4.2. JSPでUserDetails
にアクセスする¶
<%@ taglib uri="http://www.springframework.org/security/tags" prefix="sec"%>
Note
TERASOLUNA Global Frameworkの雛形を使用している場合はWEB-INF/views/common/include.jspに設定済みである。
<sec:authentication property="principal.username" /><!-- (1) -->
項番 | 説明 |
---|---|
(1)
|
<sec:authentication> タグでAuthentication オブジェクトにアクセスでき、property 属性に指定したプロパティアクセスできる。この例ではgetPrincipal().getUsername() の結果を出力する。 |
<sec:authentication property="principal" var="userDetails" /> <!-- (1) -->
${f:h(userDetails.username)} <!-- (2) -->
項番 | 説明 |
---|---|
(1)
|
property 属性に指定したプロパティをvar 属性にした名前で変数に格納できる。 |
(2)
|
(1)で変数に格納した後はJSP内で
UserDetails にアクセスできる。 |
Note
Controller内でUserDetails
を取得してModel
に追加することもできるが、JSPに表示する際はJSPタグを使用すればよい。
Note
UserDetailsServiceクラスの設定で説明したJdbcDaoImpl
が生成するUserDetails
は「ユーザーID」や「権限」といった最低限の情報しか保持していない。
画面の表示項目として「ユーザー姓名」など他のユーザー情報が必要な場合はUserDetails
と UserDetailsService
を拡張する必要がある。
拡張方法については、UserDetailsServiceの拡張を参照されたい。
6.3.2.5. Spring Securityにおけるセッション管理¶
<session-management>
タグを指定することで、org.springframework.security.web.session.SessionManagementFilter
が有効になる。<sec:http auto-config="true" create-session="ifRequired" ><!-- (1) -->
<!-- omitted -->
<sec:session-management
invalid-session-url="/"
session-authentication-error-url="/"
session-fixation-protection="migrateSession"
session-authentication-strategy-ref="sessionStrategy" /><!-- 属性の指定順番で(2)~(5) -->
<!-- omitted -->
</sec:http>
項番 | 説明 |
---|---|
(1)
|
create-session 属性でセッションの作成方針を指定する。以下の値を指定することができる。
always : Spring Securityは、既存のセッションがない場合にセッションを新規作成する、セッションが存在すれば、再利用する。ifRequired : Spring Securityは、セッションが必要であれば作成する。デフォルトの設定である。セッションがすでにあれば、作成せずに再利用する。never : Spring Securityは、セッションを作成しないが、セッションが存在すれば、再利用する。stateless : Spring Securityは、セッションを作成しない、セッションが存在しても使用しない。そのため、毎回認証を行う必要がある。 |
(2)
|
invalid-session-url 属性で無効なセッションIDがリクエストされた場合に遷移するパスを指定する。設定しない場合、
org.springframework.security.web.session.SimpleRedirectInvalidSessionStrategy の設定に依存したパスに遷移する。
|
(3)
|
org.springframework.security.web.authentication.session.SessionAuthenticationStrategy で例外が発生した場合、遷移するパスに
session-authentication-error-url 属性に指定する。指定しない場合、
org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler の設定に依存する。
|
(4)
|
session-fixation-protection 属性でセッション管理方式を指定する。以下の値を指定することができる。
migrateSession :ログイン前のセッション情報を引き継ぎ(コピー)、IDのみ新規作成する。デフォルトの設定である。newSession :ログイン前のセッション情報を引き継がず、ID、セッション内容を新規作成する。本機能の目的は、新しいセッションIDをログイン毎に割り振ることで、セッション・フィクセーション攻撃を防ぐことにある。そのため、明確な意図がない限り、デフォルトの設定を推奨する。
|
(5)
|
session-authentication-strategy-ref 属性でセッションチェックの振る舞いを決めるorg.springframework.security.core.Authentication.SessionAuthenticationStrategy クラスのBean IDを指定する。 |
6.3.2.5.1. SessionAuthenticationStrategy
の設定¶
SessionAuthenticationStrategy
の設定例を以下に示す。
<bean id="sessionStrategy"
class="org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy"> <!-- (1) -->
<constructor-arg index="0">
<list>
<bean
class="org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy" />
<bean class="org.springframework.security.web.csrf.CsrfAuthenticationStrategy"> <!-- (2) -->
<constructor-arg index="0" ref="csrfTokenRepository" />
</bean>
</list>
</constructor-arg>
</bean>
<!-- omitted -->
項番 | 説明 |
---|---|
(1)
|
org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy のコンストラクタの引数に、listで指定する。
|
(2)
|
本例では、CSRFチェックを行う
org.springframework.security.web.csrf.CsrfAuthenticationStrategy を指定している。CSRF対策については、CSRF対策を参照されたい。
|
6.3.2.5.2. 同時セッション数の制御¶
Authentication.getPrincipal()
で取得される、認証ユーザーオブジェクトのことである。- 1ユーザの最大セッション数を超過した場合、最も使用されていないユーザを無効にする (後勝ち)
- 1ユーザの最大セッション数を超過した場合、新規ログインを受け付けない (先勝ち)
どちらの場合も、この機能を有効にするためにはweb.xmlに以下の設定を追加する必要がある。
<listener>
<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class><!-- (1) -->
</listener>
項番 | 説明 |
---|---|
(1)
|
Concurrent Session Control を使用するに当たり、
org.springframework.security.web.session.HttpSessionEventPublisher を、listenerに定義する必要がある。 |
- 最も使用されていないユーザを無効にする場合
spring-security.xmlに以下の設定を追加する。
<sec:http auto-config="true" > <!-- omitted --> <sec:custom-filter position="CONCURRENT_SESSION_FILTER" ref="concurrencyFilter" /> <!-- (1) --> <sec:session-management session-authentication-strategy-ref="sessionStrategy" /> <!-- omitted --> </sec:http> <bean id="concurrencyFilter" class="org.springframework.security.web.session.ConcurrentSessionFilter"> <!-- (2) --> <constructor-arg index="0" ref="sessionRegistry" /> <!-- (3) --> <constructor-arg index="1" value="/" /> <!-- (4) --> </bean> <bean id="sessionAuthenticationStrategy" class="org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy"> <constructor-arg index="0"> <list> <!-- omitted --> <bean class= "org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy"> <!-- (5) --> <constructor-arg index="0" ref="sessionRegistry" /> <!-- (6) --> <property name="maximumSessions" value="1" /> <!-- (7) --> </bean> </list> </constructor-arg> </bean> <bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl" /> <!-- (8) --> <!-- omitted -->
項番 説明 (1)<custom-filter>
要素の、position
属性にCONCURRENT_SESSION_FILTER
を指定する。 (2)org.springframework.security.web.session.ConcurrentSessionFilter
クラスをBean定義する。 (3) コンストラクタの第1引数に、org.springframework.security.core.session.SessionRegistryImpl
を参照指定する。 (4) コンストラクタの第2引数に、期限切れになったセッションが遷移するパスを指定する。 (5) CompositeSessionAuthenticationStrategyの第1引数に、org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy
を指定する。 (6) コンストラクタの第1引数に、org.springframework.security.core.session.SessionRegistryImpl
を参照指定する。 (7)maximumSessions
属性に、1ユーザが許容する最大セッション数を定義することができる。上記例では、1を指定しているため、1ユーザが許容するセッションは1つになる。ユーザーが複数のブラウザでログインした場合、使用した日時が最も古いセッションに対して期限切れにする。指定しない場合、1が設定される。 (8)org.springframework.security.core.session.SessionRegistry
インタフェースを実装したクラスである、org.springframework.security.core.session.SessionRegistryImpl
を指定する。
- 新規ログインを受け付けない
spring-security.xmlに以下の設定を行う。
<bean id="concurrencyFilter" class="org.springframework.security.web.session.ConcurrentSessionFilter"> <constructor-arg index="0" ref="sessionRegistry" /> <constructor-arg index="1" value="/" /> </bean> <bean id="sessionAuthenticationStrategy" class="org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy"> <constructor-arg index="0"> <list> <!-- omitted --> <bean class= "org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy"> <constructor-arg index="0" ref="sessionRegistry" /> <property name="maximumSessions" value="1" /> <property name="exceptionIfMaximumExceeded" value="true"/> <!-- (1) --> </bean> </list> </constructor-arg> </bean> <!-- omitted -->
項番 説明 (1)exceptionIfMaximumExceeded
属性をtrue
に設定することにより、 最大セッション数を超過した場合、org.springframework.security.web.authentication.session.SessionAuthenticationException
がスローされる。そのため、ConcurrentSessionFilter
の第2引数で定義したパスには遷移しないので注意すること。exceptionIfMaximumExceeded
属性の設定を省略した場合は、false
が設定される。Tip
<sec:session-management>
要素のsession-authentication-strategy-ref
属性を指定せず、<sec:session-management>
要素の子要素として<sec:concurrency-control>要素を使用することもできる。
6.3.2.6. 認証エラー時のハンドラクラスの設定¶
<sec:form-login>
要素のauthentication-failure-handler-ref
属性にorg.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler
クラスの設定をし、spring-security.xml
<sec:http auto-config="true" use-expressions="true">
<sec:form-login login-page="/login"
authentication-failure-handler-ref="authenticationFailureHandler"
authentication-success-handler-ref="authenticationSuccessHandler" />
</sec:http>
<bean id="authenticationFailureHandler"
class="org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler">
<property name="defaultFailureUrl" value="/login/defaultError" /><!-- (1) -->
<property name="exceptionMappings"><!-- (2) -->
<props>
<prop key=
"org.springframework.security.authentication.BadCredentialsException"><!-- (3) -->
/login/badCredentials
</prop>
<prop key=
"org.springframework.security.core.userdetails.UsernameNotFoundException"><!-- (4) -->
/login/usernameNotFound
</prop>
<prop key=
"org.springframework.security.authentication.DisabledException"><!-- (5) -->
/login/disabled
</prop>
<prop key=
"org.springframework.security.authentication.ProviderNotFoundException"><!-- (6) -->
/login/providerNotFound
</prop>
<prop key=
"org.springframework.security.authentication.AuthenticationServiceException"><!-- (7) -->
/login/authenticationService
</prop>
<!-- omitted -->
</props>
</property>
</bean>
項番 | 説明 |
---|---|
(1)
|
エラー時のデフォルトの遷移先パスを指定する。
後述する
exceptionMappings プロパティに定義していない例外が発生した場合、本プロパティで指定した遷移先に遷移する。 |
(2)
|
catchする例外と、例外発生時の遷移先を、リスト形式で指定する。
keyに例外クラスを指定し、値に遷移先を設定する。
|
Spring Securityがスローする代表的な例外を、以下に記述する。
項番 | エラーの種類 | 説明 |
---|---|---|
(3)
|
BadCredentialsException |
パスワード照合失敗による認証エラー時にスローされる。 |
(4)
|
UsernameNotFoundException |
不正ユーザID(存在しないユーザID)による認証エラー時にスローされる。
org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider を継承したクラスを認証プロバイダに指定している場合、hideUserNotFoundExceptions をfalse に変更しないと上記例外は、BadCredentialsException に変更される。 |
(5)
|
DisabledException |
無効ユーザIDによる認証エラー時に、スローされる。 |
(6)
|
ProviderNotFoundException |
認証プロバイダクラス未検出エラー時にスローされる。
設定誤り等の理由から、認証プロバイダクラスが不正な場合に発生する。
|
(7)
|
AuthenticationServiceException |
認証サービスエラー時にスローされる。
DB接続エラー等、認証サービス内で何らかのエラーが発生した際に発生する。
|
Warning
本例では、UsernameNotFoundException
をハンドリングして遷移させているが、
ユーザIDが存在しないことを利用者に知らせると、特定のIDの存在有無が判明するため、セキュリティの観点上望ましくない。
そのため、ユーザに通知するメッセージには、例外の種類によって区別をしない画面遷移、メッセージにした方がよい。
6.3.2.7. <sec:logout>
要素の設定¶
<sec:logout>
要素の設定方法を説明する。spring-security.xml
<sec:http auto-config="true" use-expressions="true">
<!-- omitted -->
<sec:logout
logout-url="/logout"
logout-success-url="/"
invalidate-session="true"
delete-cookies="JSESSIONID"
success-handler-ref="logoutSuccessHandler"
/> <!-- 属性の指定順番で(1)~(5) -->
<!-- omitted -->
</sec:http>
項番 | 説明 |
---|---|
(1)
|
logout-url 属性に、ログアウト処理を実行するためのパスを指定する。 |
(2)
|
logout-success-url 属性に、ログアウト後の遷移先パスを指定する。 |
(3)
|
invalidate-session 属性に、ログアウト時にセッションを破棄するかを設定する。デフォルトはtrue である。true の場合、ログアウト時にセッションがクリアされる。 |
(4)
|
delete-cookies 属性に、指定したクッキーを削除する。複数記述する場合は「,」で区切る。 |
(5)
|
success-handler-ref 属性に、ログアウト成功後に呼び出される、ハンドラクラスを指定する。 |
6.3.2.8. <sec:remember-me>
要素の設定¶
<sec:remember-me>
要素の属性について、以下に示す。spring-security.xml
<sec:http auto-config="true" use-expressions="true">
<!-- omitted -->
<sec:remember-me key="terasoluna-tourreservation-km/ylnHv"
token-validity-seconds="#{30 * 24 * 60 * 60}" /> <!-- 属性の指定順番で(1)~(2) -->
<!-- omitted -->
</sec:http>
項番 | 説明 |
---|---|
(1)
|
key 属性に、Remeber Me用のcookieを保持しておくためのユニークなキーを指定する。指定が無い場合、ユニークなキーを起動時に生成するため、起動時間向上を考えた場合指定しておくことを推奨する。
|
(2)
|
「
token-validity-seconds 属性に、Remeber Me用のcookieの有効時間を秒単位で指定する。この例では30日間を設定している。指定が無い場合、デフォルトで14日間が有効期限になる。
|
上記以外の属性については、Spring Securityのマニュアルを参照されたい。
ログインフォームには以下のように「Remeber Me」機能を有効にするためのフラグを用意する必要がある。
<form method="post"
action="${pageContext.request.contextPath}/authentication">
<!-- omitted -->
<label for="_spring_security_remember_me">Remember Me : </label>
<input name="_spring_security_remember_me"
id="_spring_security_remember_me" type="checkbox"
checked="checked"> <!-- (1) -->
<input type="submit" value="LOGIN">
<!-- omitted -->
</form>
項番 | 説明 |
---|---|
(1)
|
HTTPパラメータに、
_spring_security_remember_me を設定することで、true でリクエストされた場合、次回の認証を回避することができる。 |
6.3.3. How to extend¶
6.3.3.1. UserDetailsService
の拡張¶
org.springframework.security.core.userdetails.UserDetails
org.springframework.security.core.userdetails.userDetailsService
を実装する必要がある。
ログインユーザーの氏名や所属部署などの付属情報を常に画面のヘッダーに表示させる必要がある場合、毎リクエストでDBから取得するのは非効率的である。
UserDetails
オブジェクトに保持させて、SecurityContext
や<sec:authentication>
タグからアクセスできようにするにはこの拡張が必要である。
6.3.3.1.1. UserDetails
の拡張¶
認証情報以外に顧客情報も保持するReservationUserDetails
クラスを作成する。
public class ReservationUserDetails extends User { // (1)
// omitted
private final Customer customer; // (2)
private static final List<? extends GrantedAuthority> DEFAULT_AUTHORITIES = Collections
.singletonList(new SimpleGrantedAuthority("ROLE_USER")); // (3)
public ReservationUserDetails(Customer customer) {
super(customer.getCustomerCode(),
customer.getCustomerPassword(), true, true, true, true, DEFAULT_AUTHORITIES); // (4)
this.customer = customer;
}
public Customer getCustomer() { // (5)
return customer;
}
}
項番 | 説明 |
---|---|
(1)
|
UserDetails のデフォルトクラスである、org.springframework.security.core.userdetails.User クラスを継承する。 |
(2)
|
認証情報および顧客情報をもつDomainObjectクラスを保持する。
|
(3)
|
認可情報を、
org.springframework.security.core.authority.SimpleGrantedAuthority のコンストラクタで作成する。ここでは”ROLE_USER”という権限を与える。本実装は簡易実装であり、本来は認可情報はDB上の別のテーブルから取得すべきである。
|
(4)
|
スーパークラスのコンストラクタに、DomainObjectが持つユーザID、パスワードを設定する。
|
(5)
|
UserDetails 経由で顧客情報にアクセスするためのメソッド。 |
Note
User
クラスを継承するだけでは、業務要件を実現できない場合、UserDetails
インタフェースを実装すればよい。
6.3.3.1.2. 独自UserDetailsService
の実装¶
UserDetailsService
を実装したReservationUserDetailsServiceクラスを作成する。Customer
オブジェクトを取得する処理を実装したCustomerSharedService
クラスをインジェクションして、DBから顧客情報を取得している。public class ReservationUserDetailsService implements UserDetailsService {
@Inject
CustomerSharedService customerSharedService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Customer customer = customerSharedService.findOne(username);
// omitted
return new ReservationUserDetails(customer);
}
}
6.3.3.1.3. 使用方法¶
作成したReservationUserDetailsService
、ReservationUserDetails
の使用方法を説明する。
spring-security.xml
<sec:authentication-manager> <sec:authentication-provider user-service-ref="userDetailsService"><!-- (1) --> <sec:password-encoder ref="passwordEncoder" /> </sec:authentication-provider> </sec:authentication-manager> <bean id="userDetailsService" class="com.example.domain.service.userdetails.ReservationUserDetailsService"><!-- (2) --> </bean> <!-- omitted -->
項番 説明 (1)ReservationUserDetailsService
のBean IDをref属性に定義する。(2)ReservationUserDetailsService
をBean定義する。JSP
<sec:authentication>
タグを使用してCustomer
オブジェクトにアクセスする。<sec:authentication property="principal.customer" var="customer"/><!-- (1) --> ${f:h(customer.customerName)}<!-- (1) -->
項番 説明 (1)ReservationUserDetails
がもつCustomer
オブジェクトを変数に格納する。(2)変数に格納したCustomer
オブジェクトの任意のプロパティを表示する。f:h()
については、XSS対策を参照されたい。Controller
@RequestMapping(method = RequestMethod.GET) public String view(Principal principal, Model model) { // get Authentication Authentication authentication = (Authentication) principal; // get UserDetails ReservationUserDetails userDetails = (ReservationUserDetails) authentication.getPrincipal(); // get Customer Customer customer = userDetails.getCustomer(); // (1) // omitted ... }
項番 説明 (1)ReservationUserDetails
から、ログイン中のCustomer
オブジェクトを取得する。このオブジェクトをServiceクラスに渡して業務処理を行う。
Note
顧客情報が変更された場合、一度ログアウトしないとReservationUserDetails
がもつCustomer
オブジェクトは変更されない。
頻繁に変更されうる情報や、ログインユーザー以外のユーザー(管理者など)によって変更される情報は保持しない方がよい。
6.3.3.2. AuthenticationProvider
の拡張¶
Todo
内容をもう一度見直す。
org.springframework.security.authentication.AuthenticationProvider
を実装したクラスを作成する必要がある。AuthenticationProvider
では、org.springframework.security.core.Authentication
を実装したクラスを戻り値として返却する必要がある。Authentication
を実装したクラスには、getPrincipal
メソッド(認証情報の取得)、getCredentials
メソッド(principalの正当性を保証する情報の取得)を設定する必要がある。AuthenticationProvider
でこの値にアクセスするために、org.springframework.security.authentication.UsernamePasswordAuthenticationToken
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
を継承したクラスを作成する必要がある。
6.3.3.2.1. UsernamePasswordAuthenticationToken
の拡張¶
UsernamePasswordAuthenticationToken
を継承した、独自のAuthenticationToken
を作成する。UsernamePasswordAuthenticationToken
を拡張することで、認証情報に会社識別子を持たせることができる。// import omitted
public class CompanyIdUsernamePasswordAuthenticationToken extends
UsernamePasswordAuthenticationToken {
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
private final String companyId; // (1)
public CompanyIdUsernamePasswordAuthenticationToken(
Object principal, Object credentials, String companyId) { // (2)
super(principal, credentials);
this.companyId = companyId;
}
public CompanyIdUsernamePasswordAuthenticationToken(
Object principal, Object credentials, String companyId,
Collection<? extends GrantedAuthority> authorities) { // (3)
super(principal, credentials, authorities);
this.companyId = companyId;
}
public String getCompanyId() {
return companyId;
}
}
項番 | 説明 |
---|---|
(1)
|
会社識別子用のフィールドを作成する。
|
(2)
|
認証前に、
CompanyIdUsernamePasswordAuthenticationToken のインスタンスを作成する時に使用するコンストラクタ。 |
(3)
|
認証成功後に、
CompanyIdUsernamePasswordAuthenticationToken のインスタンスを作成する時に使用するコンストラクタ。親クラスのコンストラクタの引数に認可情報も併せて渡すことで、認証済みの状態となる。
|
6.3.3.2.2. 独自AuthenticationProvider
の実装¶
AuthenticationProvider
でCompanyIdUsernamePasswordAuthenticationToken
を使用する。// import omitted
public class CompanyIdUsernamePasswordAuthenticationProvider implements
AuthenticationProvider {
// omitted
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
CompanyIdUsernamePasswordAuthenticationToken authenticationToken =
(CompanyIdUsernamePasswordAuthenticationToken) authentication; // (1)
// Obtain userName, password, companyId
String username = authenticationToken.getName();
String password = authenticationToken.getCredentials().toString();
String companyId = authenticationToken.getCompanyId();
// Obtain user (and grants if needed) from database or other system
// UserDetails userDetails = ...
// Business logic
// ...
return new UsernamePasswordAuthenticationToken(userDetails, authentication.getCredentials(),
Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER"))); // (2)
}
@Override
public boolean supports(Class<?> authentication) {
return CompanyIdUsernamePasswordAuthenticationToken.class
.equals(authentication); // (3)
}
// omitted
}
項番 | 説明 |
---|---|
(1)
|
UsernamePasswordAuthenticationFilter を拡張したクラスで設定した、独自のAuthenticationToken にキャストする。詳細については、後述するUsernamePasswordAuthenticationFilter の拡張を参照されたい。
|
(2)
|
認証処理(ユーザ情報の取得、パスワードチェック、会社識別子チェック等)実施後、
UsernamePasswordAuthenticationToken を作成して返却する。パラメータに認可情報も併せて設定することで、認証済みの
AuthenticationToken インスタンスを生成する。UserDetails もコンストラクタのパラメータに渡す。本例では、簡易実装として、単一のロールのみ使用している。
権限についての詳細は、ユーザ情報管理クラスの設定を参照されたい。
|
(3)
|
独自の
AuthenticationToken である、CompanyIdUsernamePasswordAuthenticationToken クラスをサポート対象とする。 |
6.3.3.2.3. UsernamePasswordAuthenticationFilter
の拡張¶
UsernamePasswordAuthenticationFilter
を継承した、独自のAuthenticationFilter
を作成する。UsernamePasswordAuthenticationFilter
を拡張することで、CompanyIdUsernamePasswordAuthenticationProvider
へ、CompanyIdUsernamePasswordAuthenticationToken
を渡すことができる。// import omitted
public class CompanyIdUsernamePasswordAuthenticationFilter extends
UsernamePasswordAuthenticationFilter {
@Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
if (!request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: "
+ request.getMethod());
}
// Obtain UserName, Password, CompanyId
String username = super.obtainUsername(request);
String password = super.obtainPassword(request);
String companyId = obtainCompanyId(request);
// username required
if (!StringUtils.hasText(username)) {
throw new AuthenticationServiceException("UserName is required");
}
// validate password, companyId
// omitted other process
CompanyIdUsernamePasswordAuthenticationToken authRequest =
new CompanyIdUsernamePasswordAuthenticationToken(username, password, companyId); // (1)
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest); // (2)
}
protected String obtainCompanyId(HttpServletRequest request) {
return request.getParameter("j_companyid"); // (3)
}
}
項番 | 説明 |
---|---|
(1)
|
HttpServletRequest から取得した、ユーザ名、パスワード、会社識別子を設定した、CompanyIdUsernamePasswordAuthenticationToken のインスタンスを生成する。 |
(2)
|
未認証状態の
CompanyIdUsernamePasswordAuthenticationToken のインスタンスをorg.springframework.security.authentication.AuthenticationManager.authenticate のパラメータとして設定する。 |
(3)
|
会社識別子を、リクエストパラメータより取得する。
|
Note
ログイン情報の入力チェックについて
DBサーバへの負荷軽減等で、あきらかな入力誤りに対しては、事前にチェックを行いたい場合がある。
その場合は、UsernamePasswordAuthenticationFilterの拡張のように、
UsernamePasswordAuthenticationFilter
を拡張することで、入力チェック処理を行うことができる。
6.3.3.2.4. 使用方法¶
ログインフォームページ(JSP)
ログインフォームの作成の例に、会社識別子を追加する。
<form:form action="${pageContext.request.contextPath}/authentication" method="post"> <!-- omitted --> <span>User Id</span><br> <input type="text" id="username" name="j_username"><br> <span>Company Id</span><br> <input type="text" id="companyid" name="j_companyid"><br> <!-- (1) --> <span>Password</span><br> <input type="password" id="password" name="j_password"><br> <!-- omitted --> </form:form>
項番 説明 (1)会社識別子の入力フィールドのnameは、j_companyid を指定する。spring-security.xml
独自のAuthenticationProvider
、AuthenticationFilter
を設定する。<sec:http auto-config="false" use-expressions="true" entry-point-ref="loginUrlAuthenticationEntryPoint"> <!-- (1) --> <!-- omitted --> <sec:custom-filter position="FORM_LOGIN_FILTER" ref="companyIdUsernamePasswordAuthenticationFilter" /> <!-- (2) --> <!-- omitted --> <sec:logout logout-url="/logout" logout-success-url="/" delete-cookies="JSESSIONID" invalidate-session="true" /> </sec:http> <bean id="companyIdUsernamePasswordAuthenticationFilter" class="com.example.app.common.security.CompanyIdUsernamePasswordAuthenticationFilter"> <!-- (3) --> <property name="authenticationManager" ref="authenticationManager" /> <!-- (4) --> <property name="authenticationFailureHandler" ref="authenticationFailureHandler" /> <!-- (5) --> <property name="authenticationSuccessHandler" ref="authenticationSuccessHandler" /> <!-- (6) --> <property name="filterProcessesUrl" value="/authentication" /> <!-- (7) --> </bean> <bean id="loginUrlAuthenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint"> <!-- (8) --> <constructor-arg value="/login" /> <!-- (9) --> </bean> <sec:authentication-manager alias="authenticationManager"> <sec:authentication-provider ref="companyIdUsernamePasswordAuthenticationProvider" /> <!-- (10) --> </sec:authentication-manager> <bean id="companyIdUsernamePasswordAuthenticationProvider" class="com.example.app.common.security.CompanyIdUsernamePasswordAuthenticationProvider" /> <!-- (11) --> <!-- omitted -->
項番 説明 (1)custom-filter要素で、”FORM_LOGIN_FILTER” を差し替えた場合は、auto-config=”true” を指定することができない。そのため、auto-configの指定を削除するか、auto-config=”false” を指定する必要がある。また、form-login要素も指定できないため、entry-point-ref 属性を明示的に設定する必要がある。(2)custom-filter要素の position属性に “FORM_LOGIN_FILTER” を指定することで、UsernamePasswordAuthenticationFilterからCompanyIdUsernamePasswordAuthenticationFilterに差し替えることができる。(3)CompanyIdUsernamePasswordAuthenticationFilterをbean定義する。(4)authenticationManagerプロパティに、authentication-manager要素のalias属性の値を設定する。(5)authenticationFailureHandlerプロパティに、認証失敗時に呼ばれるハンドラクラスを指定する。(6)authenticationSuccessHandlerプロパティに、認証成功時に呼ばれるハンドラクラスを指定する。(7)filterProcessesUrlプロパティに、認証処理を行うパスを指定する。(8)http要素のentry-point-ref属性に指定する、AuthenticationEntryPointを定義する。form-login要素を指定した際のデフォルトのAuthenticationEntryPointである、org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint
をbean定義する。(9)コンストラクタの引数に、ログイン画面のパスを指定する。(10)authentication-provider要素にCompanyIdUsernamePasswordAuthenticationProvider を参照設定する。(11)CompanyIdUsernamePasswordAuthenticationProviderを、bean定義する。Warning
auto-config=”false” を指定したことで、
<sec:http-basic>
、<sec:logout>
要素を使用する場合は、明示的に定義する必要がある。
6.3.4. Appendix¶
org.terasoluna.gfw.web.security.RedirectAuthenticationHandler
を使用する。遷移元画面のJSPの記述例
<form:form action="${pageContext.request.contextPath}/login" method="get">
<!-- omitted -->
<input type="hidden" name="redirectTo"
value="${pageContext.request.contextPath}/reservetour/read?
${f:query(reserveTourForm)}&page.page=${f:h(param['page.page'])}
&page.size=${f:h(param['page.size'])}" /> <!-- (1) -->
</form:form>
項番 | 説明 |
---|---|
(1)
|
リダイレクトURLの設定
hidden項目の「redirectTo」に遷移先のURLを設定する。
nameに指定する値は、後述する設定ファイルに記載する、
targetUrlParameterと一致させること。
|
ログイン画面のJSPの記述例
<form:form action="${pageContext.request.contextPath}/authentication" method="post">
<!-- omitted -->
<input type="submit"
value="Login">
<input type="hidden" name="redirectTo" value="${f:h(param.redirectTo)}" /> <!-- (1) -->
<!-- omitted -->
</form:form>
項番 | 説明 |
---|---|
(1)
|
リダイレクトURLの設定
遷移元画面からリクエストパラメータで渡された、
リダイレクトURLをhidden項目に設定する。
|
Spring Security 設定ファイル
<sec:http auto-config="true">
<!-- omitted -->
<sec:form-login login-page="/login" default-target-url="/"
always-use-default-target="false"
authentication-failure-handler-ref="authenticationFailureHandler"
authentication-success-handler-ref="authenticationSuccessHandler" />
<!-- (1) -->
<!-- omitted -->
</sec:http>
<bean id="authenticationSuccessHandler"
class="org.terasoluna.gfw.web.security.RedirectAuthenticationHandler">
<!-- (2) -->
<property name="targetUrlParameter" value="redirectTo"/> <!-- (3) -->
</bean>
<bean id="authenticationFailureHandler"
class="org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler">
<!-- (4) -->
<property name="defaultFailureUrl" value="/login?error=true"/> <!-- (5) -->
<property name="useForward" value="true"/> <!-- (6) -->
</bean>
<!-- omitted -->
項番 | 説明 |
---|---|
(1)
|
authentication-failure-handler-ref(認証エラー時のハンドラ設定)と
authentication-success-handler-ref(認証成功時のハンドラ設定)のBeanIdを指定する。
|
(2)
|
authentication-success-handler-refの参照クラスとして
org.terasoluna.gfw.web.security.RedirectAuthenticationHandler を設定する。 |
(3)
|
前述したJSPのhiddenに記載した値と一致させること。
本例では、「redirectTo」を指定する。省略した場合、「redirectTo」が設定される。
|
(4)
|
authentication-failure-handler-refの参照クラスとして
org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler を設定する。 |
(5)
|
認証失敗時の遷移先パスにはログイン画面のパス、エラーを示すクエリを設定する。
|
(6)
|
本機能を使用する場合はuseForwardをtrueに指定する必要がある。
trueに指定することで、redirectでの遷移から、forwardでの遷移となる。
リクエストパラメータにリダイレクト先のURLを保持しているため、
forwardにすることでリクエストパラメータを保持したままにする必要がある。
|
Tip
RedirectAuthenticationHandlerは、オープンリダイレクタ脆弱性対策が施されているため、
拡張せずに「http://google.com」のような、外部サイトへの遷移をすることはできない。
別ドメインに移動したいときは、org.springframework.security.web.RedirectStrategy
を実装したクラスを作成する必要がある。
RedirectStrategyを実装したクラスは、セッターインジェクションでRedirectAuthenticationHandlerのtargetUrlParameterRedirectStrategyに設定する。
拡張する際の注意点としては、redirectToの値を改竄されても問題ない作りにする必要がある。
たとえば、リダイレクト先のURLを直接指定せず番号指定にする(ページ番号指定)、
リダイレクト先のドメインをチェックする等のいずれか対応が必要となる。