Jersey and HK2 - Injecting current user

后端 未结 1 1252
一个人的身影
一个人的身影 2021-01-23 02:54

I\'m working with jersey 2.17 and HK2 to create a simple rest app. I have a ContainerRequestFilter that rejects any request that doesn\'t have the \"currentuser\" c

相关标签:
1条回答
  • 2021-01-23 03:16

    What you're looking for is not trivially done. One way you could handle this is setting the SecurityContext inside the ContainerRequestFilter, as seen here. This doesn't involve any direct interaction with HK2. You could then inject the SecurityContext in your resource class. And get the user by

    securityContext.getUserPrincipal().getName();
    

    If you really want to go with injecting the username with a custom annotation, you will need to create a InjectionResolver (See Defining Custom Injection Annotation. You could inject ContainerRequestContext (the same one passed to the filter method in the ContainerRequestFilter) or the SecurityContext into the InjectionResolver. For example

    Filter

    @Provider
    @PreMatching
    public class UserFilter implements ContainerRequestFilter {
    
        public static final String USER_PROP = "user";
    
        @Override
        public void filter(ContainerRequestContext requestContext) throws IOException {
            requestContext.setProperty(USER_PROP, new User("peeskillet"));
        }
    }
    

    Annotation

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    public @interface CurrentUser {   
    }
    

    InjectionResolver

    public class CurrentUserInjectionResolver implements InjectionResolver<CurrentUser> {
    
        javax.inject.Provider<ContainerRequestContext> requestContext;
    
        @Inject
        public CurrentUserInjectionResolver(
            javax.inject.Provider<ContainerRequestContext> requestContext) {
            this.requestContext = requestContext;
        }
    
        @Override
        public Object resolve(Injectee injectee, ServiceHandle<?> sh) {
            if (User.class == injectee.getRequiredType()) {
                return requestContext.get().getProperty(UserFilter.USER_PROP);
            }
            return null;
        }
    
        @Override
        public boolean isConstructorParameterIndicator() { return false; }
    
        @Override
        public boolean isMethodParameterIndicator() { return false; }
    }
    

    Bind the InjectionResolver

    @Provider
    public class UserFeature implements Feature {
    
        @Override
        public boolean configure(FeatureContext context) {
            context.register(new AbstractBinder(){
                @Override
                public void configure() {
    
                    bind(CurrentUserInjectionResolver.class)
                    .to(new TypeLiteral<InjectionResolver<CurrentUser>>(){})
                            .in(Singleton.class);
                }
            });
            return true;          
        } 
    }
    

    Resource

    @Path("user")
    public class UserResource {
    
        @CurrentUser 
        private User user;
    
        @GET
        public Response getCurrentUser() {
            return Response.ok(user.getUsername()).build();
        }
    }
    

    Now I'm not quite sure about this second approach, at least the part about the filter being a @PreMatching filter. If I don't make it a pre-matching, the User will be null. It seems the ContainerRequestContext does not yet have the property we set, meaning what appears to be happening is the the the InjectResolver is being called before the filter. I will need to look into this. Making it a pre-matching, IMO should not be required.

    Personally though, I would go with the first approach, just using the SecurityContext. A full example is in the link I provided above. With this approach, you can take advantage of Jersey's RolesAllowedDynamicFeature if needed.

    0 讨论(0)
提交回复
热议问题