Spring Boot: "relaying" basic auth from REST controller to RestTemplate - spring

I'm working with two Spring Boot applications, let's call them ServiceA and ServiceB, both exposing a REST API.
ServiceA is called by end users from the browser via a frontend app (we use #RestController classes). On some calls, ServiceA has to call ServiceB (using RestTemplate). We've got authentication and authorization sorted out for our target environment, but for testing locally we are relying on Basic Auth instead, and that's where we're hitting a snag: we would like ServiceA to re-use the Basic Auth credentials the user provided when calling Service B.
Is there an easy way to pass the Basic Auth credentials used on the call to our REST controller to the RestTemplate call?

Quick and dirty solution
The easiest way to do this would be:
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
#RestController
class SomeController {
private final RestTemplate restTemplate = new RestTemplate();
#PostMapping("/delegate/call")
public void callOtherService(#RequestHeader(HttpHeaders.AUTHORIZATION) String authorization) {
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.AUTHORIZATION, authorization);
restTemplate.postForEntity("other-service.com/actual/call", new HttpEntity<Void>(null, headers), Void.class);
// handling the response etc...
}
}
Using interceptors and RestTemplateCustomizer
I didn't want to change to add an extra parameter on each controller method, and I wanted a way to enable or disable this behavior depending on the environment, so here is a slightly more complicated solution that can be enabled using Spring profiles, and doesn't touch the controllers:
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class BasicAuthPropagationInterceptor implements HandlerInterceptor, ClientHttpRequestInterceptor {
private final ThreadLocal<String> cachedHeader = new ThreadLocal<>();
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
final String header = request.getHeader(HttpHeaders.AUTHORIZATION);
cachedHeader.set(header);
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
cachedHeader.remove();
}
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
String ch = cachedHeader.get();
if (!request.getHeaders().containsKey(HttpHeaders.AUTHORIZATION) && ch != null) {
request.getHeaders().add(HttpHeaders.AUTHORIZATION, ch);
}
return execution.execute(request, body);
}
}
This stores the received header in a ThreadLocal and adds it with an interceptor for RestTemplate.
This can then be configured as such:
import org.springframework.boot.web.client.RestTemplateCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
#Configuration
#Profile("LOCAL")
class LocalConfiguration implements WebMvcConfigurer {
private final BasicAuthPropagationInterceptor basicAuthPropagationInterceptor
= new BasicAuthPropagationInterceptor();
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(basicAuthPropagationInterceptor);
}
#Bean
RestTemplateCustomizer restTemplateCustomizer() {
return restTemplate -> restTemplate.getInterceptors().add(basicAuthPropagationInterceptor);
}
}
RestTemplate obtained by using the default RestTemplateBuilder bean will then automatically set the Authorization HTTP header if it's available in the current thread.

Related

AuthenticationManager.authenticate method not getting called

