Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
217 views
in Technique[技术] by (71.8m points)

java - Custom JAX-RS authorization - using JWT in each request

I have a JAX-RS service where I want all my users to access my services, but just those who have rights to see the result. Roles based security and existing REALMS and atuhentication methods doesn't fit my requirement.

For example:

  1. user authenticates against one REST service and I send him JWT token with his ID
  2. user asks for other resource and sends his JWT with his ID in each request
  3. I check his user id (from JWT) and if the business logic returns result I send them back, else I send empty result set or specific HTTP status

Question is: Where should I check for users ID, in some separate filter, security context or in every REST method implementation? How to provide REST methods with this ID, can securityContext be injected in every method after filtering request by ID?

I'm using GlassFish 4.1 and Jersey JAX-RS implementation.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You can perform this logic in a ContainerRequestFilter. It pretty common to handle custom security features in here.

Some things to consider

  1. The class should be annotated with @Priority(Priorities.AUTHENTICATION) so it is performed before other filters, if any.

  2. You should make use of the SecurityContext, inside the filter. What I do is implement a SecurityContext. You can really implement it anyway you want.

Here's a simple example without any of the security logic

@Provider
@Priority(Priorities.AUTHENTICATION)
public class SecurityFilter implements ContainerRequestFilter {

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        SecurityContext originalContext = requestContext.getSecurityContext();
        Set<String> roles = new HashSet<>();
        roles.add("ADMIN");
        Authorizer authorizer = new Authorizer(roles, "admin", 
                                               originalContext.isSecure());
        requestContext.setSecurityContext(authorizer);
    }

    public static class Authorizer implements SecurityContext {

        Set<String> roles;
        String username;
        boolean isSecure;
        public Authorizer(Set<String> roles, final String username, 
                                             boolean isSecure) {
            this.roles = roles;
            this.username = username;
            this.isSecure = isSecure;
        }

        @Override
        public Principal getUserPrincipal() {
            return new User(username);
        }

        @Override
        public boolean isUserInRole(String role) {
            return roles.contains(role);
        }

        @Override
        public boolean isSecure() {
            return isSecure;
        }

        @Override
        public String getAuthenticationScheme() {
            return "Your Scheme";
        } 
    } 

    public static class User implements Principal {
        String name;

        public User(String name) {
            this.name = name;
        }

        @Override
        public String getName() { return name; }   
    }
}

A few things to notice

  • I've created a SecurityContext
  • I've added some roles, and used them for the isUserInRole method. This will be used for authorization.
  • I've created a custom User class, that implements java.security.Principal. I returned this custom object
  • Finally I set the new SecurityContext in the ContainerRequestContext

Now what? Let's look at a simple resource class

@Path("secure")
public class SecuredResource {
    @GET
    @RolesAllowed({"ADMIN"})
    public String getUsername(@Context SecurityContext securityContext) {
        User user = (User)securityContext.getUserPrincipal();
        return user.getName();
    }
}

A few things to notice:

  • SecurityContext is injected into the method.
  • We get the Principal and cast it to User. So really you can create any class that implements Principal, and use this object however you want.
  • The use of the @RolesAllowed annotation. With Jersey, there is a filter that checks the SecurityContext.isUserInRole by passing in each value in the @RolesAllowed annotation to see if the User is allowed to access the resource.

    To enable this feature with Jersey, we need to register the RolesAllowedDynamicFeature

    @ApplicationPath("/api")
    public class AppConfig extends ResourceConfig {
    
        public AppConfig() {
            packages("packages.to.scan");
            register(RolesAllowedDynamicFeature.class);
        }
    }
    

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...