Trouble connecting angular frontEnd with javaBackend? - java

I am trying to call the API in Backend but I have some error that I have not an idea from what is causing by.
The problem started after I config the spring security in the backend.
The call should activate Preflighted requests OPTION
In my backend file, I have
#Configuration
#EnableWebSecurity
public class SpringSecurityConfigurationBasicAuth extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS,"/**").permitAll()
.anyRequest().authenticated()
.and()
// .formLogin().and()
.httpBasic();
}
}
and in the frontend, I have this part of the code.
executeHelloWorldServiceWithPathVariable(name) {
const basicAuthHeaderString = this.createBasicAuthenticationHttpHeader();
const headers = new HttpHeaders({
Authorization: basicAuthHeaderString
});
return this.http.get<HelloWorldBean>(`http://localhost:8080/hello-world/path-variable/${name}`,
{headers});
}
createBasicAuthenticationHttpHeader() {
const username = 'start';
const password = 'end';
const basicAuthHeaderString = 'Basic ' + window.btoa(username + ':' + password);
return basicAuthHeaderString;
}
In the backend, I have already include
#CrossOrigin(origins = "http://localhost:4200")
but still, I am not able to call this API
in the console, I should get something like an OPTION method but in fact, I get those:
General
Request URL: http://localhost:8080/hello-world/path-variable/start
Referrer Policy: no-referrer-when-downgrade
Response Header
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Connection: keep-alive
Content-Length: 0
Date: Tue, 28 Jan 2020 11:11:49 GMT
Expires: 0
Keep-Alive: timeout=60
Pragma: no-cache
WWW-Authenticate: Basic realm="Realm"
WWW-Authenticate: Basic realm="Realm"
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
request head
Accept: application/json, text/plain, /
Accept-Encoding: gzip, deflate, br
Accept-Language: en,cs;q=0.9,en-US;q=0.8
Authorization: Basicc3RhcnQ6ZWVuZA==
Connection: keep-alive
Host: localhost:8080
Origin: http://localhost:4200
Referer: http://localhost:4200/welcome/start
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36
and in console, I see this error

Try to add cors policy in your security configuration:
#Configuration
#EnableWebSecurity
public class SpringSecurityConfigurationBasicAuth extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.cors();
http.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS,"/**").permitAll()
.anyRequest().authenticated()
.and()
// .formLogin().and()
.httpBasic();
}
}

