I have a service class implemented in Java 6 / Spring 3 that needs an annotation to restrict access by role.
I have defined an annotation called RequiredPermission that has as its value attribute one or more values from an enum called OperationType:
public #interface RequiredPermission {
/**
* One or more {#link OperationType}s that map to the permissions required
* to execute this method.
*
* #return
*/
OperationType[] value();}
public enum OperationType {
TYPE1,
TYPE2;
}
package com.mycompany.myservice;
public interface MyService{
#RequiredPermission(OperationType.TYPE1)
void myMethod( MyParameterObject obj );
}
package com.mycompany.myserviceimpl;
public class MyServiceImpl implements MyService{
public myMethod( MyParameterObject obj ){
// do stuff here
}
}
I also have the following aspect definition:
/**
* Security advice around methods that are annotated with
* {#link RequiredPermission}.
*
* #param pjp
* #param param
* #param requiredPermission
* #return
* #throws Throwable
*/
#Around(value = "execution(public *"
+ " com.mycompany.myserviceimpl.*(..))"
+ " && args(param)" + // parameter object
" && #annotation( requiredPermission )" // permission annotation
, argNames = "param,requiredPermission")
public Object processRequest(final ProceedingJoinPoint pjp,
final MyParameterObject param,
final RequiredPermission requiredPermission) throws Throwable {
if(userService.userHasRoles(param.getUsername(),requiredPermission.values()){
return pjp.proceed();
}else{
throw new SorryButYouAreNotAllowedToDoThatException(
param.getUsername(),requiredPermission.value());
}
}
The parameter object contains a user name and I want to look up the required role for the user before allowing access to the method.
When I put the annotation on the method in MyServiceImpl, everything works just fine, the pointcut is matched and the aspect kicks in. However, I believe the annotation is part of the service contract and should be published with the interface in a separate API package. And obviously, I would not like to put the annotation on both service definition and implementation (DRY).
I know there are cases in Spring AOP where aspects are triggered by annotations one interface methods (e.g. Transactional). Is there a special syntax here or is it just plain impossible out of the box.
PS: I have not posted my spring config, as it seems to be working just fine. And no, those are neither my original class nor method names.
PPS: Actually, here is the relevant part of my spring config:
<aop:aspectj-autoproxy proxy-target-class="false" />
<bean class="com.mycompany.aspect.MyAspect">
<property name="userService" ref="userService" />
</bean>
If I understand you correct, you want a pointcut that finds all methods in classes that extends MyService and is annotated and with the preferred arguments.
I propose that you replace:
execution(public * com.mycompany.myserviceimpl.*(..))
with:
execution(public * com.mycompany.myservice.MyService+.*(..))
The plus sign is used if you want a joinpoint to match the MyService class or a class that extends it.
I hope it helps!
Espen, your code works only for one class:
execution(public * com.mycompany.myservice.MyService+.*(..))
but what if I want this behaviour for all services in *com.mycompany.services.** package?
Related
I m a newbie to spring boot tests.Here is my situation:
I want to run spring boot test but exclude some components like component contains #Aspect annotation.Because in my test case, some code will be cut in by aspect component and cause NullPointException.
I tried to use #SpringBootTest parameter classes like this
#SpringBootTest(classes=TestApplication.class) ,and TestApplication.class is a springboot Main class with #ComponentScan annotation for scan components exclude #Aspect class.I think it is not a clear way to solve this problem and it does not work for me, can anybody help me?
1.Test Case: please look at comment flag 1. that is a query database operation
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class CheckCardFormatTest{
#Autowired
private XxxServiceImpl xxxService; // service layer
....
#Test
public void testMainCardFormat() {
String result=xxxService.query("someParam");// 1.
....
}
....
}
2.here is the problem please look at comment 2, getRequest() method will cause NullPointException.Because my aspect class will interrupt query-database operation
#Aspect
#Component
public class AbcAspect {
#Around(value = "execution(*com.xx.preparedStatement_execute(..))")
public Object druidIntercept(ProceedingJoinPoint pjp) throws Throwable {
....
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
HttpServletRequest request = servletRequestAttributes.getRequest();// 2.
....
}
add #TestComponent to AbcsAspect class
here is the javadoc of #TestComponent
/**
* {#link Component #Component} that can be used when a bean is intended only for tests,
* and should be excluded from Spring Boot's component scanning.
*
* Note that if you directly use {#link ComponentScan #ComponentScan} rather than relying
* on {#code #SpringBootApplication} you should ensure that a {#link TypeExcludeFilter} is
* declared as an {#link ComponentScan#excludeFilters() excludeFilter}.
*
* #author Phillip Webb
* #since 1.4.0
* #see TypeExcludeFilter
* #see TestConfiguration
*/
I have the following interface:
/**
* Annotation for methods, whose execution should be logged.
*/
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD, ElementType.TYPE})
public #interface Loggable {
/**
* Log severity level of 'before' and 'after' log statements.
*/
enum Level {
DEBUG,
INFO,
WARN,
ERROR
}
/**
* Defines the severity which should be used when logging the method arguments.
*/
Level level() default Level.FATAL;
}
I also have the following class:
/**
* Class for logging input and output parameters of any method with annotation #Loggable.
*/
#Aspect
public final class LoggingAspect {
private final Logger log = LoggerFactory.getLogger(getClass());
/**
* #param jp - ProceedingJointPoint
* #param loggable - Loggable
* #return returns the next executable point to proceed in target
* #throws Throwable - throws exception when proceeding with joint point
*/
#Around("execution(* *(..)) && #annotation(loggable)")
public Object loggingAroundMethod(#Nonnull final ProceedingJoinPoint jp,
#Nonnull final Loggable loggable) throws Throwable {
final String signature = jp.getTarget().getClass().getName() + '.' + jp.getSignature().getName();
final List<Object> arguments = Arrays.asList(jp.getArgs());
final Object result;
try {
doLog(loggable.level(), "[BEFORE] {}{}", signature, arguments);
result = jp.proceed();
doLog(loggable.level(), "[AFTER] {}{} result={}", signature, arguments, result);
} catch (Exception e) {
log.error("[AFTER] {}{} exception={}", signature, arguments, e);
throw e;
}
return result;
}
/**
* Logs the message with appropriate log level.
* #param level - level to log
* #param format - format for logging
* #param arguments - arguments for logging
*/
private void doLog(#Nonnull final Loggable.Level level, #Nonnull final String format, final Object... arguments) {
switch (level) {
case DEBUG:
log.debug(format, arguments);
return;
case INFO:
log.info(format, arguments);
return;
case WARN:
log.warn(format, arguments);
return;
case ERROR:
break;
default:
log.error("Unable to appropriately handle given log level={}", level);
}
log.error(format, arguments);
}
}
And here is my XML:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"
default-autowire="no">
<aop:aspectj-autoproxy proxy-target-class="true"/>
<bean class="path.to.my.package.LoggingAspect" />
</beans>
Now, when I add the #Loggable annotation to an existing method that is being called elsewhere in my program, everything shows up correctly in my logs as expected. The working annotations with method looks something like this:
#Loggable
public boolean testString(String test) {
return test.equals("foo");
}
However, when I try to add the annotation to a helper method rather than the method that is already being called in my program, no logs show up. So now the code that doesn't work looks something like this:
public boolean testString(String test) {
return testStringHelper(test);
}
#Loggable
public boolean testStringHelper(String test) {
return test.equals("foo");
}
Can anyone offer insight into why the first scenario works, but the second scenario with the helper method doesn't? As an aside, the helper methods are all public. Also, if I add a regular log statement inside the helper method, it does show up in my logs. It's just the annotation that doesn't work with the helper method for some reason.
Spring can only advise methods of Spring beans that have been injected into other Spring beans. If a bean calls one of its own methods, then the advice will not be executed.
Spring AOP proxies are explained in the docs.
It is because of proxies!
Spring works on concept of proxy. It cannot proxy your internal calls. So it won't work that way.
You can go through Spring Doc on Understanding AOP proxies to understand it in more details.
I have the following code :
public interface CreatorFactory<E extends Vehicle> {
public VehicleType<E> getVehicle();
public boolean supports(String game);
}
public abstract AbstractVehicleFactory<E extends Vehicle> implements CreatorFactory {
public VehicleType<E> getVehicle() {
// do some generic init
getVehicle();
}
public abstract getVehicle();
public abstract boolean supports(String game);
}
and i have multiple factories, for car, truck..etc..
#Component
public CarFactory extends AbstractVehicleFactory<Car> {
/// implemented methods
}
#Component
public TruckFactory extends AbstractVehicleFactory<Truck> {
/// implemented methods
}
What I would like to do is pull the implemented factories into a seperate class as a list, but im not sure how generics works in this case... I know in spring you can get all beans of a specific type... would this still work?...
With erasure, i guess the generic types would be removed .. ??
Firstly, I think there is maybe no need to get a list of beans. And you just want get the exact bean which has declared with generics type.
In BeanFactory interface in Spring framework, there is a method to use for your requirement:
public interface BeanFactory {
/**
* Return the bean instance that uniquely matches the given object type, if any.
* #param requiredType type the bean must match; can be an interface or superclass.
* {#code null} is disallowed.
* <p>This method goes into {#link ListableBeanFactory} by-type lookup territory
* but may also be translated into a conventional by-name lookup based on the name
* of the given type. For more extensive retrieval operations across sets of beans,
* use {#link ListableBeanFactory} and/or {#link BeanFactoryUtils}.
* #return an instance of the single bean matching the required type
* #throws NoSuchBeanDefinitionException if there is not exactly one matching bean found
* #since 3.0
* #see ListableBeanFactory
*/
<T> T getBean(Class<T> requiredType) throws BeansException;
}
You can use code like:
Car carFactory = applicationContext.getBean( CarFactory.class );
Trunk trunkFactory = applicationContext.getBean( TrunkFactory.class );
or just see #Qualifier annotation for injection automaticly.
#Component("carFactory")
public CarFactory extends AbstractVehicleFactory<Car> {
/// implemented methods
}
#Component("truckFactory ")
public TruckFactory extends AbstractVehicleFactory<Truck> {
/// implemented methods
}
In client side code:
#Qualifier("carFactory")
#Autowired
private CarFactory carFactory ;
#Qualifier("truckFactory")
#Autowired
private TruckFactory TruckFactory;
Looks like you need:
#Autowired
List<AbstractVehicleFactory> abstractVehicleFactories;
I would like to create a class that adds custom methods for use in spring security expression language for method-based authorization via annotations.
For example, I would like to create a custom method like 'customMethodReturningBoolean' to be used somehow like this:
#PreAuthorize("customMethodReturningBoolean()")
public void myMethodToSecure() {
// whatever
}
My question is this.
If it is possible, what class should I subclass to create my custom methods, how would I go about configuring it in the spring xml configuration files and come someone give me an example of a custom method used in this way?
None of the mentioned techniques will work anymore. It seems as though Spring has gone through great lengths to prevent users from overriding the SecurityExpressionRoot.
EDIT 11/19/14 Setup Spring to use security annotations:
<beans ... xmlns:sec="http://www.springframework.org/schema/security" ... >
...
<sec:global-method-security pre-post-annotations="enabled" />
Create a bean like this:
#Component("mySecurityService")
public class MySecurityService {
public boolean hasPermission(String key) {
return true;
}
}
Then do something like this in your jsp:
<sec:authorize access="#mySecurityService.hasPermission('special')">
<input type="button" value="Special Button" />
</sec:authorize>
Or annotate a method:
#PreAuthorize("#mySecurityService.hasPermission('special')")
public void doSpecialStuff() { ... }
Additionally, you may use Spring Expression Language in your #PreAuthorize annotations to access the current authentication as well as method arguments.
For example:
#Component("mySecurityService")
public class MySecurityService {
public boolean hasPermission(Authentication authentication, String foo) { ... }
}
Then update your #PreAuthorize to match the new method signature:
#PreAuthorize("#mySecurityService.hasPermission(authentication, #foo)")
public void doSpecialStuff(String foo) { ... }
You'll need to subclass two classes.
First, set a new method expression handler
<global-method-security>
<expression-handler ref="myMethodSecurityExpressionHandler"/>
</global-method-security>
myMethodSecurityExpressionHandler will be a subclass of DefaultMethodSecurityExpressionHandler which overrides createEvaluationContext(), setting a subclass of MethodSecurityExpressionRoot on the MethodSecurityEvaluationContext.
For example:
#Override
public EvaluationContext createEvaluationContext(Authentication auth, MethodInvocation mi) {
MethodSecurityEvaluationContext ctx = new MethodSecurityEvaluationContext(auth, mi, parameterNameDiscoverer);
MethodSecurityExpressionRoot root = new MyMethodSecurityExpressionRoot(auth);
root.setTrustResolver(trustResolver);
root.setPermissionEvaluator(permissionEvaluator);
root.setRoleHierarchy(roleHierarchy);
ctx.setRootObject(root);
return ctx;
}
Thanks ericacm, but it does not work for a few reasons:
The properties of DefaultMethodSecurityExpressionHandler are private (reflection visibility kludges undesirable)
At least in my Eclipse, I can't resolve a MethodSecurityEvaluationContext object
The differences are that we call the existing createEvaluationContext method and then add our custom root object. Finally I just returned an StandardEvaluationContext object type since MethodSecurityEvaluationContext would not resolve in the compiler (they are both from the same interface). This is the code that I now have in production.
Make MethodSecurityExpressionHandler use our custom root:
public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {
// parent constructor
public CustomMethodSecurityExpressionHandler() {
super();
}
/**
* Custom override to use {#link CustomSecurityExpressionRoot}
*
* Uses a {#link MethodSecurityEvaluationContext} as the <tt>EvaluationContext</tt> implementation and
* configures it with a {#link MethodSecurityExpressionRoot} instance as the expression root object.
*/
#Override
public EvaluationContext createEvaluationContext(Authentication auth, MethodInvocation mi) {
// due to private methods, call original method, then override it's root with ours
StandardEvaluationContext ctx = (StandardEvaluationContext) super.createEvaluationContext(auth, mi);
ctx.setRootObject( new CustomSecurityExpressionRoot(auth) );
return ctx;
}
}
This replaces the default root by extending SecurityExpressionRoot. Here I've renamed hasRole to hasEntitlement:
public class CustomSecurityExpressionRoot extends SecurityExpressionRoot {
// parent constructor
public CustomSecurityExpressionRoot(Authentication a) {
super(a);
}
/**
* Pass through to hasRole preserving Entitlement method naming convention
* #param expression
* #return boolean
*/
public boolean hasEntitlement(String expression) {
return hasRole(expression);
}
}
Finally update securityContext.xml (and make sure it's referenced from your applcationContext.xml):
<!-- setup method level security using annotations -->
<security:global-method-security
jsr250-annotations="disabled"
secured-annotations="disabled"
pre-post-annotations="enabled">
<security:expression-handler ref="expressionHandler"/>
</security:global-method-security>
<!--<bean id="expressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">-->
<bean id="expressionHandler" class="com.yourSite.security.CustomMethodSecurityExpressionHandler" />
Note: the #Secured annotation will not accept this override as it runs through a different validation handler. So, in the above xml I disabled them to prevent later confusion.
GWT's serializer has limited java.io.Serializable support, but for security reasons there is a whitelist of types it supports. The documentation I've found, for example this FAQ entry says that any types you want to serialize "must be included in the serialization policy whitelist", and that the list is generated at compile time, but doesn't explain how the compiler decides what goes on the whitelist.
The generated list contains a number of types that are part of the standard library, such as java.lang.String and java.util.HashMap. I get an error when trying to serialize java.sql.Date, which implements the Serializable interface, but is not on the whitelist. How can I add this type to the list?
There's a workaround: define a new Dummy class with member fields of all the types that you want to be included in serialization. Then add a method to your RPC interface:
Dummy dummy(Dummy d);
The implementation is just this:
Dummy dummy(Dummy d) { return d; }
And the async interface will have this:
void dummy(Dummy d, AsyncCallback< Dummy> callback);
The GWT compiler will pick this up, and because the Dummy class references those types, it will include them in the white list.
Example Dummy class:
public class Dummy implements IsSerializable {
private java.sql.Date d;
}
Any specific types that you include in your service interface and any types that they reference will be automatically whitelisted, as long as they implement java.io.Serializable, eg:
public String getStringForDates(ArrayList<java.util.Date> dates);
Will result in ArrayList and Date both being included on the whitelist.
It gets trickier if you try and use java.lang.Object instead of specific types:
public Object getObjectForString(String str);
Because the compiler doesn't know what to whitelist. In that case if the objects are not referenced anywhere in your service interface, you have to mark them explicitly with the IsSerializable interface, otherwise it won't let you pass them through the RPC mechanism.
The whitelist is generated by the GWT compiler and contains all the entries that are designated by the IsSerializable marker interface.
To add a type to the list you just need to make sure that the class implements the IsSerializable interface.
Additionally for serialization to work correctly the class must have a default no arg constructor (constructor can be private if needed). Also if the class is an inner it must be marked as static.
IMHO the simpliest way to access whitelist programmatically is to create a class similar to this:
public class SerializableWhitelist implements IsSerializable {
String[] dummy1;
SomeOtherThingsIWishToSerialize dummy2;
}
Then include it in the .client package and reference from the RPC service (so it gets analyzed by the compiler).
I couldn't find a better way to enable tranfer of unparameterized maps, which is obviously what you sometimes need in order to create more generic services...
The whitelist is generated by the gwt compiler and contains all the entries that are designated by the IsSerializable marker interface.
To add a type to the list you just need to make sure that the class implements the IsSerializable interface.
-- Andrej
This is probably the easiest solution.
The only thing to remember with this is that all the classes that you want to serialize should have "public, no-argument" constructor, and (depending upon requirements) setter methods for the member fields.
to ensure the desired result delete all war/<app>/gwt/*.gwt.rpc
To anyone who will have the same question and doesn't find previous answers satisfactory...
I'm using GWT with GWTController, since I'm using Spring, which I modified as described in this message. The message explains how to modify GrailsRemoteServiceServlet, but GWTController calls RPC.decodeRequest() and RPC.encodeResponseForSuccess() in the same way.
This is the final version of GWTController I'm using:
/**
* Used to instantiate GWT server in Spring context.
*
* Original version from this tutorial.
*
* ...fixed to work as explained in this tutorial.
*
* ...and then fixed to use StandardSerializationPolicy as explained in
* this message to allow
* using Serializable instead of IsSerializable in model.
*/
public class GWTController extends RemoteServiceServlet implements Controller, ServletContextAware {
// Instance fields
private RemoteService remoteService;
private Class<? extends RemoteService> remoteServiceClass;
private ServletContext servletContext;
// Public methods
/**
* Call GWT's RemoteService doPost() method and return null.
*
* #param request
* The current HTTP request
* #param response
* The current HTTP response
* #return A ModelAndView to render, or null if handled directly
* #throws Exception
* In case of errors
*/
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
doPost(request, response);
return null; // response handled by GWT RPC over XmlHttpRequest
}
/**
* Process the RPC request encoded into the payload string and return a string that encodes either the method return
* or an exception thrown by it.
*
* #param payload
* The RPC payload
*/
public String processCall(String payload) throws SerializationException {
try {
RPCRequest rpcRequest = RPC.decodeRequest(payload, this.remoteServiceClass, this);
// delegate work to the spring injected service
return RPC.invokeAndEncodeResponse(this.remoteService, rpcRequest.getMethod(), rpcRequest.getParameters(), rpcRequest.getSerializationPolicy());
} catch (IncompatibleRemoteServiceException e) {
return RPC.encodeResponseForFailure(null, e);
}
}
/**
* Setter for Spring injection of the GWT RemoteService object.
*
* #param RemoteService
* The GWT RemoteService implementation that will be delegated to by the {#code GWTController}.
*/
public void setRemoteService(RemoteService remoteService) {
this.remoteService = remoteService;
this.remoteServiceClass = this.remoteService.getClass();
}
#Override
public ServletContext getServletContext() {
return servletContext;
}
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
}
I found that just putting it in the client package or using it in a dummy service interface was not sufficient as it seemed the system optimized it away.
I found it easiest to create a class that derived from one of the types already used in the service interface and stick it in the client package. Nothing else needed.
public class GWTSerializableTypes extends SomeTypeInServiceInterface implements IsSerializable {
Long l;
Double d;
private GWTSerializableTypes() {}
}
I had this problem but ended up tracing the problem back to a line of code in my Serializable object:
Logger.getLogger(this.getClass().getCanonicalName()).log(Level.INFO, "Foo");
There were no other complaints before the exception gets caught in:
#Override
protected void serialize(Object instance, String typeSignature)
throws SerializationException {
assert (instance != null);
Class<?> clazz = getClassForSerialization(instance);
try {
serializationPolicy.validateSerialize(clazz);
} catch (SerializationException e) {
throw new SerializationException(e.getMessage() + ": instance = " + instance);
}
serializeImpl(instance, clazz);
}
And the business end of the stack trace is:
com.google.gwt.user.client.rpc.SerializationException: Type 'net.your.class' was not included in the set of types which can be serialized by this SerializationPolicy or its Class object could not be loaded. For security purposes, this type will not be serialized.: instance = net.your.class#9c7edce
at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter.serialize(ServerSerializationStreamWriter.java:619)