What you need to do is make an aspect, ie. a method that gets called every time before a specific "target method" is called. You can use Spring as mentioned before but you can also do this using just Java core and Reflection. To do this, you will need to have a proxy method that calls your actual "target method" only if the user has the right permissions. Alternatively the proxy would throw an exception. So what this means is that you also need to make your dependency injection dynamic.
For a very simple application you don't even need annotations. Say you want to secure the access to the method Service::getSecuredResource :
public interface Service {
Object getSecuredResource();
}
public class ServiceImpl implements Service {
public Object getSecuredResource() {
System.out.println("in my secure method");
return "something private";
}
}
public class ClientController {
private Service service; //line 1
public void doSomething(){
service.getSecuredResource();
}
public void setService(Service service)...
}
public final class AuthenticationService {
public static void authenticate()...
public static boolean isAuthenticated()...//do permission checks here
}
A simple solution would be:
public class Main {
public static void main(String[] args) {
final ClientController clientController = new ClientController();
injectDependencies(clientController);
clientController.doSomething();
}
private static void injectDependencies(ClientController clientController) { //line 2
final Service serviceProxy = new Service() {
private Service targetObject = new ServiceImpl();
public Object getSecuredResource() {
if (AuthenticationService.isAuthenticated()) {
return targetObject.getSecuredResource();
}
throw new SecurityException("you're not authorised");
}
};
clientController.setService(serviceProxy);
}
}
Of course, if you want something more dynamic, you will need to have annotations and use Reflection to create dynamic proxies and to inject the dependencies.
I would annotate the service declaration (line 1) with my custom annotation, say MyInject
.
Then Main::injectDependencies method (line 2) would be replaced by something like this:
private static void injectDependencies(ClientController clientController) {
Arrays.stream(clientController.getClass().getFields())
.filter(field -> field.getAnnotation(MyInject.class)!=null)
.forEach(field -> injectField(field, clientController));
}
private static void injectField(Field field, ClientController clientController) {
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//verify here that the user has the right permissions for the called method
}
};
Service serviceProxy = (Service) Proxy.newProxyInstance(
Service.class.getClassLoader(),
new Class[] { Service.class },
handler);
clientController.setService(serviceProxy);
}