Description
Describe the bug
When using @RolesAllowed annotation on interface level and inheriting that interface with another interface spring detects @RolesAllowed as a duplicate.
`
org.springframework.core.annotation.AnnotationConfigurationException: Found more than one annotation of type interface jakarta.annotation.security.RolesAllowed attributed to public void com.example.demo.Service1.doSomethig() Please remove the duplicate annotations and publish a bean to handle your authorization logic.
at org.springframework.security.authorization.method.AuthorizationAnnotationUtils.findUniqueAnnotation(AuthorizationAnnotationUtils.java:65)
at org.springframework.security.authorization.method.Jsr250AuthorizationManager$Jsr250AuthorizationManagerRegistry.findAnnotation(Jsr250AuthorizationManager.java:115)
at org.springframework.security.authorization.method.Jsr250AuthorizationManager$Jsr250AuthorizationManagerRegistry.findJsr250Annotation(Jsr250AuthorizationManager.java:108)
at org.springframework.security.authorization.method.Jsr250AuthorizationManager$Jsr250AuthorizationManagerRegistry.resolveManager(Jsr250AuthorizationManager.java:91)
at org.springframework.security.authorization.method.AbstractAuthorizationManagerRegistry.lambda$getManager$1(AbstractAuthorizationManagerRegistry.java:50)
at java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1708)
at org.springframework.security.authorization.method.AbstractAuthorizationManagerRegistry.getManager(AbstractAuthorizationManagerRegistry.java:50)
at org.springframework.security.authorization.method.Jsr250AuthorizationManager.check(Jsr250AuthorizationManager.java:82)
at org.springframework.security.authorization.method.Jsr250AuthorizationManager.check(Jsr250AuthorizationManager.java:48)
at org.springframework.security.config.annotation.method.configuration.DeferringObservationAuthorizationManager.check(DeferringObservationAuthorizationManager.java:47)
at org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor.attemptAuthorization(AuthorizationManagerBeforeMethodInterceptor.java:252)
at org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor.invoke(AuthorizationManagerBeforeMethodInterceptor.java:198)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:756)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708)
at com.example.demo.Service1$$SpringCGLIB$$0.doSomethig(<generated>)
at com.example.demo.DemoApplicationTests.contextLoads(DemoApplicationTests.java:18)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:727)
at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:156)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:147)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:86)
at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103)
at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:92)
at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:86)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:217)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:213)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:138)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
`
To Reproduce
Please see the below code. The doSomething method is annotated with RolesAllowed. The subinterface not. But still the spring duplicate check will detect as duplicate.
public interface MyGenericService { @RolesAllowed("ABC") void doSomethig(); }
public interface Specific1 extends MyGenericService { }
@Service public class Service1 implements Specific1 { @Override public void doSomethig() { System.out.println("Do Something1."); } }
Expected behavior
Should not fail with duplicate annotation exception since its not 'redefining'.
Sample
Please run the 'AnnotationIssueTests' unit test.
Reports that include a sample will take priority over reports that do not.
At times, we may require a sample, so it is good to try and include a sample up front.