to the class SpringSecurityConfigurationBasicAuth try to add this method
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://localhost:4200"));
configuration.setAllowedMethods(Arrays.asList("GET","POST"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}

Related

Spring Security Requests are 403

I am currently working on an application with spring, and I currently face the problem that all requests I do return the error 403 - Forbidden. It is not only [post,put,patch,delete], but also get. Also, I have csrf already disabled.
Here my SecurityConfig:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.anonymous().and()
.authorizeRequests()
.antMatchers("/signin").permitAll()
.antMatchers("/signup").permitAll()
.antMatchers("/rss/feed").permitAll()
.antMatchers("/article/{guid}").permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint).and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.csrf().disable().cors();
}
And here one of the endpoints which is not working properly:
#PreAuthorize("hasAnyRole('ADMIN', 'PUBLISHER', 'USER')")
#GetMapping("/users/current")
public User getCurrent(#RequestHeader Map<String, String> headers){
String token = headers.get("Authorization");
System.out.println("Current user request");
return userAuthService.getUserByUsername(jwtUtil.getUser(token).getUsername());
}
And yes, I know that csrf().disable() is dangerous, I disabled it for now to see if it is some problem with csrf.
Here my JwtAuthenticationFilter:
#Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
#Autowired
private JwtUtil jwtUtil;
#Autowired
private UserAuthService userAuthService;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String header = request.getHeader("Authorization");
if(header == null){
throw new NullPointerException("No headers");
}
if (!header.startsWith("Bearer")) {
throw new JwtTokenMissingException("No JWT token found in the request headers");
}
String token = header.substring(7);
// Optional - verification
jwtUtil.validateToken(token);
UserVo userVo = jwtUtil.getUser(token);
UserDetails userDetails = userAuthService.loadUserByUsername(userVo.getUsername());
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
if (SecurityContextHolder.getContext().getAuthentication() == null) {
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
filterChain.doFilter(request, response);
}
}
Edit: I enabled Security logging and got the following error on Spring console:
2022-04-04 18:22:26.611 DEBUG 9804 --- [nio-8080-exec-2] o.s.s.a.i.a.MethodSecurityInterceptor : Failed to authorize ReflectiveMethodInvocation: public at.brigot.kainblog.pojos.User at.brigot.kainblog.controller.AuthController.getCurrent(java.util.Map); target is of class [at.brigot.kainblog.controller.AuthController] with attributes [[authorize: 'hasAnyRole('ADMIN', 'PUBLISHER', 'USER')', filter: 'null', filterTarget: 'null']]
Also if needed, here the full request info I got from spring:
************************************************************
Request received for GET '/users/current':
org.apache.catalina.connector.RequestFacade#1d49a6ca
servletPath:/users/current
pathInfo:null
headers:
host: localhost:8080
connection: keep-alive
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="98", "Opera GX";v="84"
accept: application/json, text/plain, */*
authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJnb3RwZWQxNyIsInJvbGVzIjpbIkFETUlOIiwiVVNFUiJdLCJpYXQiOjE2NDkwODkyMDcsImV4cCI6MTY0OTA4OTM4N30.b5vg-azO433Ozk8GoiakQC-T2ULdFVsde6MrJhW8XpIhA5k5AtA_Q6i0vuCGATQV8RwteMzBc86CzKmuQ7kuYA
sec-ch-ua-mobile: ?0
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.109 Safari/537.36 OPR/84.0.4316.52
sec-ch-ua-platform: "Windows"
origin: http://localhost:3000
sec-fetch-site: same-site
sec-fetch-mode: cors
sec-fetch-dest: empty
referer: http://localhost:3000/
accept-encoding: gzip, deflate, br
accept-language: de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7
Security filter chain: [
WebAsyncManagerIntegrationFilter
SecurityContextPersistenceFilter
HeaderWriterFilter
CorsFilter
LogoutFilter
JwtAuthenticationFilter
RequestCacheAwareFilter
SecurityContextHolderAwareRequestFilter
AnonymousAuthenticationFilter
SessionManagementFilter
ExceptionTranslationFilter
FilterSecurityInterceptor
]
************************************************************

Why CorsConfigurationSource's is ignored, no Access-Control-Allow-Origin is present?

My goal is to add Access-Control-Allow-Origins in the header.
The Spring Security Reference's clearly said to
Set a POJO as #EnableWebSecurity's bean.
Override a configure(HttpSecurity http) method.
Create a CorsConfigurationSource's bean.
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) {
http
.cors(withDefaults())
.httpBasic(withDefaults());
}
#Bean
protected CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Collections.singletonList("http://localhost:3000"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
#RestController
public class AccountController {
#GetMapping("/ping")
public String getPing() {
return "PONG";
}
}
What I did:
Run a Spring Boot's application with the above configuration
Send a Fetch request to http://localhost:8080/ping from a React client.
The actual result:
The response does not include Access-Control-Allow-Origin header, I think it should be in the OPTIONS request.
The expected result:
A success response.
I am confused... I did a curl and the Access-Control-Allow-Origin is present. I think there is something wrong with the client side handling the HttpBasic.
curl -v -H "Access-Control-Request-Method: POST" -H "Origin: http://localhost:3000" -X OPTIONS http://localhost:8080/ping
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> OPTIONS /ping HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.55.1
> Accept: */*
> Access-Control-Request-Method: POST
> Origin: http://localhost:3000
>
< HTTP/1.1 200
< Vary: Origin
< Vary: Access-Control-Request-Method
< Vary: Access-Control-Request-Headers
< Access-Control-Allow-Origin: http://localhost:3000
< Access-Control-Allow-Methods: GET,POST
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Content-Length: 0
< Date: Fri, 26 Feb 2021 13:37:15 GMT
<
* Connection #0 to host localhost left intact

