🔥 TIL-Day 75 KotlinとSpringboot 06トークンによる認証実装2(認証+同じURIに対する処理-エージェントモード)


以前Spring Securityが実装されていなかった認証には、権限の検証は含まれていません.
今回は,認証権限などのURIであっても,Http Methodによって認証が必要か否かを区切る機能を実現する.
📌 権限検証の追加
Jwtタグを生成する場合、Climで権限を持ちます.
次に、JWTUtilsクラスに、JWTに対する権限検証を実行する方法を追加します.
    fun verifySellerToken(token: String): Boolean {
        try {
            val claims = getAllClaims(token)

            val expiration = claims.expiration // 만료기간 검증
            if (!expiration.after(Date())) return false

            when(val role = claims["role"]) { 
                is String -> {
                    return role.equals("ROLE_SELLER") // 권한검증
                }
                else -> throw IllegalArgumentException("타입에러")
            }
        } catch (e: JwtException) {
            return false
        } catch (e: IllegalArgumentException) {
            return false
        }
    }
📌 アクセス権を検証するためのインタフェースの定義
売り手権限の検証(ROLE SELLER)が必要な場合は、以下のパラメータで設定できます.
@Component
class RoleVerifyInterceptor(private val jwtTokenProvider: JwtTokenProvider): HandlerInterceptor {

    override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean {
        if(isPreRequest(request)) return true

        val token = JwtTokenExtractor.extract(request)
        token?.let {
            if(jwtTokenProvider.verifySellerToken(it)) return true
        }

        throw AuthenticationException("인증실패")
    }

    private fun isPreRequest(request: HttpServletRequest): Boolean {
        return request.method.equals(HttpMethod.OPTIONS)
    }
}
📌 Httpメソッドに従ってインタフェースを適用するかどうかを決定する
既存の場合、URIを要求するだけで認証を行うインタフェースが適用されるか否かを判断する.
しかしながら、Rest APIについては、通常、同じURI上で異なるHTTPメソッドが用いられる.
ex)
GET/articles=>投稿の表示(検証不要)
POST/articles=>投稿の登録(認証が必要)
IntercepterではHttpServletRequestを使用することができるので、要求URIおよびHTTPメソッドを識別することができる.しかし、各リクエストメソッドに対してブランチ操作を実行することは、非常に煩雑で重複するタスクである.
(if... if ... if ... if..........)
このような重複作業を減らすために,認証インタフェースを適用するか否かを判断する論理を排除する.
class PatternMatcherInterceptor(
    var targetInterceptor: HandlerInterceptor, // 적용될 인터셉터
) : HandlerInterceptor {

    var addPathPatterns: MutableMap<String, HttpMethod> = mutableMapOf() // 인터셉터를 적용조건 <요청URI, 요청메서드>

    fun addPathPatterns(uri: String, httpMethod: HttpMethod) = this.addPathPatterns.put(uri, httpMethod)

    override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean {
        val method = request.method
        val requestURI = request.requestURI

        // URI와 요청메서드가 일치하는 경우 targetInterceptor 를 통하도록 처리
        for (uri in addPathPatterns.keys) {
            if (requestURI.equals(uri) && addPathPatterns[uri].toString() == method) {
                return targetInterceptor.preHandle(request,response, handler)
            }
        }

        return true
    }
}
生成者入力
  • による実際の検証のためのインタフェース.
  • で実際に検証を行うURI、HTTPメソッドの設定方法を定義します.( addPathPatterns )
  • クライアント要求URIおよびHTTPメソッドがaddPathPatternsに存在する場合、実際の検証を実行するインタフェースが呼び出される.( targetInterceptor.preHandle )
  • これは,実際の認証を実行するIntercepterの前にエージェント感覚を追加して要求に適合させるIntercepterである.
    📌 IntercepterとArcgumentResolverの登録
    @Configuration
    class WebMvcConfig(
        private val tokenVerifyInterceptor: TokenVerifyInterceptor,
        private val roleVerifyInterceptor: RoleVerifyInterceptor,
        private val authenticatedUserArgumentResolver: AuthenticatedUserArgumentResolver) : WebMvcConfigurer{
    
        override fun addInterceptors(registry: InterceptorRegistry) {
            val patternMatcherInterceptor1 = PatternMatcherInterceptor(roleVerifyInterceptor) // 판매자 권한 검증을 위한 인터셉터
            patternMatcherInterceptor1.addPathPatterns("/test/auth-test/seller", HttpMethod.GET)
    
            val patternMatcherInterceptor2 = PatternMatcherInterceptor(tokenVerifyInterceptor) // 일반 검증 인터셉터
            patternMatcherInterceptor2.addPathPatterns("/items", HttpMethod.POST)
            patternMatcherInterceptor2.addPathPatterns("/test/auth-test", HttpMethod.GET)
    
            registry.addInterceptor(patternMatcherInterceptor1)
                .addPathPatterns("/**")
            registry.addInterceptor(patternMatcherInterceptor2)
                .addPathPatterns("/**")
        }
    
        override fun addArgumentResolvers(resolvers: MutableList<HandlerMethodArgumentResolver>) {
            resolvers.add(authenticatedUserArgumentResolver)
        }
    }