I have problem with #DeleteMapping.
Situation is like below.
If I request to /v1/cache/{cacheEntry} with method DELETE,
It respond with 404, but body was empty. no message, no spring default json 404 response message.
If i request to /v1/cache/{cacheEntry} with method POST,
It respond with 405 and body was below. (This action is correct, not a bug.)
If I change #DeleteMapping to #PostMapping, and request /v1/cache/{cacheEntry} with method POST, It respond success with code 200.
{
"timestamp": 1643348039913,
"status": 405,
"error": "Method Not Allowed",
"message": "",
"path": "/v1/cache/{cacheEntry}"
}
// Controller
#Slf4j
#RestController
#RequestMapping("/v1/cache")
#RequiredArgsConstructor
public class CacheController {
private final CacheService cacheService;
#PostMapping("/{cacheEntry}")
public CacheClearResponse clearCacheEntry(#PathVariable("cacheEntry") CacheChannels cacheEntry) {
try {
log.info("Cache entry :: " + cacheEntry);
cacheService.evictCacheEntry(cacheEntry);
return CacheClearResponse.builder()
.result(
RequestResult.builder()
.code(9200)
.message("SUCCESS")
.build()
)
.common(
Common.builder().build()
)
.date(LocalDateTime.now())
.build();
} catch (Exception e) {
e.printStackTrace();
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
return CacheClearResponse.builder()
.result(
RequestResult.builder()
.code(9999)
.message(sw.toString())
.build()
)
.common(
Common.builder().build()
)
.date(LocalDateTime.now())
.build();
}
}
}
}
// CacheService
#Service
#RequiredArgsConstructor
public class CacheService {
private final CacheManager cacheManager;
public void evictCacheEntry(CacheChannels cacheEntry) {
Cache cache = cacheManager.getCache(cacheEntry.getCacheName());
if (cache != null) {
cache.clear();
}
}
public void evictCache(CacheChannels cacheEntry, String cacheKey) {
Cache cache = cacheManager.getCache(cacheEntry.getCacheName());
if (cache != null) {
cache.evict(cacheKey);
}
}
}
// Enum
#Getter
#AllArgsConstructor
public enum CacheChannels {
CACHE_TEN_MIN(Names.CACHE_TEN_MIN, Duration.ofMinutes(10)),
CACHE_HALF_HR(Names.CACHE_HALF_HR, Duration.ofMinutes(30)),
CACHE_ONE_HR(Names.CACHE_ONE_HR, Duration.ofHours(1)),
CACHE_THREE_HR(Names.CACHE_THREE_HR, Duration.ofHours(3)),
CACHE_SIX_HR(Names.CACHE_SIX_HR, Duration.ofHours(6)),
CACHE_ONE_DAY(Names.CACHE_ONE_DAY, Duration.ofDays(1));
private final String cacheName;
private final Duration cacheTTL;
public static CacheChannels from(String value) {
return Arrays.stream(values())
.filter(cacheChannel -> cacheChannel.cacheName.equalsIgnoreCase(value))
.findAny()
.orElse(null);
}
public static class Names {
public static final String CACHE_TEN_MIN = "cache10Minutes";
public static final String CACHE_HALF_HR = "cache30Minutes";
public static final String CACHE_ONE_HR = "cache1Hour";
public static final String CACHE_THREE_HR = "cache3Hours";
public static final String CACHE_SIX_HR = "cache6Hours";
public static final String CACHE_ONE_DAY = "cache1Day";
}
}
// Converter
#Slf4j
public class StringToCacheChannelConverter implements Converter<String, CacheChannels> {
#Override
public CacheChannels convert(String source) {
log.info("Convert Target: " + source);
return CacheChannels.from(source);
}
}
// Security Config
#Configuration
#EnableWebSecurity
#Order(1)
public class APISecurityConfig extends WebSecurityConfigurerAdapter {
#Value("${spring.security.auth-token-header-name:Authorization}")
private String apiKeyHeader;
#Value("${spring.security.secret}")
private String privateApiKey;
#Override
protected void configure(HttpSecurity http) throws Exception {
APIKeyAuthFilter filter = new APIKeyAuthFilter(apiKeyHeader);
filter.setAuthenticationManager(new AuthenticationManager() {
#Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
String requestedApiKey = (String) authentication.getPrincipal();
if (!privateApiKey.equals(requestedApiKey)) {
throw new BadCredentialsException("The API Key was not found or not the expected value");
}
authentication.setAuthenticated(true);
return authentication;
}
});
http
.csrf().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilter(filter)
.authorizeRequests()
.antMatchers("/v1/cache/**")
.authenticated();
}
}
// Filter
#Slf4j
public class APIKeyAuthFilter extends AbstractPreAuthenticatedProcessingFilter {
private String apiKeyHeader;
public APIKeyAuthFilter(String apiKeyHeader) {
this.apiKeyHeader = apiKeyHeader;
}
#Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest httpServletRequest) {
log.info("Check authenticated.");
return httpServletRequest.getHeader(apiKeyHeader);
}
#Override
protected Object getPreAuthenticatedCredentials(HttpServletRequest httpServletRequest) {
return "N/A";
}
}
// Web Config
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StringToCacheChannelConverter());
}
#Bean
public HiddenHttpMethodFilter hiddenHttpMethodFilter() {
return new HiddenHttpMethodFilter();
}
}
This can be expected the controller was loaded, endpoint was mapped.
I tried change #DeleteMapping to #PostMapping and it was successfully respond against to POST request.
What am I missing?
I found reason why received 404 without any messages.
My tomcat is on remote server. It configured with security-constraint and disable DELETE method for all enpoints.
I just comment out it and It work properly with delete method.
Related
I've a springboot/openapi application. No dependency on spring security.
When launching a POST request via swagger, the returned status is 403.
The request doesn't arrive in the controller class.
A Get request however does work and returns a status 200.
The following is configured
#Configuration
public class Config {
#Bean
ForwardedHeaderFilter forwardedHeaderFilter() {
return new ForwardedHeaderFilter();
}
}
}
application.yaml
server:
port: 50086
forward-headers-strategy: framework
use-forward-headers: true
What could be the cause of the status 403 ?
Controller
#CrossOrigin
#RestController
#RequestMapping("/ta")
public class TaController {
#Operation(summary = "Calculate")
#RequestMapping(value = "/calculateWithPrices", method = RequestMethod.POST)
public ResponseEntity<CaculationResponseDto> calculateWithPrices(#RequestBody CaculationWithPricesRequestDto caculationWithPricesRequestDto) {
// code ...
}
Try to add a SecurityConfig which inherits from WebSecurityConfigurerAdapter. Example is here.
With the method configure you can set the access to specific url-endpoints and allow the call on them.
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomAuthenticationProvider authProvider;
#Autowired
public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider).eraseCredentials(false);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().and().authorizeRequests().antMatchers("**apiEndpoint**").authenticated()
.and().csrf().disable().headers().frameOptions().disable().and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// Deactivate authorization for whole application
// http.authorizeHttpRequests().antMatchers("/**").permitAll().and().csrf().disable();
}
}
Class CustomAuthenticationProvider:
#Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Autowired
private ICustomerRepository customerRepository;
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String id = authentication.getName().toString();
String pin = authentication.getCredentials().toString();
try {
// Check if the customer passed in Username exists
CustomerDTO customer = customerRepository.findById(Long.parseLong(id)).orElseThrow();
} catch (Exception e) {
// TODO Auto-generated catch block
throw new BadCredentialsException(id);
}
Collection<? extends GrantedAuthority> authorities = Collections
.singleton(new SimpleGrantedAuthority("ROLE_CUSTOMER"));
return new UsernamePasswordAuthenticationToken(id, pin, authorities);
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
I am trying to obtain the currently authenticated user in the controller for websockets. The problem is, I cannot access the user using SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getId().
I have tried to give Principal as a parameter to the method but it returns principal null.
Security configuration:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/connect").setAllowedOrigins("*");
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic/messages");
registry.setApplicationDestinationPrefixes("/ws");
}
}
Controller for websocket:
#Controller
public class MessageController {
#Autowired
private Consumer consumer;
#Autowired
private Utils utils;
#Autowired
private PersonService personService;
#Autowired
SimpMessagingTemplate simpMessagingTemplate;
String destination = "/topic/messages";
ExecutorService executorService =
Executors.newFixedThreadPool(1);
Future<?> submittedTask;
#MessageMapping("/start")
public void startTask(Principal principal){
// Here, I would like to get the logged in user
// If I use principal like this: principal.getName() => NullPointerException
if ( submittedTask != null ){
simpMessagingTemplate.convertAndSend(destination,
"Task already started");
return;
}
simpMessagingTemplate.convertAndSendToUser(sha.getUser().getName(), destination,
"Started task");
submittedTask = executorService.submit(() -> {
while(true){
simpMessagingTemplate.convertAndSend(destination,
// "The calculated value " + val + " is equal to : " + max);
}
});
}
How can I get the authenticated user? I needed it to check when to start the task for the web socket
Try to implement ChannelInterceptor, that need to be registrated in Config file (class that implements WebSocketMessageBrokerConfigurer)
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
private final ChannelInterceptor serverPushInBoundInterceptor;
#Autowired
public WebSocketConfig(#Qualifier("serverPushInBoundInterceptor") ChannelInterceptor serverPushInBoundInterceptor) {
this.serverPushInBoundInterceptor = serverPushInBoundInterceptor;
}
....
#Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(serverPushInBoundInterceptor);
}
}
#Component("serverPushInBoundInterceptor")
public class ServerPushInBoundInterceptor implements ChannelInterceptor {
private static final Logger log = LoggerFactory.getLogger(ServerPushInBoundInterceptor.class);
#Override
#SuppressWarnings("NullableProblems")
public Message<?> postReceive(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
if (StompCommand.CONNECT.equals(Objects.requireNonNull(accessor).getCommand())) {
List<String> authorization = accessor.getNativeHeader("Authorization");
if (authorization != null && !authorization.isEmpty()) {
String auth = authorization.get(0).split(" ")[1];
System.out.println(auth);
try {
// find Principal
Principal principal = ...
accessor.setUser(new UsernamePasswordAuthenticationToken(principal, principal.getCredentials(), principal.getAuthorities()));
} catch (Exception exc) {
log.error("preSend", exc);
}
}
}
return message;
}
}
I'm updating an old application to use WebFlux but I've gotten a bit lost when it comes to handling JWT validation with Spring Security.
The existing code (which works with standard Spring Web) looks like:
(Validating a Firebase Token)
public class FirebaseAuthenticationTokenFilter extends AbstractAuthenticationProcessingFilter {
private static final String TOKEN_HEADER = "X-Firebase-Auth";
public FirebaseAuthenticationTokenFilter() {
super("/v1/**");
}
#Override
public Authentication attemptAuthentication(
final HttpServletRequest request, final HttpServletResponse response) {
for (final Enumeration<?> e = request.getHeaderNames(); e.hasMoreElements(); ) {
final String nextHeaderName = (String) e.nextElement();
final String headerValue = request.getHeader(nextHeaderName);
}
final String authToken = request.getHeader(TOKEN_HEADER);
if (Strings.isNullOrEmpty(authToken)) {
throw new RuntimeException("Invaild auth token");
}
return getAuthenticationManager().authenticate(new FirebaseAuthenticationToken(authToken));
}
However when switching to WebFlux we lose HttpServletRequest and HttpServletResponse. There is a GitHub issue which suggests there is an alternative method/fix https://github.com/spring-projects/spring-security/issues/5328 however following it through I'm not able to identify what was actually changed to make this work.
The Spring Security docs while great, don't really explain how to handle the use-case.
Any tips on how to proceed?
Got there in the end:
First need to update the filter chain with a custom filter just like before
#Configuration
public class SecurityConfig {
private final FirebaseAuth firebaseAuth;
public SecurityConfig(final FirebaseAuth firebaseAuth) {
this.firebaseAuth = firebaseAuth;
}
#Bean
public SecurityWebFilterChain springSecurityFilterChain(final ServerHttpSecurity http) {
http.authorizeExchange()
.and()
.authorizeExchange()
.pathMatchers("/v1/**")
.authenticated()
.and()
.addFilterAt(firebaseAuthenticationFilter(), SecurityWebFiltersOrder.AUTHENTICATION)
.csrf()
.disable();
return http.build();
}
private AuthenticationWebFilter firebaseAuthenticationFilter() {
final AuthenticationWebFilter webFilter =
new AuthenticationWebFilter(new BearerTokenReactiveAuthenticationManager());
webFilter.setServerAuthenticationConverter(new FirebaseAuthenticationConverter(firebaseAuth));
webFilter.setRequiresAuthenticationMatcher(ServerWebExchangeMatchers.pathMatchers("/v1/**"));
return webFilter;
}
}
The main workhorse of the process is FirebaseAuthenticationConverter where I validate the incoming JWT against Firebase, and perform some standard logic against it.
#Slf4j
#Component
#RequiredArgsConstructor
public class FirebaseAuthenticationConverter implements ServerAuthenticationConverter {
private static final String BEARER = "Bearer ";
private static final Predicate<String> matchBearerLength =
authValue -> authValue.length() > BEARER.length();
private static final Function<String, Mono<String>> isolateBearerValue =
authValue -> Mono.justOrEmpty(authValue.substring(BEARER.length()));
private final FirebaseAuth firebaseAuth;
private Mono<FirebaseToken> verifyToken(final String unverifiedToken) {
try {
final ApiFuture<FirebaseToken> task = firebaseAuth.verifyIdTokenAsync(unverifiedToken);
return Mono.justOrEmpty(task.get());
} catch (final Exception e) {
throw new SessionAuthenticationException(e.getMessage());
}
}
private Mono<FirebaseUserDetails> buildUserDetails(final FirebaseToken firebaseToken) {
return Mono.just(
FirebaseUserDetails.builder()
.email(firebaseToken.getEmail())
.picture(firebaseToken.getPicture())
.userId(firebaseToken.getUid())
.username(firebaseToken.getName())
.build());
}
private Mono<Authentication> create(final FirebaseUserDetails userDetails) {
return Mono.justOrEmpty(
new UsernamePasswordAuthenticationToken(
userDetails.getEmail(), null, userDetails.getAuthorities()));
}
#Override
public Mono<Authentication> convert(final ServerWebExchange exchange) {
return Mono.justOrEmpty(exchange)
.flatMap(AuthorizationHeaderPayload::extract)
.filter(matchBearerLength)
.flatMap(isolateBearerValue)
.flatMap(this::verifyToken)
.flatMap(this::buildUserDetails)
.flatMap(this::create);
}
}
To the previous answer there could be added that this method also works fine:
private Mono<FirebaseToken> verifyToken(final String unverifiedToken) {
try {
return Mono.just(FirebaseAuth.getInstance().verifyIdToken(unverifiedToken));
} catch (final Exception e) {
throw new SessionAuthenticationException(e.getMessage());
}
}
And this one does not provid warnings regarding unnecessary use of blocking methods (like get())
I am unable to extract user info from the access token generated by keycloak. I have a protected route where I am expecting Principal or Authentication objects to be populated correctly.
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
private final String SIGNING_KEY = "MIIBCgKCAQ...AB";
#Override
public void configure(HttpSecurity http) throws Exception {
http
.requestMatcher(new RequestHeaderRequestMatcher("Authorization"))
.authorizeRequests()
.antMatchers("/api/register").anonymous()
.antMatchers("/api/token").anonymous()
.anyRequest().authenticated();
}
#Override
public void configure(ResourceServerSecurityConfigurer config) {
config.tokenServices(createTokenServices());
}
#Bean
#Primary
public DefaultTokenServices createTokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(createTokenStore());
return defaultTokenServices;
}
#Bean
public TokenStore createTokenStore() {
return new JwtTokenStore(createJwtAccessTokenConverter());
}
#Bean
public JwtAccessTokenConverter createJwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setAccessTokenConverter(new JwtConverter());
// converter.setSigningKey(SIGNING_KEY);
return converter;
}
public static class JwtConverter extends DefaultAccessTokenConverter implements JwtAccessTokenConverterConfigurer {
#Override
public void configure(JwtAccessTokenConverter converter) {
converter.setAccessTokenConverter(this);
}
#Override
public OAuth2Authentication extractAuthentication(Map<String, ?> map) {
OAuth2Authentication auth = super.extractAuthentication(map);
auth.setDetails(map); //this will get spring to copy JWT content into Authentication
return auth;
}
}
}
In Profile controller, I would like to display the details from the token passed in.
#GetMapping("/api/profile/details")
public Object details(final Principal authentication) {
// Object details = authentication.getDetails();
// if (details instanceof OAuth2AuthenticationDetails) {
// OAuth2AuthenticationDetails oAuth2AuthenticationDetails = (OAuth2AuthenticationDetails) details;
// Map<String, Object> decodedDetails = (Map<String, Object>) oAuth2AuthenticationDetails.getDecodedDetails();
//
// return decodedDetails;
// }
return authentication.getName();
}
I get the following error when I call the above endpooint.
{
"error": "invalid_token",
"error_description": "Cannot convert access token to JSON"
}
Whether I pass in the signing key or not it doesn't make a difference.
Thanks for helping.
We are using spring boot for our application. I'm trying to return the localized result in the response when the Rest service is called based on the Accept-Language header. For example if the Accept-Language header is : zh,ja;q=0.8,en. So the response will be in chinese since we support that.
But if Accept-Language header is : zh1,ja;q=0.8,en. Then I get Internal server error like below, because it can't invoke #ExceptionHandler i do not get response i like. Below is what I get
{
"timestamp": 1462213062585,
"status": 500,
"error": "Internal Server Error",
"exception": "java.lang.IllegalArgumentException",
"message": "java.lang.IllegalArgumentException: range=zh1",
"path": "/user/v1//paymentmethods/creditdebitcards"
}
Instead this is what I want to throw because for all other exceptions we handle and throw a similar response.
{
"operation": {
"result": "ERROR",
"errors": [
{
"code": "1000",
"message": "An unidentified exception has occurred.",
"field": ""
}
],
"requestTimeStampUtc": "2016-05-02T18:22:03.356Z",
"responseTimeStampUtc": "2016-05-02T18:22:03.359Z"
}
}
Below are my classes, if the header is wrong (like zh1,ja;q=0.8,en) then the parse method below throws 500 error like above.
public class SmartLocaleResolver extends AcceptHeaderLocaleResolver {
#Autowired
ExceptionHandling exceptionHandling;
#Autowired
MessageHandler messageHandler;
#Override
public Locale resolveLocale(HttpServletRequest request) {
try {
List<LanguageRange> list = Locale.LanguageRange.parse(request.getHeader("Accept-Language"));
if (!list.isEmpty()) {
for (LanguageRange s : list) {
if (ApplicationConstants.LOCALE.contains(s.getRange())) {
return Locale.forLanguageTag(s.getRange());
}
}
}
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException(e);
}
return request.getLocale();
}
Below is the ExceptionHandler class
#EnableWebMvc
#ControllerAdvice
public class ExceptionHandling extends ResponseEntityExceptionHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionHandling.class);
#Autowired
private MessageHandler messageHandler;
#ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
#ExceptionHandler(value = { UnsupportedMediaTypeException.class, InvalidMediaTypeException.class })
public void unsupportedMediaTypeException() {
}
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
#ExceptionHandler(value = Exception.class)
public #ResponseBody OperationsErrorBean handleglobalException(final HttpServletRequest request,
final Exception ex) {
LOGGER.error("Unhandled Exception Occurred: ", ex);
return errorResponse("1000", messageHandler.localizeErrorMessage("error.1000"), "", request.getRequestURI(),
request.getAttribute("startTime").toString());
}
}
This is my ApplicationConfig.java class
#Configuration
#ComponentScan("com.hsf")
#EnableWebMvc
public class ApplicationConfig extends WebMvcConfigurerAdapter {
#Value("${spring.application.name}")
String appName;
#Bean
public AlwaysSampler defaultSampler() {
return new AlwaysSampler();
}
#Override
public void addInterceptors(final InterceptorRegistry registry) {
if (StringUtils.isNotBlank(appName)) {
MDC.put("AppName", appName);
} else {
MDC.put("AppName", "APPNAME_MISSING");
}
registry.addInterceptor(new RequestInterceptor()).addPathPatterns("/user/v1/**");
}
#Bean
public LocaleResolver localeResolver() {
return new SmartLocaleResolver();
}
#Bean
public DispatcherServlet dispatcherServlet() {
final DispatcherServlet servlet = new DispatcherServlet();
servlet.setDispatchOptionsRequest(true);
return servlet;
}
#Bean
public MessageSource messageSource() {
final ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasenames("classpath:i18n/messages");
// If true, the key of the message will be displayed if the key is not
// found, instead of throwing an exception
messageSource.setUseCodeAsDefaultMessage(true);
messageSource.setDefaultEncoding("UTF-8");
// The value 0 means always reload the messages to be developer friendly
messageSource.setCacheSeconds(10);
return messageSource;
}
}
The #ExceptionHandler annotation for the unsupportedMediaTypeException method does not contain IllegalArgumentException, instead of:
#ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
#ExceptionHandler(value = { UnsupportedMediaTypeException.class,
InvalidMediaTypeException.class })
public void unsupportedMediaTypeException() { }
it should be:
#ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
#ExceptionHandler(value = { UnsupportedMediaTypeException.class,
InvalidMediaTypeException.class, IllegalArgumentException.class })
public void unsupportedMediaTypeException() { }
Also, since it seems handling of numerous languages is one of requirements of your application I suggest to create a dedicated RuntimeException for this situation InvalidAcceptLanguageException instead of using a generic IllegalArgumentException for this purpose.
I have the Accept-Language check in the interceptor and I throw a custom exception I created when there is an exception parsing the header. So I throw a 400 Bad request with a proper response I want to display.
public class RequestInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
final String startTime = DateUtils.getUTCDate();
request.setAttribute("startTime", startTime);
**try {
Locale.LanguageRange.parse(request.getHeader("Accept-Language"));
} catch (IllegalArgumentException e) {
throw new InvalidAcceptLanguageException();
}**
return true;
}
}
I have added a method in my ExceptionHandling class to throw InvalidAcceptLanguageException.
#EnableWebMvc
#ControllerAdvice
public class ExceptionHandling extends ResponseEntityExceptionHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionHandling.class);
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ExceptionHandler(value = InvalidAcceptLanguageException.class)
#ResponseBody
public OperationsErrorBean invalidAcceptLanguageException(final HttpServletRequest request, final Exception ex) {
return errorResponse("N/A", "Accept-Language is not in correct format", "", request.getRequestURI(),
request.getAttribute("startTime").toString());
}
}