I am trying to follow the API Key authentication code from this answer:
https://stackoverflow.com/a/48448901
I created my filter class:
package com.propfinancing.CADData.web;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.LoggerFactory;
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
public class PullAPIKeyFromHeaderFilter extends AbstractPreAuthenticatedProcessingFilter {
private String apiKeyHeaderName;
public PullAPIKeyFromHeaderFilter(String apiKeyHeaderName) {
this.apiKeyHeaderName = apiKeyHeaderName;
}
#Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
String headerValue = request.getHeader(apiKeyHeaderName);
return request.getHeader(headerValue);
}
#Override
protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
return apiKeyHeaderName;
}
}
And then I implemented my security configuration:
package com.propfinancing.CADData.web;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
#Configuration
#EnableWebSecurity
#Order(1)
public class APIKeySecurityConfig extends WebSecurityConfigurerAdapter {
#Value("${caddata.apiKey.header.name}")
private String apiKeyHeaderName;
#Value("${caddata.apiKey}")
private String apiKey;
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
PullAPIKeyFromHeaderFilter pullAPIKeyFromHeaderfilter = new PullAPIKeyFromHeaderFilter(apiKeyHeaderName);
AuthenticationManager authManager = new AuthenticationManager() {
#Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
String principal = (String) authentication.getPrincipal();
if (!apiKey.equals(principal))
throw new BadCredentialsException("Invalid API key");
authentication.setAuthenticated(true);
return authentication;
}
};
pullAPIKeyFromHeaderfilter.setAuthenticationManager(authManager);
httpSecurity.antMatcher("/**");
httpSecurity.addFilter(pullAPIKeyFromHeaderfilter);
httpSecurity.requiresChannel().anyRequest().requiresSecure();
httpSecurity.csrf().disable();
httpSecurity.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry urlAuthConfigurer = httpSecurity.authorizeRequests();
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.AuthorizedUrl authorizedUrl = urlAuthConfigurer.anyRequest();
authorizedUrl.authenticated();
}
}
When I do an external call to the application with the header as part of the request, I get a 403 Forbidden response.
I can see the filter pulling the key from the header. That part is working.
But, the authenticate() method is not being called to check if the header is valid.
I am not sure what I missed, the code looks the same to me.
Any ideas?
Looks like the wrong base class, per https://docs.spring.io/spring-security/site/docs/4.0.x/apidocs/org/springframework/security/web/authentication/preauth/AbstractPreAuthenticatedProcessingFilter.html :
The purpose is then only to extract the necessary information on the
principal from the incoming request, rather than to authenticate them.
Try extending https://docs.spring.io/spring-security/site/docs/4.0.x/apidocs/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.html instead.
I was not able to get the code above to work, but I changed it to use the second answer on the thread (Using a Filter) https://stackoverflow.com/a/63852212 It works as expected.

Spring Boot RestController DELETE request fails without .csrf().disable()

