Managing endpoint security and testing approaches - java

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??

Related

Avoiding long dependency lists in Spring Boot

I'm working on code that has an ever increasing amount of implementations for an interface VendorService. Right now, where these services are used, we autowire them all in the constructor, leading to long lists of dependencies. Is there a preferred way to handle dependencies when a single interface is repeatedly used?
Current approach:
private final VendorService xVendorService;
private final VendorService yVendorService;
private final VendorService zVendorService;
...
#Autowired
public VendorDelegateService(XVendorService xVendorService,
YVendorService yVendorService,
ZVendorService zVendorService,
...) {
this.xVendorService = xVendorService;
this.yVendorService = yVendorService;
this.yVendorService = yVendorService;
...
}
public void doSomething(VendorId vendorId) {
if (vendorId = VendorId.X) {
xVendorService.doSomething();
} else if (vendorId = VendorId.Y) {
yVendorService.doSomething();
} else if (vendorId = VendorId.Z) {
zVendorService.doSomething();
}
...
}
Clearly this is very verbose and requires updating whenever a new implementation of the interface is created.
An alternative is getting the Bean from the ApplicationContext, something like:
private final ApplicationContext context;
#Autowired
public VendorDelegateService(ApplicationContext context) {
this.context = context;
}
public void doSomething(VendorId vendorId) {
context.getBean(VendorService.class, vendorId.name()).doSomething();
}
This wouldn't require another if/else bracket with every new implementation, but it's obtuse and doesn't feel correct. This logic could of course be abstracted away in its own class to lessen that problem.
Which of these is more idiomatic in Spring and Java? Are there any other approaches I haven't considered?
I feel it is a matter of preference whether there is an idiomatic way for this, but what I suggest is the following solution:
Create an interface for all the services, we can call this VendorService:
public interface VendorService {
void doSomething();
VendorId getVendorId();
}
Now we would want to implement this interface for all the services, as an example this can be done like this for XVendorService:
#Service
public XVendorService implements VendorService {
private VendorId vendorId = ....
#Override
public void doSomething() {
...
}
#Override
public VendorId getKey() {
return vendorId;
}
}
Now for the VendorDelegateService we can do something like this:
#Service
public class VendorDelegateService {
private Map<VendorId, VendorService> services = new HashMap<>();
#Autowired
public AllServices(Set<? extends VendorService> serviceSet) {
serviceSet.stream().forEach(service -> services.put(service.getVendorId(), service));
}
public void doSomething(VendorId vendorId) {
if (services.containsKey(vendorId)) {
services.get(vendorId).doSomething();
}
}
}
Please note that with Set<? extends VendorService> serviceSet all the services will be autowired automatically. By creating a map afterwards, we are able to dispatch our request to every service based on its vendorKey.

Refactor polymorphism using Java 8

