Spring Securityでログインしたユーザーを追跡する

1概要

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

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 HTTPSessionBindingListener

これは基本的に 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. ログインとログアウトの追跡

4.1. AuthenticationSuccessHandler の実装

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

@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コントローラとビュー

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

5.1. コントローラ

@Controller
public class UserController {

    @Autowired
    ActiveUserStore activeUserStore;

    @RequestMapping(value = "/loggedUsers", method = RequestMethod.GET)
    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. 結論

この記事では、現在ログインしているユーザーがSpring Securityアプリケーションで誰であるかを判断する方法を説明しました。

このチュートリアルの実装はhttps://github.com/eugenp/spring-security-registration[GitHubプロジェクト]にあります - これはMavenベースのプロジェクトなので、そのままインポートして実行するのは簡単なはずです。