How to implement Quarkus interceptor with parameter annotations

63 views Asked by At

Here is my code, I want to write a Interceptor which could receive some roles (Array<String>), but I have trouble accepting parameters

import jakarta.annotation.Priority
import jakarta.interceptor.AroundInvoke
import jakarta.interceptor.Interceptor
import jakarta.interceptor.InterceptorBinding
import jakarta.interceptor.InvocationContext
import jakarta.ws.rs.core.HttpHeaders
import java.lang.annotation.Inherited


@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@InterceptorBinding
@Inherited
annotation class RolesRequired( val rolesRequired: Array<String>)

@Interceptor
@RolesRequired(["role1", "role2"])
@Priority(Interceptor.Priority.PLATFORM_BEFORE + 1)
class RolesRequiredInterceptor(
    private val header: HttpHeaders
) {
    @AroundInvoke
    fun intercept(context: InvocationContext): Any? {
        println("[check-role]")
        val rolesRequired = this.javaClass.getAnnotation(
            RolesRequired::class.java
        ).rolesRequired
        println("[roles]: ${rolesRequired.size}")
//        TODO check roles
        return context.proceed()
    }
}

What puzzles me is the @RolesRequired(["role1", "role2"]) line, if I write this line, then this annotation @RolesRequired would not work unless it's param is ["role1", "role2"], it seems that the value of the param rolesRequired defined at RolesRequiredInterceptor is the only value that can let this annotation RolesRequired work.

I've tried to remove @RolesRequired(["role1", "role2"]) from the class RolesRequiredInterceptor but was told by IDE that "@Interceptor must specify at least one interceptor binding". And param rolesRequired seems to be necessary when define class RolesRequiredInterceptor.

In the following cases, interceptor @RolesRequired seems not executed, quarkus prints noting about that when I call login()

    @RolesRequired(rolesRequired = ["role1"])
    fun login(input: LoginData): LoginResponse {...}

    @RolesRequired(rolesRequired = ["role2"])
    fun login(input: LoginData): LoginResponse {...}

    @RolesRequired(rolesRequired = ["role2", "role1"])
    fun login(input: LoginData): LoginResponse {...}

only in this case, quarkus prints

[check-role] 
[roles]: 2
    @RolesRequired(rolesRequired = ["role1", "role2"])
    fun login(input: LoginData): LoginResponse {...}
2

There are 2 answers

1
Bart On
  @Interceptor
@Priority(Interceptor.Priority.PLATFORM_BEFORE + 1)
class RolesRequiredInterceptor {
    @AroundInvoke
    fun intercept(context: InvocationContext): Any? {
        println("[check-role]")
        val method = context.method
        val rolesRequiredAnnotation = method.getAnnotation(RolesRequired::class.java)
        val rolesRequired = rolesRequiredAnnotation?.rolesRequired ?: arrayOf()
        println("[roles]: ${rolesRequired.size}")
        // TODO: Check roles against the actual roles
        return context.proceed()
    }
}

@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class RolesRequired(val rolesRequired: Array<String>)

// Example usage:
@RolesRequired(rolesRequired = ["role1", "role2"])
fun login(input: LoginData): LoginResponse {...}

Try this tell me if it works

0
Prixii On

I answered my own question

the solution is: Add another annotation @Nonbinding before the parameter. in kotlin, like : annotation class RolesRequired(@get:Nonbinding val rolesRequired: Array<String>)

related github issue: https://github.com/quarkusio/quarkus/issues/21765