I have an old code base that I need to refactor using Java 8, so I have an interface, which tells whether my current site supports the platform.
public interface PlatformSupportHandler {
public abstract boolean isPaltformSupported(String platform);
}
and I have multiple classes implementing it and each class supports a different platform.
A few of the implementing classes are:
#Component("bsafePlatformSupportHandler")
public class BsafePlatoformSupportHandler implements PlatformSupportHandler {
String[] supportedPlatform = {"iPad", "Android", "iPhone"};
Set<String> supportedPlatformSet = new HashSet<>(Arrays.asList(supportedPlatform));
#Override
public boolean isPaltformSupported(String platform) {
return supportedPlatformSet.contains(platform);
}
}
Another implementation:
#Component("discountPlatformSupportHandler")
public class DiscountPlatoformSupportHandler implements PlatformSupportHandler{
String[] supportedPlatform = {"Android", "iPhone"};
Set<String> supportedPlatformSet = new HashSet<>(Arrays.asList(supportedPlatform));
#Override
public boolean isPaltformSupported(String platform) {
return supportedPlatformSet.contains(platform);
}
}
At runtime in my filter, I get the required bean which I want:
platformSupportHandler = (PlatformSupportHandler) ApplicationContextUtil
.getBean(subProductType + Constants.PLATFORM_SUPPORT_HANDLER_APPEND);
and call isPlatformSupported to get whether my current site supports the following platform or not.
I am new to Java 8, so is there any way I can refactor this code without creating multiple classes? As the interface only contains one method, can I somehow use lambda to refactor it?
If you want to stick to the current design, you could do something like this:
public class MyGeneralPurposeSupportHandler implements PlatformSupportHandler {
private final Set<String> supportedPlatforms;
public MyGeneralPurposeSupportHandler(Set<String> supportedPlatforms) {
this.supportedPlatforms = supportedPlatforms;
}
public boolean isPlatformSupported(String platform) {
return supportedPlatforms.contains(platform);
}
}
// now in configuration:
#Configuration
class MySpringConfig {
#Bean
#Qualifier("discountPlatformSupportHandler")
public PlatformSupportHandler discountPlatformSupportHandler() {
return new MyGeneralPurposeSupportHandler(new HashSefOf({"Android", "iPhone"})); // yeah its not a java syntax, but you get the idea
}
#Bean
#Qualifier("bsafePlatformSupportHandler")
public PlatformSupportHandler bsafePlatformSupportHandler() {
return new MyGeneralPurposeSupportHandler(new HashSefOf({"Android", "iPhone", "iPad"}));
}
}
This method has an advantage of not creating class per type (discount, bsafe, etc), so this answers the question.
Going step further, what happens if there no type that was requested, currently it will fail because the bean does not exist in the application context - not a really good approach.
So you could create a map of type to the set of supported platforms, maintain the map in the configuration or something an let spring do its magic.
You'll end up with something like this:
public class SupportHandler {
private final Map<String, Set<String>> platformTypeToSuportedPlatforms;
public SupportHandler(Map<String, Set<String>> map) {
this.platformTypeToSupportedPlatforms = map;
}
public boolean isPaltformSupported(String type) {
Set<String> supportedPlatforms = platformTypeToSupportedPlatforms.get(type);
if(supportedPlatforms == null) {
return false; // or maybe throw an exception, the point is that you don't deal with spring here which is good since spring shouldn't interfere with your business code
}
return supportedPlatforms.contains(type);
}
}
#Configuration
public class MyConfiguration {
// Configuration conf is supposed to be your own way to read configurations in the project - so you'll have to implement it somehow
#Bean
public SupportHandler supportHandler(Configuration conf) {
return new SupportHandler(conf.getRequiredMap());
}
}
Now if you follow this approach, adding a new supported types becomes codeless at all, you only add a configuration, by far its the best method I can offer.
Both methods however lack the java 8 features though ;)
You can use the following in your config class where you can create beans:
#Configuration
public class AppConfiguration {
#Bean(name = "discountPlatformSupportHandler")
public PlatformSupportHandler discountPlatformSupportHandler() {
String[] supportedPlatforms = {"Android", "iPhone"};
return getPlatformSupportHandler(supportedPlatforms);
}
#Bean(name = "bsafePlatformSupportHandler")
public PlatformSupportHandler bsafePlatformSupportHandler() {
String[] supportedPlatforms = {"iPad", "Android", "iPhone"};
return getPlatformSupportHandler(supportedPlatforms);
}
private PlatformSupportHandler getPlatformSupportHandler(String[] supportedPlatforms) {
return platform -> Arrays.asList(supportedPlatforms).contains(platform);
}
}
Also, when you want to use the bean, it is again very easy:
#Component
class PlatformSupport {
// map of bean name vs bean, automatically created by Spring for you
private final Map<String, PlatformSupportHandler> platformSupportHandlers;
#Autowired // Constructor injection
public PlatformSupport(Map<String, PlatformSupportHandler> platformSupportHandlers) {
this.platformSupportHandlers = platformSupportHandlers;
}
public void method1(String subProductType) {
PlatformSupportHandler platformSupportHandler = platformSupportHandlers.get(subProductType + Constants.PLATFORM_SUPPORT_HANDLER_APPEND);
}
}
As it was written in Mark Bramnik's answer you can move this to configuration.
Suppose that it would be in yaml in that way:
platforms:
bsafePlatformSupportHandler: ["iPad", "Android", "iPhone"]
discountPlatformSupportHandler: ["Android", "iPhone"]
Then you can create config class to read this:
#Configuration
#EnableConfigurationProperties
#ConfigurationProperties
public class Config {
private Map<String, List<String>> platforms = new HashMap<String, List<String>>();
// getters and setters
You can than create handler with checking code.
Or place it in your filter like below:
#Autowired
private Config config;
...
public boolean isPlatformSupported(String subProductType, String platform) {
String key = subProductType + Constants.PLATFORM_SUPPORT_HANDLER_APPEND;
return config.getPlatforms()
.getOrDefault(key, Collections.emptyList())
.contains(platform);
}

Implementing shared logic for multiple KafkaListeners in spring-kafka

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.

jersey 2: How to create custom HTTP param binding

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");
}
};
}

Spring MVC: Appropriate extension point for wrapping API Responses

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;
}
}
}
}

Categories

Resources