Can spring cloud functions get access to any spring managed component? - java

I'm working on a spring-cloud-function with Azure-functions.
Is it possible to use any spring managed components within the "handlers" (extending AzureSpringBootRequestHandler) ?
I tried to narrow this down with the sample project:
https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-samples/function-sample-azure
So the simplest example I could imagine is:
public class UppercaseHandler extends AzureSpringBootRequestHandler<String, String> {
private final UppercaseService uppercaseService;
#Autowired
public UppercaseHandler (UppercaseService uppercaseService){
this.uppercaseService = uppercaseService;
}
#FunctionName("uppercase")
public String execute(#HttpTrigger(name = "req", methods = {HttpMethod.GET,
HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<String>> request,
ExecutionContext context) {
return handleRequest(request.getBody().get(), context);
}
}
However it looks like this handler is not managed by spring and does not work with autowiring.
Would be great to get some help, thanks!

You are trying to use Spring auto-wiring in the Azure Function HTTP request handler (a very thin adapter specific to cloud provider) which is decoupled outside Spring function. That's why it's not working there. You should not have any business logic in the adapter. But if you use DI in any Spring controller/service it would work like in a regular Spring boot application.
You can check out this handy blog post Playing with Spring Cloud in Azure Functions.

Related

how to use #Requestmapping of a different project

I have a Java project with various #RequestMapping annotations.I want to make a new project which can use this #RequestMapping,is that possible
Of course you can.
If I understand your question correctly, do you want to use the data provided by a Spring application in another application?
It's not that hard, you just have to keep a few things in mind.
The applications have to run on different ports, of course both applications have to be started.
For example, App1 has a #RequestMapping #GetMapping for personal data.
The path is e.g. http://localhost:8080/persondata
In the second application, you only need to address the API endpoint if you need this data.
This can be done with RestTemplate, for example.
#RestController
#RequestMapping("/persondata")
class PersonDataRestController {
private final Service personService;
public PersonDataRestController(Service personService) {
this.personService = personService;
}
#GetMapping
public ResponseEntity<Collection<?>> getAllPersonData() {
return ResponseEntity.ok(personService.allPersonData());
}
}
You just have to replace the Service with your PersonService or whatelse.
in the second application you can call the REST endpoint with RestTemplate for example
RestTemplate template = new RestTemplate();
try{
ResponseEntity<ArrayList<?>> response = template.exchange("http://localhost:8080/persondata", HttpMethod.GET, null, new ParameterizedTypeReference<ArrayList<?>>() {});
In terms of the specific application, you may need a DTO object.
For this topic i can suggest you this website
I hope I could answer your question

How do I use "/images" path in Spring boot Rest controller with security?

I have a Spring boot REST service (spring-boot-starter-parent:1.3.2) that exposes some endpoints using RestController defined methods. I am also using Spring security. Everything works fine until I try to define a controller method that is mapped to "/images". When I try to access this api path I get the following error. By debugging I can see that my controller handler is being mapped, but the preauthorize filter is not being called (it is called properly for other mappings). I have set the following properties, but with no change. How do I fix this so that I can use "/images"?
spring.resources.add-mappings=false
spring.mvc.static-path-pattern=/hide-me/**
Error:
"exception": "org.springframework.security.authentication.AuthenticationCredentialsNotFoundException",
"message": "An Authentication object was not found in the SecurityContext",
Code:
#RestController
#PreAuthorize(value = "hasAnyAuthority('SOMEUSER')")
public class ImageController {
...
#RequestMapping(value = { "/images/{imageId}" }, method = RequestMethod.GET)
#ResponseBody
public Image getImage(#PathVariable UUID imageId) {
return imageDataService.getImage(imageId);
}
...
If I change the mapping to the following then it works just fine.
#RequestMapping(value = { "/image/{imageId}" }, method = RequestMethod.GET)
#ResponseBody
public Image getImage(#PathVariable UUID imageId) {
return imageDataService.getImage(imageId);
}
I'm thinking that the config for static resources has a default entry that tells Spring security to ignore the "/images" path for the preauth filter. I'm debugging around trying to figure out where that might be overridden.
SpringBoot by default use some paths
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
"classpath:/META-INF/resources/", "classpath:/resources/",
"classpath:/static/", "classpath:/public/" };
https://spring.io/blog/2013/12/19/serving-static-web-content-with-spring-boot
And one of this paths is /images
Java Web Application. Spring Boot. Locating Images
Also you have the following restrictions when usind SpringSecurity
The basic features you get out of the box in a web application are:
An AuthenticationManager bean with in-memory store and a single user
(see SecurityProperties.User for the properties of the user). Ignored
(insecure) paths for common static resource locations (/css/,
/js/, /images/, /webjars/ and **/favicon.ico). HTTP Basic
security for all other endpoints. Security events published to
Spring’s ApplicationEventPublisher (successful and unsuccessful
authentication and access denied).
http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/
Common low-level features (HSTS, XSS, CSRF, caching) provided by Spring Security are on by default.
You need to ensure, that security is done for every request. This can be done using the following SecurityConfiguration:
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated();
}
}

Overriding RolesAllowedDynamicFeature in Jersey 2.5.1

Short and sweet:
I want to be able to filter incoming requests to authenticate the user then take the roles defined in my database and use them in the Jersey 2.5.1 Service classes.
e.g.
#RolesAllowed("Custom1", "Custom2")
#Post
.....
Currently I have the following, which seems to work with the basic #PermitAll and #DenyAll annotations, I am just not sure how to overload/what to overload to get some custom code working with Jersey 2.5.1. I've seen examples for Jersey1. Should I just create a request Filter and do it in there and set the securityContext? Any help would be appreciated.
public class TestApi extends ResourceConfig {
public TestApi() {
super(AuthenticateResource.class);
register(RolesAllowedDynamicFeature.class);
}
}
Figured out my problem. Injected Resource Info then pulled out the annotation. This works if it's not pre-matching
#Context
private ResourceInfo resourceInfo;
Annotation[] annotations = resourceInfo.getResourceMethod().getDeclaredAnnotations();
SecurityContext is either set by the underlying container or it's set manually in your application (usually in ContainerRequestFilter). If your container is sophisticated enough to set the correct security context for you (with correct principal) you can go this way. Otherwise create a custom ContainerRequestFilter similar to the one in Jersey example ContainerAuthFilter.

Jersey Async ContainerRequestFilter