I have a Spring Boot Rest Service proof of concept.
I have this for my security: (obviously a poor real implmentation).
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
#Configuration
public class MySecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
/* not production grade quality */
httpSecurity.authorizeRequests().anyRequest().permitAll();
}
// #Override
// public void configure(WebSecurity web) throws Exception {
// web.debug(true);
// }
}
Using Postman:
All of my GETs were working fine. Then I added a DELETE request. and got
{
"timestamp": "blah blah blah",
"status": 403,
"error": "Forbidden",
"message": "Forbidden",
"path": "/v1/mything/1"
}
Postman setup: (not rocket science)
DELETE
http://localhost:8080/v1/mythings/1
So I added the ".csrf().disable()", and my DELETE works.
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
#Configuration
public class MySecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
/* not production grade quality */
httpSecurity.csrf().disable(); /* had to add this "Cross Site Request Forgery" disable for DELETE operations */
httpSecurity.authorizeRequests().anyRequest().permitAll();
}
// #Override
// public void configure(WebSecurity web) throws Exception {
// web.debug(true);
// }
}
But my question is WHY does .csrf().disable() .. allow DELETE requests? Seems somewhat unrelated.
Thanks.
My full rest controller below:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.inject.Inject;
import java.time.OffsetDateTime;
import java.util.Collection;
import java.util.Optional;
import java.util.Set;
#RestController
#RequestMapping("/v1")
public class MyThingController {
private final Logger logger;
private final IMyThingManager mythingManager;
/* The Inject annotation is the signal for which constructor to use for IoC when there are multiple constructors. Not needed in single constructor scenarios */
#Inject
public MyThingController(IMyThingManager mythingManager) {
this(LoggerFactory.getLogger(MyThingController.class), mythingManager);
}
public MyThingController(Logger lgr, IMyThingManager mythingManager) {
if (null == lgr) {
throw new IllegalArgumentException("Logger is null");
}
if (null == mythingManager) {
throw new IllegalArgumentException("IMyThingManager is null");
}
this.logger = lgr;
this.mythingManager = mythingManager;
}
#RequestMapping(value = "/mythings", method = RequestMethod.GET)
Collection<MyThingDto> getAllMyThings() {
Collection<MyThingDto> returnItems = this.mythingManager.getAll();
return returnItems;
}
#RequestMapping(method = RequestMethod.GET, value = "mythings/{mythingKey}")
ResponseEntity<MyThingDto> getMyThingById(#PathVariable Long mythingKey) {
this.logger.info(String.format("Method getMyThingById called. (mythingKey=\"%1s\")", mythingKey));
Optional<MyThingDto> foundItem = this.mythingManager.getSingle(mythingKey);
ResponseEntity<MyThingDto> responseEntity = new ResponseEntity<>(HttpStatus.NOT_FOUND);
if (foundItem.isPresent()) {
responseEntity = new ResponseEntity<>(foundItem.get(), HttpStatus.OK);
}
return responseEntity;
}
#RequestMapping(value = "mythings/{mythingKey}", method = RequestMethod.DELETE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Integer> deleteUser(#PathVariable("mythingKey") Long mythingKey) {
this.logger.info(String.format("Method deleteUser called. (mythingKey=\"%1s\")", mythingKey));
int rowCount = this.mythingManager.deleteByKey(mythingKey);
int rowCount = 1; /* use this to "fake it" */
ResponseEntity<Integer> responseEntity = new ResponseEntity<>(HttpStatus.NOT_FOUND);
if (rowCount > 0) {
responseEntity = new ResponseEntity<>(rowCount, HttpStatus.OK);
}
return responseEntity;
}
}
CSRF protection checks for a CSRF token on changing methods like POST, PUT, DELETE. And as a REST API is stateless you don't have a token in a cookie. That's why you have to disable it for REST APIs.
References
https://security.stackexchange.com/questions/166724/should-i-use-csrf-protection-on-rest-api-endpoints
Spring Security Reference: https://docs.spring.io/spring-security/site/docs/current/reference/html5/#csrf
Guide to CSRF Protection in Spring https://www.baeldung.com/spring-security-csrf
The guide to CSRF Protection says: "However, if our stateless API uses a session cookie authentication, we need to enable CSRF protection as we'll see next."
For this case the solution is not to disable csrf. Is there another possibility to use CSRF Protection with DELETE

How to pass request parameters as-is between REST service calls in a Spring Boot services application?