Validate an authToken

Got a websocket - see the authToken in the Cookie, in Java Spring how do you validate this authToken? I understand this authToken is passed down from the http layer to the websocket so I'm trying to validate that the websocket is being opened by our app and not by some other source.
Headers for Websocket:
GET ws://localhost:9999/somePath/websocket HTTP/1.1
Host: localhost:9999
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36
Upgrade: websocket
Origin: http://localhost
Sec-WebSocket-Version: 13
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Cookie: authToken=elFKMk5TckR0ZUNvdnZySUJxc2ZMdz09OklEZENrRFRySkp0U0ltVFdKU1RIZVE9PQ
Sec-WebSocket-Key: e//VDAjHSRjE810tCbIEyw==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Sec-WebSocket-Protocol: v10.stomp, v11.stomp, v12.stomp
I would like to validate that authToken in the HttpHandshakeInterceptor.beforeHandshake
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer
registry.addEndpoint(stompEndPoint).addInterceptors(new HttpHandshakeInterceptor()).setAllowedOrigins("*").withSockJS();
public class HttpHandshakeInterceptor implements HandshakeInterceptor
Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map attributes) throws Exception {
if (request instanceof ServletServerHttpRequest) {
ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
HttpSession session = servletRequest.getServletRequest().getSession();
attributes.put("sessionId", session.getId());
// validate token logic
}
return true;
}

Unauthorized request, 401, using Spring if I open a modal or I send an ajax request

