1. 概要

このクイックチュートリアルでは、Springセキュリティを使用して、アプリケーションに現在ログインしているユーザーを追跡する方法の例を示します。

この目的のために、ログイン時にユーザーを追加し、ログアウト時に削除することで、ログインしているユーザーのリストを追跡します。

HttpSessionBindingListener を利用して、ユーザー情報がシステムにログインまたはシステムからログアウトすることに基づいて、ユーザー情報がセッションに追加またはセッションから削除されるたびに、ログインしているユーザーのリストを更新します。

2. アクティブユーザーストア

簡単にするために、ログインしたユーザーのメモリ内ストアとして機能するクラスを定義します。

public class ActiveUserStore {

    public List<String> users;

    public ActiveUserStore() {
        users = new ArrayList<String>();
    }

    // standard getter and setter
}

これをSpringコンテキストの標準beanとして定義します。

@Bean
public ActiveUserStore activeUserStore(){
    return new ActiveUserStore();
}

3. HTTPSBindingListener

次に、 HTTPSBindingListener インターフェイスを使用して、現在ログインしているユーザーを表すラッパークラスを作成します。

これは基本的に、タイプ HttpSessionBindingEvent のイベントをリッスンします。これらのイベントは、値が設定または削除されるたびに、つまりHTTPセッションにバインドまたは非バインドされるたびにトリガーされます。

@Component
public class LoggedUser implements HttpSessionBindingListener {

    private String username; 
    private ActiveUserStore activeUserStore;
    
    public LoggedUser(String username, ActiveUserStore activeUserStore) {
        this.username = username;
        this.activeUserStore = activeUserStore;
    }
    
    public LoggedUser() {}

    @Override
    public void valueBound(HttpSessionBindingEvent event) {
        List<String> users = activeUserStore.getUsers();
        LoggedUser user = (LoggedUser) event.getValue();
        if (!users.contains(user.getUsername())) {
            users.add(user.getUsername());
        }
    }

    @Override
    public void valueUnbound(HttpSessionBindingEvent event) {
        List<String> users = activeUserStore.getUsers();
        LoggedUser user = (LoggedUser) event.getValue();
        if (users.contains(user.getUsername())) {
            users.remove(user.getUsername());
        }
    }

    // standard getter and setter
}

リスナーには、実装する必要のある2つのメソッドがあります。リッスンしているイベントをトリガーする2種類のアクションに対して、 valueBound() valueUnbound()です。 リスナーを実装するタイプの値が設定またはセッションから削除されるか、セッションが無効化されるたびに、これら2つのメソッドが呼び出されます。

この場合、 valueBound()メソッドはユーザーがログインしたときに呼び出され、 valueUnbound()メソッドはユーザーがログアウトしたときまたはセッションが期限切れになったときに呼び出されます。

各メソッドで、イベントに関連付けられた値を取得し、値がセッションにバインドされているかバインドされていないかに応じて、ログインしているユーザーのリストにユーザー名を追加または削除します。

4. ログインとログアウトの追跡

次に、アクティブなユーザーをセッションに追加したり、セッションから削除したりできるように、ユーザーが正常にログインまたはログアウトしたタイミングを追跡する必要があります。 Spring Securityアプリケーションでは、これはAuthenticationSuccessHandlerおよびLogoutSuccessHandlerインターフェースを実装することで実現できます。

4.1. AuthenticationSuccessHandlerの実装

ログインアクションでは、セッションおよびへのアクセスを提供する onAuthenticationSuccess()メソッドをオーバーライドすることにより、セッションの属性としてログインしているユーザーのユーザー名を設定します。 ] authentication オブジェクト:

@Component("myAuthenticationSuccessHandler")
public class MySimpleUrlAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    @Autowired
    ActiveUserStore activeUserStore;
    
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, 
      HttpServletResponse response, Authentication authentication) 
      throws IOException {
        HttpSession session = request.getSession(false);
        if (session != null) {
            LoggedUser user = new LoggedUser(authentication.getName(), activeUserStore);
            session.setAttribute("user", user);
        }
    }
}

4.2. LogoutSuccessHandlerの実装

ログアウトアクションの場合、 LogoutSuccessHandlerインターフェイスのonLogoutSuccess()メソッドをオーバーライドして、ユーザー属性を削除します。

@Component("myLogoutSuccessHandler")
public class MyLogoutSuccessHandler implements LogoutSuccessHandler{
    @Override
    public void onLogoutSuccess(HttpServletRequest request, 
      HttpServletResponse response, Authentication authentication)
      throws IOException, ServletException {
        HttpSession session = request.getSession();
        if (session != null){
            session.removeAttribute("user");
        }
    }
}

5. コントローラとビュー

上記のすべての動作を確認するために、URL “ / users” のコントローラーマッピングを作成します。これにより、ユーザーのリストが取得され、モデル属性として追加され、usersが返されます。 .html ビュー:

5.1. コントローラ

@Controller
public class UserController {
    
    @Autowired
    ActiveUserStore activeUserStore;

    @GetMapping("/loggedUsers")
    public String getLoggedUsers(Locale locale, Model model) {
        model.addAttribute("users", activeUserStore.getUsers());
        return "users";
    }
}

5.2. Users.html

<html>
<body>
    <h2>Currently logged in users</h2>
    <div th:each="user : ${users}">
        <p th:text="${user}">user</p>
    </div>
</body>
</html>

6. Sessionregistryを使用する代替方法

現在ログインしているユーザーを取得するもう1つの方法は、ユーザーとセッションを管理するクラスであるSpringのSessionRegistryを利用することです。 このクラスには、ユーザーのリストを取得するためのメソッド getAllPrincipals()があります。

ユーザーごとに、メソッド getAllSessions()を呼び出すことにより、すべてのセッションのリストを表示できます。 現在ログインしているユーザーのみを取得するには、 getAllSessions()の2番目のパラメーターを false に設定して、期限切れのセッションを除外する必要があります。

@Autowired
private SessionRegistry sessionRegistry;

@Override
public List<String> getUsersFromSessionRegistry() {
    return sessionRegistry.getAllPrincipals().stream()
      .filter(u -> !sessionRegistry.getAllSessions(u, false).isEmpty())
      .map(Object::toString)
      .collect(Collectors.toList());
}

SessionRegistry クラスを使用するには、以下に示すように、Beanを定義し、それをセッション管理に適用する必要があります。

http
  .sessionManagement()
  .maximumSessions(1).sessionRegistry(sessionRegistry())

...

@Bean
public SessionRegistry sessionRegistry() {
    return new SessionRegistryImpl();
}

7. 結論

この記事では、SpringSecurityアプリケーションに現在ログインしているユーザーを特定する方法を示しました。

このチュートリアルの実装は、 GitHubプロジェクトにあります。これはMavenベースのプロジェクトであるため、そのままインポートして実行するのは簡単です。