Running aspect on an Annotated Class using AspectJ [duplicate] - java

I'm writing an aspect to log Request and Response of each API call in a controller.
I want to be able to use this annotation on a class, hence used #Target(ElementType.TYPE)
Previously I had added #Target(ElementType.Method) and I was using this annotation on methods and it was working fine.
Now I want to change it to #Target(ElementType.TYPE)
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
public #interface ReLogger {}
#Aspect
#Component
public class ReLoggerAspect {
public static final Logger log = LoggerFactory.getLogger("ReLoggerAspect");
#PostConstruct
private void postConstruct() {
log.info("ReLoggerAspect Created");
}
#Around("#annotation(ReLogger)")
private Object reqLoggingAspect(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("Request {}",jointPoint.getArgs()[0);
}
}
Using #ReLoggerAspect on a class
#RestController
#RequestMapping(value = "....", produces = { "application/json" })
#ReLogger
public class Samplecontroller {
/** Some logic here**/.....
}
It doesn't print the Request when an API SampleController is invoked

Your premise that #annotation would match type annotations is wrong, see (Spring AOP manual](https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#aop-pointcuts-designators):
#within: Limits matching to join points within types that have the given annotation (the execution of methods declared in types with the given annotation when using Spring AOP).
#annotation: Limits matching to join points where the subject of the join point (the method being executed in Spring AOP) has the given annotation.
Thus, you ought to use #within(fully.qualified.AnnotationType).

Related

Inject multiple beans of the same type and automatically select between them based on generic type

I have two (more in the future) implementations of ImportantService – VeryImportantService and LessImportantService:
public interface ImportantService<T extends ImportantRequest> {}
#Service
public class VeryImportantService implements ImportantService<VeryImportantRequest> {}
#Service
public class LessImportantService implements ImportantService<LessImportantRequest> {}
And then I have a controller, in which I want to inject all of the implementations of ImportantService:
#RequiredArgsConstructor
#RestController
#RequestMapping("/api/important")
public class ImportantController<T extends ImportantRequest> {
private final ImportantService<T> importantService;
#PostMapping
public ResponseEntity<ImportantResponse> create(#RequestBody #Valid T request) {
// very important code here
}
}
Obviously, such king of injecting fails:
UnsatisfiedDependencyException: Error creating bean with name 'importantController' defined in file ...
...
Consider marking one of the beans as #Primary, updating the consumer to accept multiple beans, or using #Qualifier to identify the bean that should be consumed
What I want is:
Inject all of the implementations of ImportantService, and then, based on the T automatically select required bean. I know I can add method to ImportantService, which returns the type that implementation works with and then inject ImportantService as List<ImportantService> importantServices and then filter like this:
importantServices.stream()
.filter(importantService -> importantService.getType().equals(request.getClass()))
.findFirst()
.ifPresent(importantService -> importantService.doImportantJob(request));
BUT! I have hundreds of services to refactor like this and I really don't want to write additional logic to controllers.
I know about #Conditional annotation and Condition interface, but AFAIK there's no way to make them do what I want.
Why not implement the proxy pattern?
example:
#Service
#Primary
#RequiredArgsConstructor
public class ImportantServiceProxy implements ImportantService<T extends ImportantRequest> {
private final List<ImportantService> importantServices;
private ImportantService getImportantService(ImportantRequest request){
return this.importantServices.stream()
.filter(importantService -> importantService.getType().equals(request.getClass()))
.findFirst()
.get();
}
public void doImportantJob(ImportantRequest request){
this.getImportantService(request).doImportantJob(request);
}
}
Then in your controller you can call the function without check the type.
#RequiredArgsConstructor
#RestController
#RequestMapping("/api/important")
public class ImportantController<T extends ImportantRequest> {
private final ImportantService<T> importantService;
#PostMapping
public ResponseEntity<ImportantResponse> create(#RequestBody #Valid T request) {
importantService.doImportantJob(request);
}
}
what you want is a list of beans which are of type ImportantService
so you have to declare a variable like this.
final List<ImportantService> importantServices;
demoController(List<ImportantService> importantServices) {
this.importantServices = importantServices;
}

Can anyone give an insight to how the #PreAuthorize annotation works?

I know Spring Security has an abstract class SecurityExpressionRoot. In that we have methods like hasAuthority(String var1), hasRole(String var1) etc implemented.
Spring also provide a #PreAuthorize annotation to be used on the method level we pass a single value within that annotation like
#PreAuthorize("hasRole('ROLE_ABC')")
The annotation #interface is like
package org.springframework.security.access.prepost;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Inherited
#Documented
public #interface PreAuthorize {
String value();
}
I want to know how this annotation triggers the particular method from SecurityExpressionRoot.
Spring security uses Aspect Oriented Programming (AOP) to weave/intertwine security code into your own code base. The way this works in Spring is by using annotations that define injection points (cfr. pointcuts) to allow execution of extra logic before/after/within your own code (cfr. advice).
Interceptors scan your codebase for join points (i.e. for Spring this is always method execution when marked with specific annotations) and will execute the additional specific logic depending on which interception point (i.e. interface) you have used.
To enable this behaviour one could add a configuration:
#Configuration
#EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true, proxyTargetClass = true)
public class ConfigGlobalMethodSecurity extends GlobalMethodSecurityConfiguration {
...
}
For PreAuthorize in particular, the PrePostAdviceReactiveMethodInterceptor is responsible for finding your methods annotated with PreAutorize. In turn, this will delegate to PreInvocationAuthorizationAdvice which is configured here ReactiveMethodSecurityConfiguration as ExpressionBasedPreInvocationAdvice.
Internally this uses the default expression handler DefaultMethodSecurityExpressionHandler that creates a SecurityExpressionRoot. The actual implementation of this SecurityExpressionRoot will define how your expression within your PreAuthorize will be handled and what logic needs to be executed.
The SecurityExpressionRoot defines which expressions are allow within your PreAuthorize, such as hasRole.
To add additional expressions or extend upon the default permission logic you need to provide a custom implementation of SecurityExpressionRoot with optionally a custom PermissionEvaluator. E.g. if you would want to write #PreAuthorize("hasKnowledgeOf('AOP')").
public class CustomMethodSecurityExpressionRoot
extends SecurityExpressionRoot
implements MethodSecurityExpressionOperations {
private final PermissionEvaluator permissionEvaluator;
private final Authentication authentication;
private Object filterObject;
private Object returnObject;
private Object target;
public CustomMethodSecurityExpressionRoot(
Authentication authentication,
PermissionEvaluator permissionEvaluator) {
super(authentication);
this.authentication = authentication;
this.permissionEvaluator = permissionEvaluator;
super.setPermissionEvaluator(permissionEvaluator);
}
// new expression to check if the requested knowledge is present
public boolean hasKnowledgeOf(String context) {
// provide logic that performs the check
}
#Override
public void setFilterObject(Object filterObject) {
this.filterObject = filterObject;
}
#Override
public Object getFilterObject() {
return filterObject;
}
#Override
public void setReturnObject(Object returnObject) {
this.returnObject = returnObject;
}
#Override
public Object getReturnObject() {
return returnObject;
}
#Override
public Object getThis() {
return target;
}
}
and
#Configuration
public class CustomPermissionEvaluator
implements PermissionEvaluator {
#Override
public boolean hasPermission(
Authentication authentication,
Object targetDomainObject,
Object permission) {
// define your custom permission logic here
}
#Override
public boolean hasPermission(
Authentication authentication,
Serializable targetId,
String targetType,
Object permission) {
// define your custom permission logic here
}
}
To finish the configuration and pass the evaluator to the expression root.
public class CustomMethodSecurityExpressionHandler
extends DefaultMethodSecurityExpressionHandler {
PermissionEvaluator permissionEvaluator;
public CustomMethodSecurityExpressionHandler(PermissionEvaluator permissionEvaluator) {
this.permissionEvaluator = permissionEvaluator;
super.setPermissionEvaluator(permissionEvaluator);
}
#Override
protected MethodSecurityExpressionOperations createSecurityExpressionRoot(
Authentication authentication,
MethodInvocation invocation) {
CustomMethodSecurityExpressionRoot root = new CustomMethodSecurityExpressionRoot(
authentication,
permissionEvaluator);
root.setTrustResolver(new AuthenticationTrustResolverImpl());
root.setRoleHierarchy(getRoleHierarchy());
return root;
}
}
and
#Configuration
#EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true, proxyTargetClass = true)
public class ConfigGlobalMethodSecurity extends GlobalMethodSecurityConfiguration {
#Autowired CustomPermissionEvaluator permissionEvaluator;
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new CustomMethodSecurityExpressionHandler(permissionEvaluator);
}
}
An annotation does nothing and is nothing more than metadata. Adding an annotation to a method and without a piece of code that acts on that annotation it does nothing. So you need an annotation processor of some sort.
For most of the annotations used in Spring this means they are applied by AOP (Aspect Oriented Programming). In Spring this means, by default, that is done based on a proxy. This proxy intercepts the actual method call, applies some additional logic before/after calling the actual method.
The additional logic is provided by a MethodInterceptor or #Aspect when using AspectJ based aspects. In the case of the #PreAuthorize annotation this is done by the MethodSecurityInterceptor.
What the MethodSecurityInterceptor does is, based on the #PreAuthorize annotation and the expression written in there (the expression is a SpEL expression) determine if the current user is authorized to access the method.
The MethodSescurityInterceptor can be enabled either manually (doing the whole configuration yourself, quite tedious and error-prone) or by adding the #EnableGlobalMethodSecurifty annotation. The latter will add an #Configuration class and some processor to automatically configure the needed infrastructure classes.
An aspect invocation after evaluating the string given in the #PreAuthorize annotation.
For details I think you need to dig down in Spring source code. Maybe start around here: https://github.com/spring-projects/spring-security/blob/main/core/src/main/java/org/springframework/security/access/prepost/PrePostAdviceReactiveMethodInterceptor.java#nn
#PreAuthorize is equal to #Secured and #RolesAllowed.
#PreAuthorize("hasRole('ROLE_SPITTER')")
public void addSpittle(Spittle spittle) {
// ...
}
But the String argument to #PreAuthorize is a SpEL expression
With SpEL expressions
guiding access decisions, far more advanced security constraints can be written
#PreAuthorize(
"(hasRole('ROLE_SPITTER') and #spittle.text.length() <= 140)"
+"or hasRole('ROLE_PREMIUM')")
public void addSpittle(Spittle spittle) {
// ...
}