I have a problem with Keycloak and Spring Boot..
I developed a web-app with some modal and ajax request, sometimes, and I don't know why, I receive status 401 if I click on an href to open a modal, or if I submit a form via ajax...
I don't see any error log server-side, but I checked the request and I have WWW-Authenticate: Bearer realm="Unknown". I think it is weird.
This is the entire request:
1. Request URL:
MyUrl
2. Request Method:
GET
3. Status Code:
401 Unauthorized
4. Remote Address:
MyIp
5. Referrer Policy:
no-referrer-when-downgrade
Response Headers
1. Cache-Control:
no-cache, no-store, max-age=0, must-revalidate
2. Connection:
Keep-Alive
3. Content-Language:
it
4. Content-Length:
302
5. Content-Type:
text/html;charset=ISO-8859-1
6. Date:
Tue, 08 May 2018 07:32:59 GMT
7. Expires:
0
8. Keep-Alive:
timeout=5, max=99
9. Pragma:
no-cache
10. Server:
Apache/2.4.18 (Ubuntu)
11. WWW-Authenticate:
Bearer realm="Unknown"
12. X-Content-Type-Options:
nosniff
13. X-Frame-Options:
DENY
14. X-XSS-Protection:
1; mode=block
Request Header
1. Accept:
text/html, */*; q=0.01
2. Accept-Encoding:
gzip, deflate
3. Accept-Language:
it-IT,it;q=0.9,en-US;q=0.8,en;q=0.7
4. Connection:
keep-alive
5. Cookie:
JSESSIONID=1F97669EEE8A347CAE145C8D25146512.Tomcat1; _ga=GA1.2.230482432.1486023475
6. DNT:
1
7. Host:
MyHost
8. Referer:
MyUrl
9. User-Agent:
Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36
10. X-Requested-With:
XMLHttpRequest
Query String Parameters
.....
This is my config about keycloak and Spring:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled=true)
#KeycloakConfiguration
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
#Autowired
public KeycloakClientRequestFactory keycloakClientRequestFactory;
public SecurityConfig() {
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http
.httpBasic()
.disable();
http
.authorizeRequests()
.anyRequest().hasAuthority("1086")
.and()
.logout()
.logoutUrl("/logout")
.logoutRequestMatcher(new AntPathRequestMatcher("/logout", "GET"))
.permitAll()
.logoutSuccessUrl(URL)
.invalidateHttpSession(true);
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(keycloakAuthenticationProvider());
}
#Bean
#Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
#Bean
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public KeycloakRestTemplate keycloakRestTemplate() {
return new KeycloakRestTemplate(keycloakClientRequestFactory);
}
#Bean
public KeycloakConfigResolver keycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
#Bean
public FilterRegistrationBean keycloakAuthenticationProcessingFilterRegistrationBean(KeycloakAuthenticationProcessingFilter filter) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
registrationBean.setEnabled(false);
return registrationBean;
}
#Bean
public FilterRegistrationBean keycloakPreAuthActionsFilterRegistrationBean(KeycloakPreAuthActionsFilter filter) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
registrationBean.setEnabled(false);
return registrationBean;
}
#Bean
public FilterRegistrationBean keycloakAuthenticatedActionsFilterBean(KeycloakAuthenticatedActionsFilter filter) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
registrationBean.setEnabled(false);
return registrationBean;
}
#Bean
public FilterRegistrationBean keycloakSecurityContextRequestFilterBean( KeycloakSecurityContextRequestFilter filter) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
registrationBean.setEnabled(false);
return registrationBean;
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/resources/**", "/static/**", "/css/**", "/js/**", "/images/**", "/webjars/**");
}
I can continue with the request, if I reload the page, and then I re-click on the href... But why I have to reload the page? and why sometimes?

ResponseEntity with HTTP Location header not causing redirect

A Spring MVC controller needs to redirect the control flow of an app to a different url endpoint within the same app. But the current code is returning a blank page along with response headers that include the intended destination url as the forward header. When the contents of the forward header is pasted into the web browser, the intended endpoint is successfully called. What specific changes need to be made to the code below in order for the POST controller to successfully redirect the control flow to the intended destination endpoint instead of returning a blank page?
Here is the code for the Controller method:
#RequestMapping(method = RequestMethod.POST)
#ResponseStatus(value = HttpStatus.OK)
public ResponseEntity<?> auth(FormData formData, HttpServletRequest req, HttpServletResponse resp) {
System.out.println("11111111111111 inside POST");
HttpHeaders responseHeaders = new HttpHeaders();
boolean passedTheTest = true;//ACTUAL LOGIC IS OMITTED HERE FOR SIMPLICITY
if (passedTheTest) {
//SOME OFF TOPIC LOGIC HERE IS OMITTED
CsrfToken csrf = (CsrfToken) req.getAttribute(CsrfToken.class.getName());
String updateCsrf = csrf.getToken();
responseHeaders.set("XSRF-TOKEN", updateCsrf);
if(resp.getHeaders("Cache-Control")!=null){responseHeaders.put("Cache-Control" , new ArrayList<String>(resp.getHeaders("Cache-Control")));}
if(resp.getHeader("Content-Language")!=null){responseHeaders.set("Content-Language" , resp.getHeader("Content-Language"));}
if(resp.getHeader("Content-Length")!=null){responseHeaders.set("Content-Length" , resp.getHeader("Content-Length"));}
if(resp.getHeader("Date")!=null){responseHeaders.set("Date" , resp.getHeader("Date"));}
if(resp.getHeader("Expires")!=null){responseHeaders.set("Expires" , resp.getHeader("Expires"));}
if(resp.getHeader("Pragma")!=null){responseHeaders.set("Pragma" , resp.getHeader("Pragma"));}
if(resp.getHeader("Server")!=null){responseHeaders.set("Server" , resp.getHeader("Server"));}
if(resp.getHeader("X-Application-Context")!=null){responseHeaders.set("X-Application-Context" , resp.getHeader("X-Application-Context"));}
if(resp.getHeader("X-Frame-Options")!=null){responseHeaders.set("X-Frame-Options" , resp.getHeader("X-Frame-Options"));}
if(resp.getHeader("X-XSS-Protection")!=null){responseHeaders.set("X-XSS-Protection" , resp.getHeader("X-XSS-Protection"));}
if(resp.getHeader("x-content-type-options")!=null){responseHeaders.set("x-content-type-options" , resp.getHeader("x-content-type-options"));}
if(req.getSession().getAttribute("forwardTo")!=null){
String redirectTo = getValidUriFromAnotherFunction();
try {
URI location = new URI(redirectTo);
responseHeaders.setLocation(location);
} catch (URISyntaxException e) {e.printStackTrace();}
ResponseEntity<Void> forwardResponseEntity = new ResponseEntity<Void>(responseHeaders, HttpStatus.CREATED);
return forwardResponseEntity;
}
};
return new ResponseEntity<String>("aDifferentViewTemplateName", responseHeaders, HttpStatus.CREATED);
}
The request headers in the browser's developer tools are:
Host: localhost:7777
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Firefox/38.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://localhost:7777/path/to/controller_method
Cookie: JSESSIONID=911B34457B69F7729091DD97A160AD79; JSESSIONID=95AA730306330CF15E3776C495807354; XSRF-TOKEN=04ae2a0c-3c58-4e85-88bd-3818bb10402a
Connection: keep-alive
The response headers for the same POST are:
Cache-Control: no-cache, no-store, max-age=0, must-revalidate, no-cache, no-store, max-age=0, must-revalidate
Content-Length: 0
Date: Sun, 29 May 2016 21:48:24 GMT
Expires: 0, 0
Location: http://localhost:7777/path/to/forward_destination?long_querystring
Pragma: no-cache, no-cache
Server: Apache-Coyote/1.1
X-Application-Context: application:7777, application:7777
X-Content-Type-Options: nosniff, nosniff
X-Frame-Options: DENY, DENY
X-XSS-Protection: 1; mode=block, 1; mode=block
XSRF-TOKEN: 04ae2a0c-3c58-4e85-88bd-3818bb10402a
The Spring Boot debug log for the same POST includes three sections, which have been separated as follows for improved readability:
Section of debug log that shows the SYSO inside the controller:
11111111111111 inside POST
redirectTo is: http://localhost:7777/path/to/forward_destination?long_querystring
Section of debug log AFTER the controller (most important?):
2016-05-29 14:48:24.489 DEBUG 5533 --- [io-7777-exec-10] o.s.s.w.a.ExceptionTranslationFilter : Chain processed normally
2016-05-29 14:48:24.489 DEBUG 5533 --- [io-7777-exec-10] w.c.HttpSessionSecurityContextRepository : SecurityContext 'org.springframework.security.core.context.SecurityContextImpl#42259e42: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken#42259e42: Principal: org.springframework.security.core.userdetails.User#40fecce: Username: SomeUser; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ONE,ROLE_TWO; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#fffe3f86: RemoteIpAddress: 127.0.0.1; SessionId: 02A95844E8A829868542290D471503F5; Granted Authorities: ROLE_ONE, ROLE_TWO, ROLE_THREE' stored to HttpSession: 'org.apache.catalina.session.StandardSessionFacade#64307ead
2016-05-29 14:48:24.489 DEBUG 5533 --- [io-7777-exec-10] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
Instead of returning the 201 Created status code you should return a 3XX status code to ask the user agent to load a different web page. Otherwise the Location header has no "special" meaning.
So for example you can write:
ResponseEntity<Void> forwardResponseEntity = new ResponseEntity<Void>(responseHeaders, HttpStatus.MOVED_PERMANENTLY);

Categories