Adding an annotation to a method to note if it is allowed to run

0 投票
最新提问 用户: (120 分)

I have implemented parts of my own security framework in java and I have run into a small-ish problem.

I want to be able to check if a user has access to a method or not. I can do this by simply adding a check inside of the method like the following:

@GET
public Response getResource() throws Exception
{
    if (AuthUtils.isAuthenticated(request, "ROLE1", "ROLE2"))
        return Response.ok("Hello World!").build();
    else
        return Response.status(HttpServletResponse.SC_UNAUTHORIZED).build();
}

Where you supply a list of roles that are authorized to use the function.

However in an ideal world, I would like to do something like the following

@Secure(roles = {"ROLE1", "ROLE"})
@GET
public Response getResource() throws Exception
{
    return Response.ok("Hello World!").build();
}

Where it by default returns an unathorized response on all methods annotated with @Secure where the user does not have the correct roles.

Is something like this possible with Java? and if so, how?

发表于 用户: (620 分)
Certainly. That's how most of the ready-made frameworks do it, which you should be using anyway.
发表于 用户: (120 分)
Alright, fair enough. So the million-dollar-question is: How would I implement it?
发表于 用户: (620 分)
With regular annotation processing, but you also need AOP to actually intercept the calls.

3 个回答

0 投票
最新回答 用户: (140 分)

yes, possible. We can use spring security plugin/library to do this. In Spring We can follow Spring security In Grails We can follow Spring Security Plugin

发表于 用户: (620 分)
He wants to create a custom annotation for his own security framework, not use spring security.
0 投票
最新回答 用户: (300 分)

If you're using Jersey 2.x, just use the RolesAllowedDynamicFeature. You need to register it with your application. This will handle the authorization. You would use the annotation @RolesAllowed. It can go at the class level or method level

@RolesAllowed({"ROLE1", "ROLE"})
@GET
public Response getResource() throws Exception

If you are using Servlet Authentication, then the roles will be populated in the HttpServletRequest#getUserRoles. Here you will not need to do anything else. I assume servlet security is what you are using from the looks of this code

if (AuthUtils.isAuthenticated(request, "ROLE1", "ROLE2"))

I'm guessing the request is the HttpServletRequest.

If you're not using Servlet security, then to make this work, you need to use a Jersey filter to set the SecurityContext. Jersey will use the SecurityContext to check the roles. You can see an example in this post.

See also:

发表于 用户: (120 分)
This sounds really promising. I will try and see if I can implement it
0 投票
最新回答 用户: (140 分)

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);
   }
欢迎来到 Security Q&A ,有什么不懂的可以尽管在这里提问,你将会收到社区其他成员的回答。
...