Spring + AspectJ pointcut on CrudRepository and Annotation

I have #Tenantable annotation to decide for pointCut :
#Target({ElementType.TYPE, ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
#Inherited
public #interface Tenantable {
}
this my aspect :
#Slf4j
#Aspect
#Configuration
public class TenancyAspect {
#Pointcut("execution(public * *(..))")
public void publicMethod() {}
#Around("publicMethod() && #within(com.sam.example.aspect.aspectexample.model.Tenantable)")
public Object tenatable(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("my operations ...");
return joinPoint.proceed();
}
}
This is working without any problem for this service class :
#Tenantable
#Service
public class MyService(){
public void doSomething(){
...
}
}
my aspect is running when I call doSomething() method, It is ok but I want to implement aspect for CrudRepository interface that belongs spring data.
I have changed my Aspect to achieve this like below :
#Slf4j
#Aspect
#Configuration
public class TenancyAspect {
#Pointcut("execution(public * *(..))")
public void publicMethod() {}
#Pointcut("this(org.springframework.data.repository.Repository)")
public void repositoryExec(){}
#Around("publicMethod() && repositoryExec() && #within(com.sam.example.aspect.aspectexample.model.Tenantable)")
public Object tenatable(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("my operations ...");
return joinPoint.proceed();
}
}
this is repository :
#Tenantable
#Repository
public interface MyRepository extends CrudRepository{
}
But it doesn't work when I call any method inside of MyRepository.
Is there anyway to do this?
Edit :
It works for all repositories when I apply these :
#Pointcut("execution(public * org.springframework.data.repository.Repository+.*(..))")
and exclude this :
#within(com.sam.example.aspect.aspectexample.model.Tenantable)
But I need this anotation to apply it for specific repositories.
Having taken another look, I think I know what is going on here: You are assuming that just because you made your annotation #Inherited, it will be inherited by implementing classes if you annotate an interface. But this assumption is wrong. #Inherited only works in exactly one case: when extending an annotated base class. It does not work for annotated interfaces, methods etc. This is also documented here:
Note that this meta-annotation type has no effect if the annotated type is used to annotate anything other than a class. Note also that this meta-annotation only causes annotations to be inherited from superclasses; annotations on implemented interfaces have no effect.
As soon as you annotate your implementing class, it works.
Your repositoryExec pointcut should end with + to advice all subclass of Repository
#Pointcut("this(org.springframework.data.repository.Repository+)")

