18
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

SpringSecurityを使ったログアウトでセッションが破棄されない場合

Last updated at Posted at 2015-01-05

SpringSecurityで認証を設定する

SpringSecurityを使うと、Webアプリケーションの認証機構をSpringSecirityに一任できます。例えばログインやログアウトのURLや、CSRF(クロスサイト・リクエスト・フォージャリ)を回避するトークンの発行も簡単に設定可能です。

SpringSecurityの設定例

今回は以下を想定した設定にします。

  • FORM認証を使う
  • CSRFの回避を自動で行う
  • Spring4シリーズで統一する
applicationContext-security.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans
  xmlns:sec="http://www.springframework.org/schema/security"
  xmlns:beans="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/security
  http://www.springframework.org/schema/security/spring-security.xsd">

  <sec:http auto-config='true'>
    <!-- CSRF回避チェックを有効 -->
    <sec:csrf/>

    <!-- アクセス制御をしないURLを定義 -->
    <sec:intercept-url pattern="/login*" access="permitAll"/>
    <sec:intercept-url pattern="/css/**" access="permitAll" />
    <sec:intercept-url pattern="/fonts/**" access="permitAll"/>
    <sec:intercept-url pattern="/images/**" access="permitAll"/>
    <sec:intercept-url pattern="/js/**" access="permitAll"/>
    <sec:intercept-url pattern="/templates/**" access="permitAll"/>

    <!-- ロールに ROLE_ADMIN を持つユーザID(USERNAME)のみアクセス許可 -->
    <sec:intercept-url pattern="/*" access="hasRole('ROLE_ADMIN')" />

    <!-- FORM認証の振る舞い -->
    <sec:form-login
        login-page="/login.action"
        login-processing-url="/j_spring_security_check"
        username-parameter="j_username"
        password-parameter="j_password"
        authentication-failure-url="/loginFailure.action" />

    <!-- ログアウト時の振る舞い -->
    <sec:logout 
        logout-url="/logout"
        logout-success-url="/logout.action"
        invalidate-session="true" />
  </sec:http>

  <!-- データベースを使った認証管理 -->
  <sec:authentication-manager alias="authManager">
    <sec:authentication-provider>
      <sec:jdbc-user-service data-source-ref="dataSource"
        users-by-username-query="
          SELECT
            USERNAME
          , PASSWORD
          , ENABLED
          FROM
            USERTABLE
          WHERE
            USERNAME = ?"
        authorities-by-username-query="
          SELECT
            USERNAME 
          , AUTHORITY 
          FROM 
            ROLETABLE 
          WHERE 
            USERNAME = ?"
      />
    </sec:authentication-provider>
  </sec:authentication-manager>
  <beans:bean 
    id="sessionRegistry" 
    class="org.springframework.security.core.session.SessionRegistryImpl" />
</beans:beans>

SpringSecurity4ではSpringELを使った設定になります。3.xまでの記述では動作しません。

ログアウトのURLを選択しても、実はセッションが残っている場合がある

さて、先ほどのxmlにてログアウト時のURLは、/logout と定義しました。
この設定そのものは間違いないのですが、CSRF回避チェックを有効にしている場合は、アンカータグやリダイレクトで /logout を指定すると、確かにログアウトページを表示しますが、なんとセッションが破棄されません。

この理由は CSRFチェックが有効だから です。
個人的にはややもんにょりしますが (´・ω・`)

このため、ログアウトを行う際には必ず次のルールを守ります。

  • <form>を使う
  • HTTP-METHODはPOST必須
  • CSRFチェックの送信パラメータを設定する

実装例は以下のようになります。
送信ボタンなどはつけずに、JavaScriptから送信する例です。

header.jsp(+JSTL)
<c:url var="logoutUrl" value="/logout"/>
<form id="logoutForm" action="${logoutUrl}" method="POST">
  <input type="hidden" 
    name="${_csrf.parameterName}" 
    value="${_csrf.token}"/>
</form>
header.html(thymeleaf-2.1)
<form id="logoutForm" th:action="@{/logout}" method="POST">
  <input type="hidden" 
    th:name="${_csrf.parameterName}" 
    th:value="${_csrf.token}"/>
</form>
18
20
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?