We are doing an architectural refactoring to convert a monolithic J2EE EJB application to Spring services. In order to do that I'm creating services by breaking the application against the joints of its domain. Currently, I have three of them and each calls another service via Rest.
In this project our ultimate purpose is transforming the application to microservices, but since cloud infrastructure isn't clear and probably won't be possible, we decided to make it this way and thought that since services using Rest, it will be easy to make the transform in future.
Does our approach makes sense? My question stems from this.
I send a request to UserService with a header parameter, userName from Postman.
GET http://localhost:8087/users/userId?userName=12345
UserService calls another service which calls another. Rest call order between services is this:
UserService ---REST--> CustomerService ---REST--> AlarmService
Since I'm doing the work of carrying the common request parameters like this right now, I need to set common header parameters in every method that making Rest requests by taking them from incoming request to outgoing request:
#RequestMapping(value="/users/userId", method = RequestMethod.GET)
public ResponseEntity<Long> getUserId(#RequestHeader("userName") String userName) {
...
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList
(MediaType.APPLICATION_JSON));
headers.set("userName", userName);
HttpEntity<String> entity = new HttpEntity<>("parameters", headers);
HttpEntity<Long> response =
restTemplate.exchange(CUSTOMER_REST_SERVICE_URI,
HttpMethod.GET, entity, Long.class);
...
}
UserService:
package com.xxx.userservice.impl;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.Collections;
import java.util.Map;
#RestController
public class UserController extends AbstractService{
Logger logger = Logger.getLogger(UserController.class.getName());
#Autowired
private RestTemplate restTemplate;
private final String CUSTOMER_REST_SERVICE_HOST = "http://localhost:8085";
private final String CUSTOMER_REST_SERVICE_URI = CUSTOMER_REST_SERVICE_HOST + "/customers/userId";
#RequestMapping(value="/users/userId", method = RequestMethod.GET)
public ResponseEntity<Long> getUserId(#RequestHeader("userName") String userName) {
logger.info(""user service is calling customer service..."");
try {
//do the internal customer service logic
//call other service.
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList
(MediaType.APPLICATION_JSON));
headers.set("userName", userName);
HttpEntity<String> entity = new HttpEntity<>("parameters", headers);
HttpEntity<Long> response =
restTemplate.exchange(CUSTOMER_REST_SERVICE_URI,
HttpMethod.GET, entity, Long.class);
return ResponseEntity.ok(response.getBody());
} catch (Exception e) {
logger.error("user service could not call customer service: ", e);
throw new RuntimeException(e);
}
finally {
logger.info("customer service called...");
}
}
}
CustomerService:
package com.xxxx.customerservice.impl;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.xxx.interf.CustomerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class CustomerController extends AbstractService{
private final String ALARM_REST_SERVICE_HOST = "http://localhost:8086";
private final String ALARM_REST_SERVICE_URI = ALARM_REST_SERVICE_HOST + "/alarms/maxAlarmCount";
#Autowired
private CustomerService customerService;
#Autowired
private RestTemplate restTemplate;
...
#GetMapping(path="/customers/userId", produces = "application/json")
public long getUserId(#RequestHeader(value="Accept") String acceptType) throws RemoteException {
//customer service internal logic.
customerService.getUserId();
//customer service calling alarm service.
return restTemplate.getForObject(ALARM_REST_SERVICE_URI, Long.class);
}
}
AlarmService:
package com.xxx.alarmservice.impl;
import com.xxx.interf.AlarmService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class PriceAlarmController extends AbstractService{
#Autowired
private AlarmService priceAlarmService;
#RequestMapping("/alarms/maxAlarmCount")
public long getMaxAlarmsPerUser() {
// alarm service internal logic.
return priceAlarmService.getMaxAlarmsPerUser();
}
}
I have tried these config and interceptor files but i can use them just for logging and can't transfer header parameters by using them. Probably because each service has them. And also, this interceptor only works in UserService which first uses RestTemplate to send request. Called service and first request which is coming from Postman doesn't work with it because they doesn't print any log message like UserService does.
CommonModule:
package com.xxx.common.config;
import com.xxx.common.util.HeaderRequestInterceptor;
import org.apache.cxf.common.util.CollectionUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.web.client.RestTemplate;
import java.util.ArrayList;
import java.util.List;
#Configuration
public class RestTemplateConfig {
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
List<ClientHttpRequestInterceptor> interceptors
= restTemplate.getInterceptors();
if (CollectionUtils.isEmpty(interceptors)) {
interceptors = new ArrayList<>();
}
interceptors.add(new HeaderRequestInterceptor());
restTemplate.setInterceptors(interceptors);
return restTemplate;
}
}
ClientHttpRequestInterceptor:
package com.xxx.common.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpRequest;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.StreamUtils;
import java.io.IOException;
import java.nio.charset.Charset;
public class HeaderRequestInterceptor implements ClientHttpRequestInterceptor {
private final Logger log = LoggerFactory.getLogger(this.getClass());
#Override
public ClientHttpResponse intercept(
HttpRequest request,
byte[] body,
ClientHttpRequestExecution execution) throws IOException
{
log.info("HeaderRequestInterceptor....");
logRequest(request, body);
request.getHeaders().set("Accept", MediaType.APPLICATION_JSON_VALUE);
ClientHttpResponse response = execution.execute(request, body);
logResponse(response);
return response;
}
private void logRequest(HttpRequest request, byte[] body) throws IOException
{
log.info("==========request begin=======================");
}
private void logResponse(ClientHttpResponse response) throws IOException
{
log.info("==========response begin=============");
}
}
How can I manage the passing of common header information like userName by using some kind of interceptors or other mechanism in single place?
In your HeaderRequestInterceptor's intercept method, you can access the current http request and its headers (userId in your case) in the following way:
#Override
public ClientHttpResponse intercept(HttpRequest request..
...
HttpServletRequest httpServletRequest = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
String userId = httpServletRequest.getHeader("userId");
request.getHeaders().set("userId", userId);

Intercept calls to RestController in Spring Boot

I've looked at similar problems:
how to intercept all requests in spring REST controllers?
spring boot adding http request interceptors
And still can't figure out the solution.
I have a Spring Boot app that runs a bunch of Rest Controllers and want to intercept calls and perform some business logic before the request is passed to the controllers. I have this:
My Application class for Sprint Boot:
package com.amazonaws.lambda.keefinty.application;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
#SpringBootApplication(scanBasePackages="com.amazonaws.lambda.keefinty.controllers")
public class Application extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
My Configuration class in same package a Application:
package com.amazonaws.lambda.keefinty.application;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import com.amazonaws.lambda.keefinty.interceptors.MyInterceptor;
#Configuration
public class MyConfiguration extends WebMvcConfigurerAdapter {
#Bean
public MyInterceptor getInterceptor() {
return new MyInterceptor();
}
#Override
public void addInterceptors (InterceptorRegistry registry) {
System.out.println("\n\nAdding interceptors\n\n");
registry.addInterceptor(getInterceptor()).addPathPatterns("/**");
}
}
And finally the interceptor class:
package com.amazonaws.lambda.keefinty.interceptors;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
#Component
public class MyInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.print("\\n\\nIn MyInterceptor.preHandle\\n\\n");
String token = request.getParameter("token");
if (StringUtils.isBlank(token)) {
throw new Exception("Invalid User Id or Password. Please try again.");
}
return true;
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception exception) throws Exception {
// TODO Auto-generated method stub
System.out.print("\n\nIn MyInterceptor.afterCompletion\\n\\n");
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// TODO Auto-generated method stub
System.out.print("\\n\\nIn MyInterceptor.postHandle\\n\\n");
}
}
The Configuration class never seems to get called to register the interceptors nor does MyInterceptor.
By declaring #SpringBootApplication(scanBasePackages="com.amazonaws.lambda.keefinty.controllers") only annotated components from com.amazonaws.lambda.keefinty.controllers package and it's sub packages will be discovered.
You MyConfiguration class is in com.amazonaws.lambda.keefinty.application package which is not part of the declared component scan package.
One way to resolve this is to remove scanBasePackages argument from your #SpringBootApplication declaration. This will allow MyConfiguration to be component scanned as by default the package in which #SpringBootApplication is declared is component scanned.

How to redirect to other site from onApplicationEvent() in spring boot application

I have a class:
#Component
public class AuthenticationSuccessListener implements ApplicationListener<InteractiveAuthenticationSuccessEvent>
and method which is void:
#Override
public void onApplicationEvent(InteractiveAuthenticationSuccessEvent arg0)
I would like to redirect to other site
if(daysBetweenLocalDates(LocalDate.now(), operator.getDateOfChangePassword()) >= 30)
{
System.out.println("###DEB forcing operator to change pass because date of change pass is: " + operator.getDateOfChangePassword());
ModelAndView mav = new ModelAndView();
mav.setViewName("redirect:/changePassword");
}
but I need return mav; and there is problem since onApplicationEvent is void. How can I redirect user to another website from that method?
As #Brian Clozel suggested Handler works great!
So I made a handler:
package local.vlex.security;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
#Component
public class VlexAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
response.sendRedirect("/changePassword");
}
}
and used it in WebSecurityConfig:
#Autowired
VlexAuthenticationSuccessHandler successHandler;
then:
.formLogin().loginPage("/login").permitAll().successHandler(successHandler)
Thank you #Brian Clozel!

Resources