【Spring MVC + Spring Security】ログアウト機能を追加する方法

概要

ログアウト機能を追加する方法についてまとめた。

 

前提

以下の記事の続きとなる。

あわせて読みたい

概要 認証成功/失敗時に呼び出されるハンドラを利用して、後続処理を制御する方法についてまとめた。   前提 以下の記事の続きとなる。

【Spring MVC + Spring Security】認証成功/失敗時の処理をハンドラで制御する方法

 

ログアウト仕様

Spring Securityでは以下の流れでログアウト処理を行う。

 

【Spring MVC + Spring Security】ログアウト機能を追加する方法_概要
▲ログアウト処理の流れ

 

①クライアントがログアウトを行う
②ログアウトのURLにマッチングして、LogoutFilterの処理が動作する
③LogoutFilterはログアウトハンドラのリストを保持しており、順次ログアウト処理を呼び出す
※デフォルトでは実装クラスのSecurityContextLogoutHandlerが呼ばれ、認証情報のクリアやセッションの破棄が行われる
④すべてのログアウト処理が完了した後、LogoutSuccessHandlerを呼び出して画面遷移等を行う
※デフォルトでは実装クラスのSimpleUrlLogoutSuccessHandlerが呼ばれ、ログアウト遷移先へリダイレクトする

 

 

実装

ログアウト機能を実装する。

 

ログアウト機能

ログアウトに必要な情報を定義する。

 

SecurityConfig.java


package com.example.prototype.biz.security.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import com.example.prototype.biz.security.handler.LoginFailureHandler;
import com.example.prototype.biz.security.handler.LoginSuccessHandler;

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Autowired
    private LoginSuccessHandler loginSuccessHandler;
    
    @Autowired
    private LoginFailureHandler loginFailureHandler;
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    
    @Bean
    public UserDetailsService userDetailsService(PasswordEncoder encoder) {
        UserDetails disabledUser = User.withUsername("user")
            .password(encoder.encode("password"))
            .roles("USER")
            .build();
        
        return new InMemoryUserDetailsManager(disabledUser);
    }
    
    @Bean
    public DaoAuthenticationProvider authenticationProvider(MessageSource messageSource,
            PasswordEncoder passwordEncoder) {
        var provider = new DaoAuthenticationProvider();
        provider.setUserDetailsService(userDetailsService(passwordEncoder));
        provider.setPasswordEncoder(passwordEncoder);
        provider.setMessageSource(messageSource);
        return provider;
    }
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                // CSRF機能を無効
                .csrf(csrf -> csrf.disable())

                // 認可ポリシー定義
                .authorizeHttpRequests(auth -> auth
                        // 認証不要のURL
                        .requestMatchers(
                                new AntPathRequestMatcher("/authentication"),
                                new AntPathRequestMatcher("/logout")
                                ).permitAll()
                        .anyRequest().authenticated())

                // ログイン機能定義
                .formLogin(form -> form
                        .loginPage("/authentication")
                        .loginProcessingUrl("/authentication/process")
                        .usernameParameter("loginId")
                        .passwordParameter("pass")
                        .successHandler(loginSuccessHandler)
                        .failureHandler(loginFailureHandler))
                
                // ログアウト機能定義
                .logout(logout -> logout
                        .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                        .logoutSuccessUrl("/authentication?logout=true")
                );


        return http.build();
    }
}

 

.requestMatchers(
new AntPathRequestMatcher(“/authentication”),
new AntPathRequestMatcher(“/logout“)
).permitAll()

認可ルールを設定する。
上記により、「/logout」へのリクエストが認証不要となる。

 

.logout(logout -> logout

ログアウトに関するルールを構成するためのメソッド。
この設定により、SecurityFilterChainにLogoutFilterが組み込まれ、ログアウト処理が有効化される。

 

.logoutRequestMatcher(new AntPathRequestMatcher(“/logout”))

ログアウト対象となるURLを指定するためのメソッド。

 

.logoutSuccessUrl(“/authentication?logout=true”)

ログイン成功後に呼ばれるSimpleUrlLogoutSuccessHandlerのリダイレクト先URLを指定するためのメソッド。
クエリパラメータ「logout=true」を送信している。

 

画面制御

ログアウト用フォーム部品とメッセージ表示制御を追加する。

 

home.jsp


<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>トップページ画面</title>
</head>
<body>
	<form action="${pageContext.request.contextPath}/logout" method="post">
	    <input type="submit" value="ログアウト" />
	</form>
	<h3>トップページ</h1>
	<p>${message}</p>
</body>
</html>

 

<form action=”${pageContext.request.contextPath}/logout” method=”post“>

ログアウト用フォームを定義する。
リクエストする際のHTTPメソッドはPOSTとなるので注意。

 

login.jsp


<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>ログイン画面</title>
</head>
<body>
	<h2>ログイン</h2>
	<c:if test="${not empty param.logout}">
		<p>ログアウトしました</p>
	</c:if>

	<c:if test="${not empty SPRING_SECURITY_LAST_EXCEPTION}">
		<p style="color: red;">
			<c:out value="${SPRING_SECURITY_LAST_EXCEPTION.message}" />
		</p>
	</c:if>
	
	<form action="${pageContext.request.contextPath}/authentication/process" method="post">
		<label for="loginId">ログインID:</label>
		<input type="text" id="loginId" name="loginId" autofocus />
		<br />
		<label for="password">パスワード:</label>
		<input type="password" id="pass" name="pass" />
		<br />
		<input type="submit" value="ログイン" />
	</form>
	<br>
</body>
</html>

 

<c:if test=”${not empty param.logout}”>

上記の記載により、クエリパラメータの値を判定できる。
クエリパラメータ「logout=true」が送信されている場合、ログアウトメッセージを表示する。

 

 

まとめ

 

☑ HttpSecurity.logout(…) を定義することで、SecurityFilterChainにLogoutFilterが組み込まれてログアウト処理が有効になる

☑ LogoutFilterは登録されたログアウトハンドラを順次実行し、認証情報のクリアやセッション破棄などを行う

☑ ログアウトをリクエストする際にはPOST通信で行う

 

スポンサーリンク