プログラミングホリック

開発に関係することとか気まぐれで書いていきます

Spring Security ログイン中に認証情報を更新させる

今回はSpring Securityに関連することを書いていきます。 文章下手ですみません。見て不足や分からないこと、間違っていることがあればコメントなどもらえれば修正・追記します(多分)

1. やること

ログイン中のユーザーの権限情報を途中で書き換える

2. なぜやるのか

3. 実装

YandroidFilter.java(名前は適当です)

 ~ import省略~

@Component
public class YandroidFilter implements Filter {

 ~ doFilter以外は今回は省略~

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

         Authentication authentication= SecurityContextHolder.getContext().getAuthentication();
         String userName = ((UserDetails) authentication.getPrincipal()).getUsername();
         UserDetails user = UserDetailsService.loadUserByUsername(userName);
         Authentication updatedAuthentication = new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities());
         SecurityContextHolder.getContext().setAuthentication(updatedAuthentication);
    }

 ~ doFilter以外は今回は省略~

}

4. 解説①

ざっくり説明すると、このFilterにくるとセッション中のユーザー情報を元に改めてユーザー情報を取得し、新しく認証情報を作成し詰めなおしています。

仮にログイン中にページAへのアクセス権限が追加されたとします。 しかし、ログイン時点ではページAへのアクセス権限を持っていなかったため、セッション中の情報ではアクセス権限がありません。 このタイミングでFilterを通すリクエストがきた場合、セッション中に持つ情報にはページAへのアクセス権限がないのですが、このFilterを通すことでセッション中の認証情報が更新され、Filterの処理完了後のタイミングではページAへのアクセス権限をセッション中に持っています。

しかし、うまくセッション中の認証情報の更新ができないパターンが存在します。 それはログイン時点では権限を持っていなかったページへのアクセスを試みた(アクセスを試みたタイミングではDB上には権限を持っている)場合です。

4. 解説②

ログイン時点では権限を持っていなかったページへのアクセスを試みた(アクセスを試みたタイミングではDB上には権限を持っている)場合は権限がない感じのエラーが出ると思います。 これはFilterの呼ばれる優先順位の問題です。このFilterが呼ばれるより先に認可処理が呼ばれているため認証情報の更新にたどり着く前にエラーとなるのです。

解決方法として、HttpSecurity のaddFilterBeforeなどで認可処理より認証情報を更新するFilterの呼ばれる優先順位を上げることです。私の場合はFilterSecurityInterceptorより優先度が上になるようにしました。

参考: http://terasolunaorg.github.io/guideline/5.5.1.RELEASE/ja/Security/Authorization.html#filtersecurityinterceptor

6. おわりに

ユーザーの権限がころころ変わるようなサービスじゃなければあまりこの処理は要らないかもですが、ユーザーがログインしている最中にアクセス権限が変化する場合などは参考にしてもらえると嬉しいです。