I have a simple REST API, build using Spring MVC #Controllers and #RequestMapping. I'd like to start wrapping responses to provide additional with metadata.
For example, given a call that would return
HTTP GET: /users/1
{
"userName" : "Jack Jackerson"
}
I'd like to wrap it, as follows:
{
"metadata" :
{
"callLimit" : "50",
"callsRemaining" : "49"
},
"result" :
{
"userName" : "Jack Jackerson"
}
} ..etc..
Additionally, I'd like to support standard set of parameters for managing lists (limit and offset).
As this touches all the api methods, I'd like to implement it as a decorator of some spring internal service, so the methods themselves can focus on their actual logic, and keep this boilerplate stuff centralized.
I've started down the path of decorating the HttpMessageConverter's that are registered, and wrapping them with a decorator.
However, this doesn't provide me access to the inbound request for methods that don't declare a #RequestBody. (Many don't)
Ideally, I need to be higher in the stack - RequestResponseBodyMethodProcessor.writeWithMessageConverters() looks like a good candidate, but I don't know how to hook in here.
What options are available with Spring MVC to implement this type of API-Wide processing of request / responses?
Here's the implementation I used:
public class MetadataInjectingReturnValueHandler implements HandlerMethodReturnValueHandler {
private final HandlerMethodReturnValueHandler delegate;
public MetadataInjectingReturnValueHandler(HandlerMethodReturnValueHandler delegate)
{
this.delegate = delegate;
}
#Override
public boolean supportsReturnType(MethodParameter returnType) {
return delegate.supportsReturnType(returnType);
}
#Override
public void handleReturnValue(Object returnValue,
MethodParameter returnType, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest) throws Exception {
returnValue = wrapResult(returnValue); //Omitted
delegate.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
}
#Component
public class MetadataInjectionFactoryBean implements InitializingBean {
#Autowired
private RequestMappingHandlerAdapter adapter;
#Override
public void afterPropertiesSet() throws Exception {
HandlerMethodReturnValueHandlerComposite returnValueHandlers = adapter.getReturnValueHandlers();
List<HandlerMethodReturnValueHandler> handlers = Lists.newArrayList(returnValueHandlers.getHandlers());
decorateHandlers(handlers);
adapter.setReturnValueHandlers(handlers);
}
private void decorateHandlers(List<HandlerMethodReturnValueHandler> handlers) {
for (HandlerMethodReturnValueHandler handler : handlers) {
if (handler instanceof RequestResponseBodyMethodProcessor)
{
MetadataInjectingReturnValueHandler decorator = new MetadataInjectingReturnValueHandler(handler);
int index = handlers.indexOf(handler);
handlers.set(index, decorator);
log.info("Metadata Injecting decorator wired up");
break;
}
}
}
}
Related
I am working on a small project that includes signing up an account, logging a user in, and request data via certain endpoints. The authentication piece is done via Spring security session Ids. The endpoints consist of publicly available endpoints (i.e. signing up, or forgot password), and some endpoints that require the user to be signed in (i.e. change password, get some content, etc). Kind of like this:
#RestController
public class FightController {
//publicly available
#GetMapping("/public/foo")
String methodForEveryone() {
return "Hi common dude";
}
#GetMapping("secret/bar")
String methodForSpecialPeople() {
return "What happens in fight controller...";
}
}
Spring security then has a list of public endpoints in a static WHITE_LIST
private static final String[] AUTH_WHITELIST = {
//public endpoints
"/public/foo", "swagger", "etc"
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterAfter(customAuthFilter(), RequestHeaderAuthenticationFilter.class)
.authorizeRequests()
.antMatchers(AUTH_WHITELIST).permitAll()
.antMatchers("/**").authenticated()
.and()
Tests are currently being done by hitting every endpoint and determining whether it is behaving as expected (via custom antmatchers in the WebSecurityConfigurer). Like so:
package com.fight.testpackages
public class EndpointList {
public static class PublicEndpoints implements ArgumentsProvider {
#Override
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
return Stream.of(
Arguments.of("/public/foo"),
);
}
}
public static class PrivateEndoints implements ArgumentsProvider {
#Override
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
return Stream.of(
Arguments.of("secret/bar"),
);
}
}
and then managed with tests like
#ParameterizedTest
#ArgumentsSource(PrivateContentEndpoints.class)
public void privateEndpoint_unauthorizedUser_isUnauthorizedResponse(String url) throws Exception {
assertFalse(super.isAuthenticated(url));
}
#WithMockUser(roles = "USER")
#ParameterizedTest
#ArgumentsSource(PublicAccountManagementEndpoints.class)
public void publicEndpoint_authorizedUser_hasAccess(String url) throws Exception {
assertTrue(super.isAuthenticated(url));
}
The issue I am trying to solve can best be described with the following scenario:
A developer adds a new endpoint;
They add the endpoint to list of antmatchers (if it should be public);
And then they add the endpoint to a list of public and private endpoints that gets pulled into the tests.
The problem here is that there is no enforcement of this behaviour, and it's super easy to forget to add an endpoint to a test, or if the names of the endpoints change then the tests need to be updated.
The current setup works, but I was wondering if there was a standard for this? I've looked at the #PreAuthorize and #RolesAllowed, etc but they only seem to be useful for securing a method, and not making it public. I actually want the reverse (i.e. the endpoint to be private by default, and then to be marked as publicly available intentionally).
A solution that I've come up with is as follows:
Create an annotation
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface EndpointSecurity {
boolean isPublic() default false;
}
assign annotation to method if you want to make it public
#EndpointSecurity(isPublic = true)
#GetMapping("/public/foo")
String methodForEveryone() {
return "Hi common dude";
}
Build a scanner that checks all RestController methods for the EndpointSecurity annotation and the REST mapping annotation, kind of like below.. Hopefully it's enough to get the point..
#DependsOn("classScanner")
public class ClassMethodScanner {
private final List<Class<? extends Annotation>> annotationFilters;
private List<Method> annotatedMethods;
private final AnnotationScanner<?> classScanner;
public <T extends Annotation> ClassMethodScanner(ClassScanner<T> classScanner) {
this(classScanner, Collections.emptyList());
}
public <T extends Annotation> ClassMethodScanner(ClassScanner<T> classScanner,
List<Class<? extends Annotation>> annotations) {
this.classScanner = classScanner;
this.annotationFilters = annotations;
}
#PostConstruct
void extractAnnotatedMethods() throws ClassNotFoundException {
if (annotatedMethods == null) {
annotatedMethods =
classScanner.getAnnotatedHandlers().stream()
.sequential()
.map(Class::getDeclaredMethods)
.flatMap(Arrays::stream)
.filter(this::hasExpectedAnnotations)
.collect(Collectors.toUnmodifiableList());
}
}
private boolean hasExpectedAnnotations(Method method) {
return
(annotationFilters.isEmpty() && method.getAnnotations().length > 0)
|| annotationFilters.stream().anyMatch(method::isAnnotationPresent);
}
//Is there a good way of making this protected?
public List<Method> getAnnotatedMethods() {
return annotatedMethods;
}
}
And finally produce a list of public and private endpoints that feeds into the HttpSecurity.
public class SecurityEndpoints {
private List<String> publicEndpoints;
private List<String> privateEndpoints;
private final EndpointDetailsCollector<?> collector;
public String[] getWhiteList() {
And feeds into the EndpointList that I mentioned above.
This seems somewhat convoluted though. So was wondering what is a standard approach, or am I making too much of testing the endpoints??
tldr: Is there a way to make an internal request (using the method's path) without going to the internet?
--
Why do I need it? I have a project which receives many events. The decision of who will handle each event is made by a Controller. So I have something similar to this:
#RestController
#RequestMapping("/events")
public class EventHandlerAPI {
#Autowired
private EventAHandler eventAhandler;
#Autowired
private EventBHandler eventBhandler;
#PostMapping("/a")
public void handleEventA(#RequestBody EventA event) {
eventAhandler.handle(id, event);
}
#PostMapping("/b")
public void handleEventB(#RequestBody EventB event) {
eventBhandler.handle(id, event);
}
}
We recently added support to receive events through a Queue service. It sends to us the payload and the event class. Our decision is to let both interfaces working (rest and queue). The solution to avoid code duplication was to keep the Controller choosing which handler will take care of the event. The code nowadays is similar to this:
#Configuration
public class EventHandlerQueueConsumer {
#Autowired
private EventHandlerAPI eventHandlerAPI;
private Map<Class, EventHandler> eventHandlers;
#PostConstruct
public void init() {
/* start listen queue */
declareEventHandlers();
}
private void declareEventHandlers() {
eventHandlers = new HashMap<>();
eventHandlers.put(EventAHandler.class, (EventHandler<EventAHandler>) eventHandlerAPI::handleEventA);
eventHandlers.put(EventBHandler.class, (EventHandler<EventBHandler>) eventHandlerAPI::handleEventB);
}
private void onEventReceived(AbstractEvent event) {
EventHandler eventHandler = eventHandlers.get(event.getClass());
eventHandler.handle(event);
}
private interface EventHandler<T extends AbstractEvent> {
void handle(T event);
}
}
This code works, but it doesn't let the controller choose who will handle the event (our intention). The decision is actually being made by the map.
What I would like to do was to invoke the controller method through it's request mapping without going to the internet. Something like this:
#Configuration
public class EventHandlerQueueConsumer {
// MADE UP CLASS TO SHOW WHAT I WANT
#Autowired
private ControllerInkover controllerInvoker;
#PostConstruct
public void init() { /* start listen queue */ }
private void onEventReceived(AbstractEvent event) {
controllerInvoker.post(event.getPath(), new Object[] { event });
}
}
This way is much cleaner and let all the decisions be made by the controller.
I've researched a lot and didn't found a way to implement it. Debugging spring, I found how he routes the request after the DispatcherServlet, but all the spring internals uses HttpServletRequest and HttpServletResponse :(
Is there a way to make an internal request (using the method's path) without going to the internet?
They are classes of the same application
Then it should easy enough.
1) You can call your own API on http(s)://localhost:{port}/api/{path} using RestTemplate utility class. This is preferred way, since you'll follow standard MVC pattern. Something like:
restTemplate.exchange(uri, HttpMethod.POST, httpEntity, ResponseClass.class);
2) If you don't want to invoke network connection at all, then you can either use Spring's internal to find the mapping/method map or use some reflection to build custom
map upon controller's startup. Then you can pass your event/object to the method from the map in a way shown in your mock-up class. Something like:
#RequestMapping("foo")
public void fooMethod() {
System.out.println("mapping = " + getMapping("fooMethod")); // you can get all methods/mapping in #PostContruct initialization phase
}
private String getMapping(String methodName) {
Method methods[] = this.getClass().getMethods();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName() == methodName) {
String mapping[] = methods[i].getAnnotation(RequestMapping.class).value();
if (mapping.length > 0) {
return mapping[mapping.length - 1];
}
}
}
return null;
}
I want implement strategy design pattern in spring boot application. I create BeanPostProcessor for construct strategy resolver:
#Component
public class HandlerInAnnotationBeanPostProcessor implements BeanPostProcessor {
private final UnpHandlersResolver unpHandlersResolver;
public HandlerInAnnotationBeanPostProcessor(UnpHandlersResolver unpHandlersResolver) {
this.unpHandlersResolver = unpHandlersResolver;
}
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
Annotation[] annotations = bean.getClass().getAnnotations();
for (Annotation annotation : annotations) {
if (annotation instanceof HandlerIn) {
if (bean.getClass() != UnpHandler.class)
throw new RuntimeException("Not UnpHandler bean annotated by HandlerIn");
SmevMessageType[] type = ((HandlerIn) annotation).type();
for (SmevMessageType smevMessageType : type) {
unpHandlersResolver.setHandler(smevMessageType, (UnpHandler) bean);
}
}
}
return bean;
}
}
And I create resolver:
#Slf4j
#Component
public class UnpHandlersResolverImpl implements UnpHandlersResolver {
private Map<SmevMessageType, UnpHandler> map = new HashMap<>();
#Override
public void setHandler(SmevMessageType messageType, UnpHandler unpHandler) {
map.put(messageType, unpHandler);
}
#Override
public UnpHandler getUnpHandler(SmevMessageType type) {
UnpHandler sendRequestHandler = map.get(type);
if (sendRequestHandler == null)
throw new IllegalArgumentException("Invalid SendRequestHandler type: " + type);
return sendRequestHandler;
}
}
My BeanPostProcessor scan all beans with annotation HandlerIn and add to resolver's mup. I think it's wrong to do that:
unpHandlersResolver.setHandler(smevMessageType, (UnpHandler) bean);
But I not understand how can I add find beans to resolver. Before this implementation I faind beans in #Postconstruct method of resolver like:
context.getBeansWithAnnotation(HandlerIn.class);
But in this solution I have context in resolver and I think is bad.
Tell me how to properly implement what I want? In short, I want to have a set of classes that implement different behaviors. And the class that controls them. Give the class a parameter so that he chooses the right strategy and gives it to me. Like this:
Handler handler = handlersResolver.getHandler(messageType);
Result result = handler.somthing(param);
I'm going to try to make a simple example.
Interface Greeting {
void sayHello();
String getSupportedLanguage();
}
Then you have X number of implementations and you can loop through them in your "resolver"'s constructor to build the map. (I've seen this called a Proxy or a Decorator in code though, i.e. GreetingProxy or GreetingDecorator)
#Service
public GreetingResolver {
private Map<String, Greeting> languageToGreetingMap = new HashMap<>();
#Autowired
public GreetingResolver(List<Greeting> greetings) {
for (Greeting greeting : greetings) {
languageToGreetingMap.put(greeting.getSupportedLanguage(), greeting);
}
}
public void sayGreetingForLanguage(String language) {
languageToGreetingMap.get(language).sayHello();
}
}
This is a basic example of how one can do the strategy pattern in Spring. Every interface implementation of "Greeting" only knows about itself and what it can support. We then autowire all implementations in a list and loop through to create the map once and then during runtime only the relevant entry from the map in retrieved and used.
Note: this was typed "free hand" directly in the web page so please forgive any typos in the code.
My Spring Boot application contains several #KafkaListeners, and each listener performs the same steps before and after actually processing the payload: Validate the payload, check whether the event has been processed already, check whether it's a tombstone (null) message, decide whether processing should be retried in case of failure, emit metrics, etc.
These steps are currently implemented in a base class, but because the topics passed to #KafkaListener must be constant at runtime, the method annotated with #KafkaListener is defined in the subclass, and does nothing but pass its parameters to a method in the base class.
This works just fine, but I wonder if there's a more elegant solution. I assume my base class would have to create a listener container programmatically, but after a quick look at KafkaListenerAnnotationBeanPostProcessor, it seems to be quite involved.
Does anyone have any recommendadtions?
Having stumbled upon this question while looking to implement something similar, I first started with Artem Bilan's answer. However this did not work because annotations by default are not inherited in child classes unless they are themselves annotated with #Inherited. Despite this there may yet be a way to make an annotation approach work and I will update this answer if and when I get it to work. Thankfully though I have achieved the desired behavour using programtic registration of the Kafka listeners.
My code is something like the following:
Interface:
public interface GenericKafkaListener {
String METHOD = "handleMessage";
void handleMessage(ConsumerRecord<String, String> record);
}
Abstract Class:
public abstract class AbstractGenericKafkaListener implements GenericKafkaListener {
private final String kafkaTopic;
public AbstractGenericKafkaListener(final String kafkaTopic) {
this.kafakTopic = kafkaTopic;
}
#Override
public void handleMessage(final ConsumerRecord<String, String> record) {
//do common logic here
specificLogic(record);
}
protected abstract specificLogic(ConsumerRecord<String, String> record);
public String getKafkaTopic() {
return kafkaTopic;
}
}
We can then programtically register all beans of type AbstractGenericKafkaListener in a KafkaListenerConfigurer:
#Configuration
public class KafkaListenerConfigurataion implements KafkaListenerConfigurer {
#Autowired
private final List<AbstractGenericKafkaListener> listeners;
#Autowired
private final BeanFactory beanFactory;
#Autowired
private final MessageHandlerMethodFactory messageHandlerMethodFactory;
#Autowired
private final KafkaListenerContainerFactory kafkaListenerContainerFactory;
#Value("${your.kafka.consumer.group-id}")
private String consumerGroup;
#Value("${your.application.name}")
private String service;
#Override
public void configureKafkaListeners(
final KafkaListenerEndpointRegistrar registrar) {
final Method listenerMethod = lookUpMethod();
listeners.forEach(listener -> {
registerListenerEndpoint(listener, listenerMethod, registrar);
});
}
private void registerListenerEndpoint(final AbstractGenericKafkaListener listener,
final Method listenerMethod,
final KafkaListenerEndpointRegistrar registrar) {
log.info("Registering {} endpoint on topic {}", listener.getClass(),
listener.getKafkaTopic());
final MethodKafkaListenerEndpoint<String, String> endpoint =
createListenerEndpoint(listener, listenerMethod);
registrar.registerEndpoint(endpoint);
}
private MethodKafkaListenerEndpoint<String, String> createListenerEndpoint(
final AbstractGenericKafkaListener listener, final Method listenerMethod) {
final MethodKafkaListenerEndpoint<String, String> endpoint = new MethodKafkaListenerEndpoint<>();
endpoint.setBeanFactory(beanFactory);
endpoint.setBean(listener);
endpoint.setMethod(listenerMethod);
endpoint.setId(service + "-" + listener.getKafkaTopic());
endpoint.setGroup(consumerGroup);
endpoint.setTopics(listener.getKafkaTopic());
endpoint.setMessageHandlerMethodFactory(messageHandlerMethodFactory);
return endpoint;
}
private Method lookUpMethod() {
return Arrays.stream(GenericKafkaListener.class.getMethods())
.filter(m -> m.getName().equals(GenericKafkaListener.METHOD))
.findAny()
.orElseThrow(() ->
new IllegalStateException("Could not find method " + GenericKafkaListener.METHOD));
}
}
How about this:
public abstract class BaseKafkaProcessingLogic {
#KafkaHandler
public void handle(Object payload) {
}
}
#KafkaListener(topics = "topic1")
public class Topic1Handler extends BaseKafkaProcessingLogic {
}
#KafkaListener(topics = "topic2")
public class Topic2Handler extends BaseKafkaProcessingLogic {
}
?
I needed the same functionality and came up with solution close to Artem Bilan answer. Yes, #KafkaHandler annotation is not inherited by the child classes but defined in interface it is. Here is the solution:
interface AbstractKafkaListener<T> {
default Class<T> getCommandType() {
TypeToken<T> type = new TypeToken<>(getClass()) {};
return (Class<T>) type.getRawType();
}
#KafkaHandler
default void handle(String message) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
T value = objectMapper.readValue(message, getCommandType());
handle(value);
}
void handle(T message);
}
The class should implement the handle method only:
#Component
#KafkaListener(topics = "my_topic")
public class KafkaListenerForMyCustomMessage implements AbstractKafkaListener<MyCustomMessage> {
#Override
public void handle(MyCustomMessage message) {
System.out.println(message);
}
}
The 2 implemented methods in the interface should be private/protected but because they are in interface this cannot be done. default methods are always public. Actually, all methods defined in interface are always public.
I use this solution to dynamically parse the message from kafka (received in String) to the custom class.
getCommandType method returns the class of the T generic param. TypeToken is from Google Guava package.
I am trying to create a custom http param binding for my restful service. Please see the example below.
#POST
#Path("/user/{userId}/orders")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public MyResult foo(#PathParam("userId") String someString, #UserAuthHeaderParam String authString){
}
You can see that there is a UserAuthHeaderParam annotation in the function signature. What I want to do is have a custom http param binding other than the standard javax.ws.rs.*Param .
I have try to implement org.glassfish.hk2.api.InjectionResolver which basically extract the value from http header:
public class ProtoInjectionResolver implements InjectionResolver<UserAuthHeaderParam>{
...
#Override
public Object resolve(Injectee injectee, ServiceHandle< ? > root)
{
return "Hello World";
}
...
}
When I call the restful service, the server get below exceptions. It indicates that the framework fails to resolve the param in the function signature:
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at Injectee(requiredType=String,parent=MyResource,qualifiers={}),position=0,optional=false,self=false,unqualified=null,2136594195),
java.lang.IllegalArgumentException: While attempting to resolve the dependencies of rs.server.MyResource errors were found
Please help. Any advise is appreciated. I do make a lot of search on google but fails to make it work. Jersey 2.*. How to replace InjectableProvider and AbstractHttpContextInjectable of Jersey 1.* might be the similar question.
-- UPDATES:
I use AbstractBinder to bind my resolver to UserAuthHeaderParam:
public class MyApplication extends ResourceConfig
{
public MyApplication()
{
register(new AbstractBinder()
{
#Override
protected void configure()
{
// bindFactory(UrlStringFactory.class).to(String.class);
bind(UrlStringInjectResolver.class).to(new TypeLiteral<InjectionResolver<UrlInject>>()
{
}).in(Singleton.class);
}
});
packages("rs");
}
}
Thank you!
If all you want is to pass value directly from the header to the method you don't need to create custom annotations. Let's say you have a header Authorization, then you can easily access it by declaring your method like this:
#GET
public String authFromHeader(#HeaderParam("Authorization") String authorization) {
return "Header Value: " + authorization + "\n";
}
You can test it by calling curl, e.g.
$ curl --header "Authorization: 1234" http://localhost:8080/rest/resource
Header Value: 1234
Given that the answer to your question, how to create custom binding is as follows.
First you have to declare your annotation like this:
#java.lang.annotation.Target(PARAMETER)
#java.lang.annotation.Retention(RUNTIME)
#java.lang.annotation.Documented
public #interface UserAuthHeaderParam {
}
Having your annotation declared you have to define how it will be resolved. Declare the Value Factory Provider (this is where you'll have access to the header parameters - see my comment):
#Singleton
public class UserAuthHeaderParamValueFactoryProvider extends AbstractValueFactoryProvider {
#Inject
protected UserAuthHeaderParamValueFactoryProvider(MultivaluedParameterExtractorProvider mpep, ServiceLocator locator) {
super(mpep, locator, Parameter.Source.UNKNOWN);
}
#Override
protected Factory<?> createValueFactory(Parameter parameter) {
Class<?> classType = parameter.getRawType();
if (classType == null || (!classType.equals(String.class))) {
return null;
}
return new AbstractHttpContextValueFactory<String>() {
#Override
protected String get(HttpContext httpContext) {
// you can get the header value here
return "testString";
}
};
}
}
Now declare an injection resolver
public class UserAuthHeaderParamResolver extends ParamInjectionResolver<UserAuthHeaderParam> {
public UserAuthHeaderParamResolver() {
super(UserAuthHeaderParamValueFactoryProvider.class);
}
}
and a Binder for your configuration
public class HeaderParamResolverBinder extends AbstractBinder {
#Override
protected void configure() {
bind(UserAuthHeaderParamValueFactoryProvider.class)
.to(ValueFactoryProvider.class)
.in(Singleton.class);
bind(UserAuthHeaderParamResolver.class)
.to(new TypeLiteral<InjectionResolver<UserAuthHeaderParam>>() {})
.in(Singleton.class);
}
}
now the last thing, in your ResourceConfig add register(new HeaderParamResolverBinder()), like this
#ApplicationPath("rest")
public class MyApplication extends ResourceConfig {
public MyApplication() {
register(new HeaderParamResolverBinder());
packages("your.packages");
}
}
Given that, you should be now able to use the value as you wanted:
#GET
public String getResult(#UserAuthHeaderParam String param) {
return "RESULT: " + param;
}
I hope this helps.
I don't know how to resolve your exception. However, may I propose you a different way to do the same thing. I hope it helps.
I've faced exactly the same problem: I need extra parameters in the http header (btw, also related to authentication). Besides, I need to send them in every call, since I want to do a "typical" rest implementation, without maintaining a session.
I'm using Jersey 2.7 - but I'd say it should work in 2.0. I've followed their documentation
https://jersey.java.net/documentation/2.0/filters-and-interceptors.html
It's quite clear there, but anyway I copy-paste my implementation below.
It works fine. True there are some other ways to secure a rest service, for example this is a good one:
http://www.objecthunter.net/tinybo/blog/articles/89
But they depend on the application server implementation and the database you use. The filter, in my opinion, is more flexible and easier to implement.
The copy-paste: I've defined a filter for authentication, which applies to every call and it is executed before the service (thanks to #PreMatching).
#PreMatching
public class AuthenticationRequestFilter implements ContainerRequestFilter {
#Override
public void filter(final ContainerRequestContext requestContext) throws IOException {
final MultivaluedMap<String, String> headers = requestContext.getHeaders();
if (headers == null) {
throw new...
}
// here I get parameters from the header, via headers.get("parameter_name")
// In particular, I get the profile, which I plan to use as a Jersey role
// then I authenticate
// finally, I inform the Principal and the role in the SecurityContext object, so that I can use #RolesAllowed later
requestContext.setSecurityContext(new SecurityContext() {
#Override
public boolean isUserInRole(final String arg0) {
//...
}
#Override
public boolean isSecure() {
//...
}
#Override
public Principal getUserPrincipal() {
//...
}
#Override
public String getAuthenticationScheme() {
//...
}
});
}
}
You have to include this filter class in your implementation of ResourceConfig,
public class MyResourceConfig extends ResourceConfig {
public MyResourceConfig() {
// my init
// my packages
register(AuthenticationRequestFilter.class); // filtro de autenticación
// other register
}
}
Hope it helps!
If your need is to retrieve all the http headers binding into one object, a solution could be to use the #Context annotation to get javax.ws.rs.core.HttpHeaders; which contains the list of all request headers.
#POST
#Path("/user/{userId}/orders")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public MyResult foo(#PathParam("userId") String someString, #Context HttpHeaders headers){
// You can list all available HTTP request headers via following code :
for(String header : headers.getRequestHeaders().keySet()){
System.out.println(header);
}
}
here is my actual implementatipn of UserAuthHeaderParamValueFactoryProvider class
import javax.inject.Inject;
import javax.inject.Singleton;
import org.glassfish.hk2.api.Factory;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.jersey.server.internal.inject.AbstractContainerRequestValueFactory;
import org.glassfish.jersey.server.internal.inject.AbstractValueFactoryProvider;
import org.glassfish.jersey.server.internal.inject.MultivaluedParameterExtractorProvider;
import org.glassfish.jersey.server.model.Parameter;
#Singleton
public class UserAuthHeaderParamValueFactoryProvider extends AbstractValueFactoryProvider {
#Inject
protected UserAuthHeaderParamValueFactoryProvider(MultivaluedParameterExtractorProvider mpep, ServiceLocator locator) {
super(mpep, locator, Parameter.Source.UNKNOWN);
}
#Override
protected Factory<?> createValueFactory(Parameter parameter) {
Class<?> classType = parameter.getRawType();
if (classType == null || (!classType.equals(String.class))) {
return null;
}
return new AbstractContainerRequestValueFactory<String>() {
#Override
public String provide() {
//you can use get any header value.
return getContainerRequest().getHeaderString("Authorization");
}
};
}