yuusuke-roughの日記

Java,SpringBoot,趣味等

特定のURLで認可処理を行う in Spring Security

はじめに

※年末の記録 + 1月1日の記録に修正を加え投稿しました。

 

今回は、最終目標の「特定のURLにてルームメンバーかチェックする認可処理を行う実装」です。

 

前回はロールと権限の実装で、認可について学びました。

Role実装 in Spring Security - yuusuke-roughの日記

 

Spring Security6.0.1へ移行で書き方が変わった話

Spring Security 6.0.1にバージョンを変更した話 - yuusuke-roughの日記

 

ーー余談:実装以外の悩みーー

ドキュメントの読み込みが段々深くなった分、「作成しているアプリに必要な非機能要件って...」と思うようになりました。

今までは、気付いた課題...例えば、多重ログインやアカウント削除時のセッションの処理等は適宜対策してきました。しかし、これでは行き当たりばったりの良くない実装と思います。

そこで、先週から「ずっと受けたかったソフトウェアエンジニアリングの新人研修」を再読のうえ、外部・内部設計書の作成に取り掛かりました。

まだまだ先になりますが、完成すれば、「何が足りないのか」と意見をどなたかに伺いやすいのではないかと考えたからです。

 

※年末年始はSpringBoot3.0.0移行時に発生したエラーの調査のため一旦保留中

 

内容

今回もこちらを使用。

式ベースのアクセス制御 :: Spring Security - リファレンス

 

まずは、認可を行う式を準備

public class URLSecurity {

    
    
    private final RoomRepository roomRepository;
    
    @Autowired
    URLSecurity( RoomRepository roomRepository){
        this.roomRepository = roomRepository;
    }
    
    
    public boolean permissionToParticipate(Authentication authentication, String roomURL) {
        Room room = roomRepository.findByRoomURL(roomURL);
        
        List<RoomParticipant_users> participatingUsers = room.getParticipatingUsers();
        for(int i=0; i < participatingUsers.size(); i++) {
            String participatingUserName = participatingUsers.get(i).getParticipant().getMail();
            
            if(authentication.getName() .equals(participatingUserName)) {
                return true;
            }
            
        }
        
        return false;
        
    }

}

 

SecurityConfigには、下記を追記

...

anyRequest().authenticated()

    .requestMatchers("/room/{id}").access(new WebExpressionAuthorizationManager("@urlSecurity.permissionToParticipate"))

 

すると、下記のエラーが発生...

 java.lang.IllegalStateException: Can't configure mvcMatchers after anyRequest

anyRequestの後に追加していたため発生。requestMatchersを先に追加し修正完了。

 

次に、下記のエラーが発生...

 Failed to evaluate expression '@urlSecurity.permissionToParticipate(authentication, #roomId)'

...

No bean resolver registered in the context to resolve access to bean 'URLSecurity'

 

はじめにurlSecurityのbeanはあるのだろうか?

Springで定義済のBean名一覧を出力する - endokのブログ

 

...

httpSessionEventPublisher
urlSecurity
org.springframework.web.socket.config.annotation.DelegatingWebSocketMessageBrokerConfiguration
stompWebSocketHandlerMapping

...

 

urlSecurityはありました。

そうなると、Bean ResolverにこのBeanを登録する実装をしなければいけない可能性を考えた。

 

SpELとは...

Spring Framework コアテクノロジー - リファレンス

 

評価の仕組みについて

EvaluationContext インターフェースは、式を評価してプロパティ、メソッド、またはフィールドを解決し、型変換を実行する際に使用されます。

 

Bean Resolverとは...

BeanResolver (Spring Framework API) - Javadoc

A bean resolver can be registered with the evaluation context and will kick in for bean references: @myBeanName and &myBeanName expressions. The & variant syntax allows access to the factory bean where relevant.

 

しかし、今回はWebExpressionAuthorizationManagerで与えられた式を評価しようとしている。改めて調べると、no bean resolverの原因については下記にあった。

 

java - Spring Boot 3.0/Security 6.0 Migration - "EL1057E: No bean resolver registered in the context to resolve access to bean..." in SecurityFilterChain - Stack Overflow

Bean empFilter is registered in the ApplicationContext of the test. In Spring Security 6, WebExpressionAuthorizationManager uses an instance of DefaultHttpSecurityExpressionHandler as its SpEL expression handler. DefaultHttpSecurityExpressionHandler has no access to the application context unless explicitly configured.

 

少し遡るが、初めにBeanがApplicationContextに登録されているのは確認していた。

また、上記の通り、SpEL expression handlerとしてDefaultHttpSecurityExpressionHandlerが使用されるので、SpELについてはここで処理と考える。

そして、「設定しないとapplication contextにアクセスできない。」...ここが原因とわかった。

 

以上、カスタム認可の実装でした。

あとは、認可処理でユーザーが弾かれた場合にForbiddenになるので、その処理を実装したいです。

 

感想

実装のきっかけはリファレンスからとはいえ、リファレンスのみで完結できる問題ではありませんでした。

「no bean resolver」という言葉に引っ張られ、SpELのリファレンスにあるようなbean resolverを含める方法を考え、遠回りしました。

結果的に、SpringSecurity内でSpELを扱うというWebExpressionAuthorizationManagerのSecurityExpressionHandlerの話にたどり着き、解決しました。

 

独学の恐ろしいところは、着眼点がおかしいと時間がかかる事です。

そして、その時間を短くするために、何度も間違った調べ方、考え方を修正していく作業は少しずつ上達を実感させてくれます。

 

また、「はじめに」の項の「余談」に記載したように、新たに設計の問題が加わりました。

全方面で突き詰めていかなくてはいけない、自己完結できない問題に気づいたのは大きな一歩だと思います。

まずは、完成を目指して進めます。

 

全然関係のない話

元旦は、従弟や妹夫婦と過ごしました。

高校生になるまではよく一緒にいたので、兄弟のような感覚で、懐かしさと今も関わりのある事に不思議さを感じ、楽しいです。

 

あれ、この中で自分だけ音楽やった事ない...