はじめに
後半です。
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に時間が余ったら書きたい。