yuusuke-roughの日記

Java,SpringBoot,趣味等

二要素認証のログイン画面遷移 in Spring Security 後半

はじめに

後半です。

4./OTPLoginはROLEがOTP_USERのみアクセスできるようにする。

5.ROLEにUSERのないユーザーの認可調整

 

内容

 

4./OTPLoginはROLEがOTP_USERのみアクセスできるようにする。と5.ROLEにUSERのないユーザーの認可調整

5.ROLEにUSERのないユーザーの認可調整

 

AuthorizationFilter で HttpServletRequests を認証する :: Spring Security

AccessDesionVoterであるRoleVoterは、ROLE_プレフィックスで始まるConfigAttribute(例えば、ROLE_OTPUSERなど)とGrantedAuthorityにあるString表現が一致するか確認するとの事。

ROLE_OTPUSERに修正。

以前作成したWebSecurity式にhasRole("USER")を追加

.requestMatchers("/room/{roomId}").access(getWebExpressionAuthorizationManager("@urlSecurity.permissionToParticipate(authentication, #roomId) and hasRole('USER')"))

 

.requestMatchers("/OTPLogin").hasRole("OTPUSER")

...

.anyRequest().hasRole("USER")

 

試しに、ROLE_OTPUSERが/OTPLoginから/homeにアクセスしようとすると...

jakarta.servlet.ServletException: Filter execution threw an exception
java.lang.StackOverflowError

 

下記が原因だった。

.exceptionHandling(exceptionhandling -> exceptionhandling.accessDeniedHandler(new DeniedAccessRoomHandler()).accessDeniedPage("/joinRoom"))

.anyRequest().hasRole("USER")

 

①successHandlerでUSERが割り当てられていても403。

②一度にユーザ認証とOTP認証をするとログインできる。

 

stack over flow前のログ

jakarta.servlet.ServletRequestWrapper.getCharacterEncoding(ServletRequestWrapper.java:103)

その下

org.springframework.security.web.access.AccessDeniedHandlerImpl.handle(AccessDeniedHandlerImpl.java:72)
    org.springframework.security.web.access.ExceptionTranslationFilter.handleAccessDeniedException(ExceptionTranslationFilter.java:208)
    org.springframework.security.web.access.ExceptionTranslationFilter.handleSpringSecurityException(ExceptionTranslationFilter.java:178)
    org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:147)
    org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:120)

 

ExceptionTranslationFilter...

 

流れを整理すると、HttpServletRequestの認可については、AuthorizationFilterがAuthenticationを受け取り、RequestMatcherDelegatingAuthorizationManagerに渡す。そこでAccessDeniedExceptionがスローされる。これを処理するのがExceptionTranslationFilter。

ExceptionTranslationFilter (spring-security-docs API) - Javadoc

 

ログだけでは判別がつかないのでどの状態ならAccessDeniedExceptionが発生しないか調査。

.authenticated()だと問題がなく、.hasRole("USER")だと発生する。

authenticated()についてはAuthenticatedAuthorizationManager→AuthenticatedAuthorizationStrategy

該当のソースコード

private static class AuthenticatedAuthorizationStrategy extends AbstractAuthorizationStrategy {

        @Override
        boolean isGranted(Authentication authentication) {
            return authentication != null && !this.trustResolver.isAnonymous(authentication)
                    && authentication.isAuthenticated();
        }

    }

 

Authenticationはnullではない。

AuthenticationTrustResolverのisAnonymousも該当しない。

 

.requestMatchers("/room/{roomId}").access(getWebExpressionAuthorizationManager("@urlSecurity.permissionToParticipate(authentication, #roomId) and hasRole('USER')"))

を消すと、上記例外は起こらない。

つまり、①上記例外②認可処理が動作していないの二つの問題が起きているという事だ。

 

access()では、AuthorizationManagerを呼び出し評価している

public AuthorizationManagerRequestMatcherRegistry access(
                AuthorizationManager<RequestAuthorizationContext> manager) {
            Assert.notNull(manager, "manager cannot be null");
            return AuthorizeHttpRequestsConfigurer.this.addMapping(this.matchers, manager);
        }

AuthorizationManagerではverify()にて、AuthorizationDecisionを認可の判定に使用している。

AuthorizationManager の check メソッドには、認可の決定を行うために必要なすべての関連情報が渡されます。特に、セキュア Object を渡すと、実際のセキュアオブジェクト呼び出しに含まれる引数をインスペクションできます。

 

→hasAuthority()に変更、AccessDeniedHandlerでのリダイレクト先をLoginで統一で修正

②→既存のSessionをクリア、再作成の上Authenticarionをセットで修正

※プロセスはGWに時間が余ったら書きたい。