Last active
August 29, 2015 14:20
-
-
Save gervaisb/f606a53d6c4517042946 to your computer and use it in GitHub Desktop.
Custom sping-boot module who set the `Principal` on all public methods with a parameter of type `Principal`
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
org.springframework.boot.autoconfigure.EnableAutoConfiguration=mvc.traits.WithConfigurer |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package mvc.traits; | |
import org.springframework.aop.aspectj.annotation.AspectJProxyFactory; | |
/** | |
* Authentication is a trait who inject a Principal on each public with a parameter of type Principal | |
*/ | |
public class Authentication implements Trait { | |
@Override | |
public Object configure(Object bean) { | |
AspectJProxyFactory factory = new AspectJProxyFactory(bean); | |
factory.addAspect(AuthenticationAspect.class); | |
return factory.getProxy(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package mvc.traits; | |
import org.aspectj.lang.ProceedingJoinPoint; | |
import org.aspectj.lang.annotation.Around; | |
import org.aspectj.lang.annotation.Aspect; | |
import java.security.Principal; | |
@Aspect | |
public class AuthenticationAspect { | |
@Around("execution(public * *(..))") | |
public void authenticate(ProceedingJoinPoint pjp) throws Throwable { | |
final InterceptedMethod method = new InterceptedMethod(pjp); | |
if ( method.hasParameterFor(Principal.class) ) { | |
method.setParameter(Principal.class, getCurrentPrincipal()); | |
} | |
method.proceed(); | |
} | |
private Principal getCurrentPrincipal() { | |
return new Principal() { | |
@Override | |
public String getName() { | |
return "Principal from "+getClass().getSimpleName(); | |
} | |
}; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package mvc.traits; | |
import org.aspectj.lang.ProceedingJoinPoint; | |
import org.aspectj.lang.reflect.MethodSignature; | |
import java.util.Arrays; | |
import java.util.LinkedList; | |
/** | |
* Wrapper hover a ProceedingJoinPoint who ease the manipulation of method parameters. | |
*/ | |
public class InterceptedMethod { | |
private final LinkedList<Object> parameters = new LinkedList<>(); | |
private final MethodSignature signature; | |
private final ProceedingJoinPoint joinpoint; | |
/** | |
* Create a new InterceptedMethod | |
* @param joinpoint ^null | |
*/ | |
public InterceptedMethod(ProceedingJoinPoint joinpoint) { | |
this.joinpoint = joinpoint; | |
this.signature = (MethodSignature) joinpoint.getSignature(); | |
this.parameters.addAll(Arrays.asList(joinpoint.getArgs())); | |
} | |
/** | |
* Verify if the method has a parameter for a type assignable to the received `type` | |
* @param type | |
*/ | |
public boolean hasParameterFor(Class<?> type) { | |
return indexOfParameter(type)>=0; | |
} | |
/** | |
* @effects set or replace all parameter of given `type` with the received `value` | |
* @modifies this | |
* @param type ^null the parameter type | |
* @param value the parameter value | |
* @param <T> parameter type | |
*/ | |
public <T> void setParameter(Class<T> type, T value) { | |
final Class[] types = signature.getMethod().getParameterTypes(); | |
int from = -1; | |
while ( (from = indexOfParameter(type, from+1)) >= 0 ) { | |
parameters.set(from, value); | |
} | |
} | |
public void proceed() throws Throwable { | |
joinpoint.proceed(parameters.toArray()); | |
} | |
private int indexOfParameter(Class<?> type) { | |
return indexOfParameter(type, 0); | |
} | |
private int indexOfParameter(Class<?> type, int from) { | |
Class<?>[] types = signature.getMethod().getParameterTypes(); | |
for (int i=from; i< types.length; i++) { | |
if ( types[i].isAssignableFrom(type) ) { | |
return i; | |
} | |
} | |
return -1; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package mvc.traits; | |
public interface Trait { | |
Object configure(Object o); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package mvc.traits; | |
import org.springframework.boot.autoconfigure.AutoConfigureAfter; | |
import org.springframework.context.annotation.Import; | |
import java.lang.annotation.Retention; | |
import java.lang.annotation.RetentionPolicy; | |
@AutoConfigureAfter | |
@Retention(RetentionPolicy.RUNTIME) | |
@Import(WithConfigurer.class) | |
public @interface With{ | |
Class<? extends Trait>[] value(); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package mvc.traits; | |
import org.apache.commons.logging.Log; | |
import org.apache.commons.logging.LogFactory; | |
import org.springframework.beans.BeansException; | |
import org.springframework.beans.factory.config.BeanPostProcessor; | |
import org.springframework.context.ApplicationContext; | |
import org.springframework.context.ApplicationContextAware; | |
import org.springframework.context.annotation.Configuration; | |
import org.springframework.core.annotation.AnnotationUtils; | |
import java.util.Arrays; | |
@Configuration | |
class WithConfigurer implements BeanPostProcessor, ApplicationContextAware { | |
private static final Log LOG = LogFactory.getLog(WithConfigurer.class); | |
private ApplicationContext ctxt; | |
@Override | |
public void setApplicationContext(ApplicationContext ctxt) throws BeansException { | |
this.ctxt = ctxt; | |
} | |
@Override | |
public Object postProcessBeforeInitialization(Object bean, String name) throws BeansException { | |
if (bean == null || AnnotationUtils.findAnnotation(bean.getClass(), With.class) == null) | |
return bean; | |
final With with = AnnotationUtils.findAnnotation(bean.getClass(), With.class); | |
if ( LOG.isDebugEnabled() ) | |
LOG.debug("Adding traits "+ Arrays.toString(with.value())+" on bean "+name+" : "+bean+"."); | |
for (final Class<? extends Trait> type: with.value()) { | |
Trait trait = (Trait) ctxt.getAutowireCapableBeanFactory().createBean(type, 0, true); | |
bean = trait.configure(bean); | |
assert bean!=null : "Trait ["+type+"] cannot nullify bean ["+name+"]"; | |
} | |
return bean; | |
} | |
@Override | |
public Object postProcessAfterInitialization(Object o, String s) throws BeansException { | |
return o; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@RestController @With({Authentication.class}) | |
public class SampleCtrl { | |
@RequestMapping("/prove") | |
public String withPrincipal(Principal principal) { | |
return principal.getName(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment