AWS Lambda Java Integration - aws-lambda

I want to integrate my Saml SSO application with AWS Lambda, but unfortunately my Saml code takes its input as can be seen below in the code. I need to send HttpServletRequest and HttpServletResponse as input to my java handler. So it requires request and response as input but my lambda handler takes only input as JSON or java POJO, and I am confused as how to proceed.
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
//validation
//output
return authentication;
}

The AWS team has created a serverless wrapper that exposes request and response objects. This should allow you to do what you need.In their handler you implement a new interface and their underlying functionality returns the request and response to you as AwsProxyRequest and AwsProxyResponse which should be children of HttpServletRequest and HttpServletResponse.
Code
public class StreamLambdaHandler implements RequestStreamHandler {
private SpringLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler;
private Logger log = LoggerFactory.getLogger(StreamLambdaHandler.class);
#Override
public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context)
throws IOException {
if (handler == null) {
try {
handler = SpringLambdaContainerHandler.getAwsProxyHandler(PetStoreSpringAppConfig.class);
} catch (ContainerInitializationException e) {
log.error("Cannot initialize Spring container", e);
outputStream.close();
throw new RuntimeException(e);
}
}
AwsProxyRequest request = LambdaContainerHandler.getObjectMapper().readValue(inputStream, AwsProxyRequest.class);
AwsProxyResponse resp = handler.proxy(request, context);
LambdaContainerHandler.getObjectMapper().writeValue(outputStream, resp);
// just in case it wasn't closed by the mapper
outputStream.close();
}
}
Source -> https://github.com/awslabs/aws-serverless-java-container

Related

Spring Security - How to handle a RuntimeException in a custom AuthenticationFilter?

Using Spring Security, I have created a custom UsernamePasswordAuthenticationFilter. In this filter's attemptAuthentication method, I would like to retrieve the body of the HttpServletRequest, since credentials should be passed inside the body instead of request parameters.
I think I have found a good way to achieve this, but I am unsure about how to handle the IOException that could now occur inside this method. I have to catch the IOException inside this method, since the original method, which I override, does not throw an IOException.
This is my implementation:
#RequiredArgsConstructor
public class AuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private final AuthenticationManager authenticationManager;
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
try {
UserDTO user = new ObjectMapper().readValue(request.getInputStream(), UserDTO.class);
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword());
return authenticationManager.authenticate(authenticationToken);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
My IDE suggests to throw a custom exception instead of a RuntimeException. But since this filter is part of the Spring Security filter chain, I am unsure about what should happen in case of an IOException.

Calling micro service from spring cloud gateway

In spring cloud gateway, added a filter that check for the authentication and authorization for further processing of request. I am calling authentication service using feign client, but I am getting the below error while invoking my service through spring cloud gateway.
java.lang.IllegalStateException: block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-epoll-3\n\tat reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:83)\n\tSuppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: \nError has been observed at the following site(s):\n\t|_ checkpoint ⇢ org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter ....."
I would like to know is it wrong architecture I am using. How to proceed? I am stuck at this error.
#Autowired
private AuthenticationService authService;
// route validator
#Autowired
private RouterValidator routerValidator;
#Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
if (routerValidator.isSecured.test(request)) {
log.info("Accessing the restricted path");
if (this.isAuthMissing(request))
return this.onError(exchange, "Authorization header is missing in request", HttpStatus.UNAUTHORIZED);
final String token = this.getAuthHeader(request);
log.info("before authservice call");
AuthenticationResponse user = authService.isTokenValid(token);
log.info("after authservice call");
if (!user.isValid())
return this.onError(exchange, "Authorization header is invalid", HttpStatus.UNAUTHORIZED);
log.info("before calling populatedRequest");
this.populateRequestWithHeaders(exchange, user);
}
return chain.filter(exchange);
}
private Mono<Void> onError(ServerWebExchange exchange, String err, HttpStatus httpStatus) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(httpStatus);
return response.setComplete();
}
private String getAuthHeader(ServerHttpRequest request) {
return request.getHeaders().getOrEmpty("Authorization").get(0);
}
private boolean isAuthMissing(ServerHttpRequest request) {
log.info("inside auth missing");
return !request.getHeaders().containsKey("Authorization");
}
private void populateRequestWithHeaders(ServerWebExchange exchange, AuthenticationResponse authRes) {
log.info("About to mutate the request->{}",exchange);
exchange.getRequest().mutate()
.header("id",Integer.toString(authRes.getUserId()))
.build();
}
Feign interface
#Autowired
private AuthenticationFeign auth;
public AuthenticationResponse isTokenValid(String token) {
return auth.getValidity(token);
}
I couldn't clearly read it. But problem is that: you can not make blocking call in filter pipeline. Current reactive impl. is like that. if you want, u can use .then() method of WebClient. U should use webclient. because it's reactive.
this link may help you:
https://github.com/spring-cloud/spring-cloud-gateway/issues/980
There was a long time, but i want to give answer. I hope, this help u, please response back, it works or not.

How do I create a JSONP filter with /**/ prepended to the callback?

I am upgrading a Spring Boot application to version 2.0 and Spring Framework to version 5.1.
The application currently uses Spring's built in JSONP support using AbstractJsonpResponseBodyAdvice.
#ControllerAdvice
public class JsonpControllerAdvice extends AbstractJsonpResponseBodyAdvice {
public JsonpControllerAdvice() {
super("jsonp");
}
}
However, JSONP support was deprecated in version 5.0.7 and removed in version 5.1 RC1. In addition, it's not feasible to switch to CORS at this time.
A final caveat is that the JavaScript callback method must begin with /**/. For example (truncated):
/**/jQuery1720351297557893959_1567180700293(...)
I've tried using jsonp-filter but I am unable to configure the callback to include /**/.
How do I create a custom Spring Boot JSONP filter with /**/ prepended to the callback?
Note: My example is similar to Spring Boot: Remove /**/ before JSONP callback function name. But I can't remove the /**/ because the existing frontend code expects it in the callback.
While you can't use jsonp-filter, you can define a simple filter based on it. For example:
#Component
public class JsonPFilter implements Filter {
#Override public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
String callback = null;
if (request instanceof HttpServletRequest) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
callback = httpServletRequest.getParameter("jsonp");
}
if (callback != null) {
OutputStream out = response.getOutputStream();
out.write(String.format("/**/%s(", callback).getBytes());
chain.doFilter(request, response);
out.write(new JsonPResponseWrapper((HttpServletResponse) response).getData());
out.write(")".getBytes());
out.close();
} else {
chain.doFilter(request, response);
}
}
private static class JsonPResponseWrapper extends HttpServletResponseWrapper {
private JsonPResponseWrapper(HttpServletResponse response) {
super(response);
}
private byte[] getData() {
return new ByteArrayOutputStream().toByteArray();
}
}
}

SpringBoot 2 - OncePerRequestFilter - modify response Headers after processing of Controller

Hello I want to modify some of my API's response Headers after I have completed processing (executed logic) and have concluded with an HTTP status code.
For example if the response is 404, then include specific for example Cache-Control Headers example dont cache, or something like that.
I have already 2 OncePerRequestFilter registered, which work fine - but obviously I can not do logic - once the processing is complete. The CacheControlFilter already has logic that adds by default some Cache-Control headers - e.g cache for 15 sec etc. It seems though that this happens (the addition of headers on the response) on a very early stage of the dispatch and when it reaches to the phase of executing the actual Controller/Endpoint and there is an exception or Error that obviously is going to be handled by an advice etc, I can not mutate these already existing headers- that were already added by the filter.
#Bean
public FilterRegistrationBean filterOne() {
Filter filter = new FilterOne();
return createFilter(filter, "FilterOne",List.of("/*"));
}
#Bean
public FilterRegistrationBean cacheControlFilter() {
Filter filter = new CacheControlFilter();
return createFilter(filter, "CacheControlFilter", List.of("/*"));
}
private FilterRegistrationBean createFilter(Filter aFilter, String filterName,
List<String> urlPatterns) {
FilterRegistrationBean filterRegBean = new FilterRegistrationBean(aFilter);
filterRegBean.addUrlPatterns(urlPatterns.toArray(new String[0]));
filterRegBean.setName(filterName);
filterRegBean.setEnabled(true);
filterRegBean.setAsyncSupported(true);
return filterRegBean;
}
I have already tried, to add an HttpServletResponseWrapper as indicated on these post here and here on the CacheControlFilter but it does not seem to work. I have also seen a similar S.O thread here.
HttpServletResponseWrapper wrapper = new HttpServletResponseWrapper(response) {
#Override
public void setStatus(int sc) {
super.setStatus(sc);
handleStatus(sc);
}
#Override
#SuppressWarnings("deprecation")
public void setStatus(int sc, String sm) {
super.setStatus(sc, sm);
handleStatus(sc);
}
#Override
public void sendError(int sc, String msg) throws IOException {
super.sendError(sc, msg);
handleStatus(sc);
}
#Override
public void sendError(int sc) throws IOException {
super.sendError(sc);
handleStatus(sc);
}
private void handleStatus(int code) {
if(code == 404)
addHeader("Cache-Control, "xxx");
}
};
But the code is not executed at all! So I want to manipulate the Cache-Control headers on the second filter only after though the processing is complete and I am ready to return a response.
I am not sure if the fact that I also have, doing some clean up and setting responses upon errors - mixes things up!
#ControllerAdvice
#Slf4j
public class GlobalErrorHandler
Update: As a note, when my Controller is throwing an Exception or Error, the above GlobalErrorHandler is invoked and there I execute a special handling, returning an error response. What I see though is that magically the response has already the default headers populated by the Filter (CacheControlFilter). So it ends up being a bit weird, I add extra logic,to change the control header and I end up with a response that has the same header 2 times (1 with the value set by the CacheControlFilter and then any special value I am trying to override on the ControllerAdvice
Any tips or help appreciated thanks! I am using Spring Boot 2.1.2 with Undertow as my underlying servlet container.
The link you mentioned says that cannot get the status code or modify the headers in ResponseBodyAdvice is not true . If you cast ServerHttpResponse to ServletServerHttpResponse , you can do both of them. So simply implement a ResponseBodyAdvice :
#ControllerAdvice
public class CacheControlBodyAdvice implements ResponseBodyAdvice {
#Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true;
}
#Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
if(response instanceof ServletServerHttpResponse) {
ServletServerHttpResponse res= (ServletServerHttpResponse)(response);
if(res.getServletResponse().getStatus() == 400){
res.getServletResponse().setHeader("Cache-Control", "XXXXX");
}
}
return body;
}
}
One more thing need to pay attention is that if your controller method throws an exception before complete normally , depending on how to handle the exceptions , the ResponseBodyAdvice may not be trigger. So , I suggest to implement the same logic in the GlobalErrorHandler for safety guard :
#ControllerAdvice
public class GlobalErrorHandler{
#ExceptionHandler(value = Exception.class)
public void handle(HttpServletRequest request, HttpServletResponse response) {
if(response.getStatus() == 400){
response.setHeader("Cache-Control", "XXXXX");
}
}
}
I supposed that you are using spring-mvc (As you mentioned in your tags); If so you can bind to HttpServletResponse to add your headers. You can do it in your method handler like so:
#RestController
class HelloWordController{
#GetMapping("/hello")
public String test(HttpServletResponse response){
response.addHeader("test", "123");
return "hola";
}
}
Another solution (fashion) would be to return a ResponseEntity instead :
#RestController
class HelloWorkController{
#GetMapping("/hello")
public ResponseEntity<String> test(HttpServletResponse response){
return ResponseEntity.status(HttpStatus.OK)
.header("test", "4567")
.body("hello world");
}
}
There are a dozen of ways of changing a HttpServletResponse before return to client in Spring and injecting the response into the handler method or leveraging ControllerAdvice are valid solutions. However, I don't understand the underlying premise of your question that filters can't do the job:
I have already 2 OncePerRequestFilter registered, which work fine -
but obviously I can not do logic - once the processing is complete.
As far as modifying HttpServletResponse is concerned, Filters work totally fine for me and are at least as suitable as any other tool for that job:
#Bean
public FilterRegistrationBean createFilter() {
Filter filter = new OncePerRequestFilter() {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
super.doFilter(request, response, filterChain);
response.setHeader("Cache-Control", "xxx");
}
};
return new FilterRegistrationBean(filter);
}

dynamically add param to userAuthorizationUri in oauth2

Sometimes user's refresh token in local DB becomes stale. To replenish I'm trying to add prompt=consent param while making the oauth2 call. I was trying to #Autowire AuthorizationCodeAccessTokenProvider in my config class and in the afterPropertiesSet I was doing a setTokenRequestEnhancer and then realized that this bean is not even initialized via spring container when i looked the following code in OAuth2RestTemplate
private AccessTokenProvider accessTokenProvider = new AccessTokenProviderChain(Arrays.<AccessTokenProvider> asList(
new AuthorizationCodeAccessTokenProvider(), new ImplicitAccessTokenProvider(),
new ResourceOwnerPasswordAccessTokenProvider(), new ClientCredentialsAccessTokenProvider()));
Searched if any spring code is calling org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider.setAuthorizationRequestEnhancer(RequestEnhancer) to learn how to access it, but no one is calling it.
Question: How to dynamically add a param to userAuthorizationUri while making oauth2 call?
Unfortunately, I haven't found an elegant solution neither. I have noticed, however, that redirect is triggered by UserRedirectRequiredException.
I was able to dynamically add request params by registering custom filter that modifies this exception on the fly.
#Component
#Order(-102)
public class EnhanceUserRedirectFilter implements Filter {
#Override
public void init(final FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException {
try {
chain.doFilter(request, response);
} catch (final UserRedirectRequiredException ex) {
ex.getRequestParams().put("prompt", "consent");
throw ex;
}
}
#Override
public void destroy() {
}
}
Please note, such servlet filter has to have higher precedence than Spring Security. In my case, -102 is higher precedence than Spring Security default of -100.

Resources