Custom Spring annotation not working

I'm trying out a simple custom Spring annotation, but it seems like Spring isn't executing anything when i slap the annotation on a method...anyone have any ideas? I see no logging at all. Maybe i need some aop dependency?
#Aspect
#Component
public class LethargicLoggerAspect {
private final Logger log = LoggerFactory.getLogger(getClass());
#Around("#annotation(LethargicLogger)")
public Object logSlowExecutionTime(ProceedingJoinPoint
proceedingJoinPoint) throws
Throwable {
log.error("HIIIIIIIIII david");
Object proceed = proceedingJoinPoint.proceed();
return proceed;
}
}
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface LethargicLogger {
}
It looks good, you need to add package to the #Around annotation.
#Around("#annotation(com.example.package.LethargicLogger)")
public Object logSlowExecutionTime(ProceedingJoinPoint
proceedingJoinPoint) throws
Throwable {
}

Java EE: Trying to bind interceptor via annotation with multiple values

I have a weird problem trying to add a parameterized logging interceptor to my Java EE 7 application.
I went from a class interceptor using the #Interceptors annotation to writing a custom interceptor binding. What I have not looks like this...
The annotation
#Inherited
#InterceptorBinding
#Retention(RetentionPolicy.RUNTIME)
#Target({
ElementType.TYPE,
ElementType.METHOD
})
public #interface LogMethodCall {
MethodLogger logLevel() default MethodLogger.INFO;
}
The interceptor
#Slf4j
#LogMethodCall
#Interceptor
#Priority(Interceptor.Priority.APPLICATION)
public class ActionInterceptor {
#AroundInvoke
protected Object protocolInvocation(final InvocationContext ic) throws Exception {
log.info(
"{}: <{}> called. Parameters={}",
ic.getTarget().getClass().getName(),
ic.getMethod().getName(),
ic.getParameters());
return ic.proceed();
}
}
The usage
#GET
#Path("/{account}")
#LogMethodCall
public void inboxes(#Suspended AsyncResponse response, #PathParam("account") String account) {
...
}
When I use it like this everything works OK.
Buy when I try to use change the logLevel and use
#LogMethodCall(logLevel=MethodLogger.DEBUG)
then my interceptor never gets called.
What am I missing here? Why setting the annotation value breaks the code?
If you say that your interceptor is catching only when the value is INFO, you can consider to put your logLevel() attribute as #Nonbinding.
By default, qualifier arguments are considered for matching bean qualifiers to injection point qualifiers. A #Nonbinding argument is not considered for matching.
Try this:
#Inherited
#InterceptorBinding
#Retention(RetentionPolicy.RUNTIME)
#Target({
ElementType.TYPE,
ElementType.METHOD
})
public #interface LogMethodCall {
#Nonbinding MethodLogger logLevel() default MethodLogger.INFO;
}

Categories