I have a Jersey REST API and am using a ContainerRequestFilter to handle authorization. I'm also using #ManagedAsync on all endpoints so that my API can serve thousands of concurrent requests.
My authorization filter hits a remote service, but when the filter is run, Jersey hasn't yet added the current thread to it's internal ExecutorService, so I'm completely losing the async benefits.
Can I tell Jersey that I want this ContainerRequestFilter to be asynchronous?
#Priority(Priorities.AUTHORIZATION)
public class AuthorizationFilter implements ContainerRequestFilter
{
#Inject
private AuthorizationService authSvc;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException
{
String authToken = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
// HITS A REMOTE SERVER
AuthorizationResponse authResponse = authSvc.authorize(authToken);
if (!authResponse.isAuthorized())
{
requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED)
.entity("unauthorized!")
.build());
}
}
}
And here's an example resource:
#Path("/stuff")
#Produces(MediaType.APPLICATION_JSON)
public class StuffResource
{
#GET
#Path("/{id}")
#ManagedAsync
public void getById(#PathParam("id") long id, #Suspended final AsyncResponse ar)
{
Stuff s;
// HIT THE DATABASE FOR STUFF
ar.resume(s);
}
}
UPDATE Just heard back from the Jersey guys, and this is not possible as of 2.7. Only the resource method itself is invoked asynchronously, not filters. Any suggestions for proceeding still welcome.
This is not built in to Jersey as of 2.7.
#ManagedAsync is useless if you have any filters or interceptors that do any serious work (like hit a remote authorization service). They may add the ability to run filters asynchronously in the future, but for now you're on your own.
UPDATE - there are other ways...
After a long and perilous journey, I have found a very hacky solution that I'm using in the short term. Here is a rundown of what I tried and why it failed/worked.
Guice AOP - failed
I use Guice for DI (getting Guice injection to work with Jersey is a feat in itself!), so I figured I could use Guice AOP to get around the issue. Though Guice injection works, it is impossible to get Guice to create resource classes with Jersey 2, so Guice AOP cannot work with resource class methods. If you are trying desperately to get Guice to create resource classes with Jersey 2, don't waste your time because it will not work. This is a well-known problem.
HK2 AOP - RECOMMENDED SOLUTION
HK2 just recently released an AOP feature, see this question for details on how to get it working.
Monitoring - also worked
This is not for the faint of heart, and it is completely discouraged in the Jersey docs. You can register and ApplicationEventListener and override onRequest to return a RequestEventListener that listens for RESOURCE_METHOD_START and calls an authentication/authorization service. This event is triggered from the #ManagedAsync thread, which is the whole goal here. One caveat, the abortWith method is a no-op, so this won't work quite like a normal ContainerRequestFilter. Instead, you can throw an exception if auth fails instead, and register an ExceptionMapper to handle your exception. If someone is bold enough to give this a try, let me know and I'll post code.
I am not sure if this is what you were looking for but, have you looked into Spring's OncePerRequestFilter? I am currently using it for my authorization layer where each request goes through some filter that extends this OncePerRequestFilter depending on how my filters are mapped to the URLs. Here's a quick overview of how I am using it:
Authentication/Authorization of a resource in Dropwizard
I am not very clear on the async dispatch parts of these filters but I hope this link atleast sheds some light to what you are trying to achieve!
We use Spring security for authentication/authorization. I worked around the problem using a sub-resource locator with empty path as shown below:
#Path("/customers")
public class CustomerResource {
#Inject
private CustomerService customerService;
#Path("")
public CustomerSubResource delegate() {
final Authentication auth = SecurityContextHolder.getContext().getAuthentication();
return new CustomerSubResource(auth);
}
public class CustomerSubResource {
private final Authentication auth;
public CustomerSubResource(final Authentication auth) {
this.auth = auth;
}
#POST
#Path("")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
#ManagedAsync
public void createCustomer(final Customer customer, #Suspended final AsyncResponse response) {
// Stash the Spring security context into the Jersey-managed thread
SecurityContextHolder.getContext().setAuthentication(this.auth);
// Invoke service method requiring pre-authorization
final Customer newCustomer = customerService.createCustomer(customer);
// Resume the response
response.resume(newCustomer);
}
}
}

Spring MVC Custom View

I am refactoring a legacy application to use Spring MVC. All of my controllers (legacy) return an object of type Model and my legacy dispatcher write the output of model.getContent(), the method getContent does internal processing and returns a json string. I have hundreds of controllers and do not want to rewrite them. Is it possible to write a custom view handler and include it in the spring servlet config?
Sample Controller:
public UserList extends BasicAction {
#Autowired
UserService userService;
#Autowired
UserCommand userCommand;
#Override
public Model getModel(Request req, Response resp)
throws ServletException, IOException {
Model model = new Model();
List<User> users;
try {
users = userService.getUsers((UserCriteria)userCommand.getResult());
model.addCollection(users);
model.setWrapper(new UserWrapper());
} catch (ValidationException e) {
e.printStackTrace();
} catch (WebCommandException e) {
e.printStackTrace();
}
return model;
}
}
I'm planning to annotate as #Controller. Specify the #RequestMapping or in the xml config, remove the base class BasicAction (legacy mvc). I've recently introduced spring to this project and refactored to use Dependency Injection and Request Scoped command objects (request wrappers)
The most straightforward is to implement View interface on your Model class. Then your legacy controllers can return this class directly (as they are now) and it will get rendered by DispatcherServlet via calling its render method.
Another possibility is to implement your own HandlerMethodReturnValueHandler, where the handler can actually render the response and mark the response as handled (mavContainer.setRequestHandled(true);) so that DispatcherServlet will not try to render any view.
I think what you want to do is create a custom ViewResolver that outputs your JSON response. This would be configured in Spring MVC to set the ViewResolver list, placing yours up top to have more precedence. The way it is supposed to work (from my recollection) is that Spring MVC will start at the top of the list, and try each ViewResolver until it finds one that returns the one that handles the return type. You will have to google how to make custom ViewResolvers, as I have only used them, never created one, but I know it's an interface so it should be do-able. I believe this would be the only way to do this that would not require any code changes in your controllers.
The more "preferred" method to do JSON, however, is to have Jackson in your classpath and simply return the object you want to serialize to JSON. Spring will auto-magically convert that to JSON, I believe using a ViewResolver they provide. But, I can surely relate to not wanting to refactor lots of working code.

Categories

Resources