Can't fix CORS error (angular + java spring) - java

I'm trying to request data from my backend through my frontend, but I'm getting the error:
Access to XMLHttpRequest at 'http://localhost:8081/api/transactions/' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I am able to get the data with postman, but not my frontend. I'm using angular and spring boot.
My application.java:
#EnableJpaRepositories
#EntityScan
#SpringBootApplication
public class KoalaTreeAccountingApplication {
public static void main(String[] args) {
SpringApplication.run(KoalaTreeAccountingApplication.class, args);
}
}
My security config:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest()
.permitAll()
.and().csrf().disable();
}
}
My service to make the http call in angular:
#Injectable({
providedIn: 'root'
})
export class TransactionService {
baseUrl = 'http://localhost:8081/api/';
transactionUrl = this.baseUrl + 'transactions/';
constructor(private http: HttpClient, private logger : Logger){ }
getAllTransactions() : Observable<Transaction[]> {
this.logger.log("Request all transactions");
return this.http.get<Transaction[]>(this.transactionUrl);
}
getTransactionById(id : number) : Observable<Transaction> {
this.logger.log("Request transaction " + id);
return this.http.get<Transaction>(this.transactionUrl + id);
}
}
Edit: I've tried
https://spring.io/guides/gs/rest-service-cors/
Spring Security CORS filter not working
Security configuration with Spring-boot
https://stackoverflow.com/a/31748398/12025088
Protip: clean install before re-running the application after a change. I'm an idiot.
Fixed by using this instead of SecurityConfig.java:
#Component
public class SimpleCORSFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
response.setHeader("Access-Control-Max-Age", "36000");
response.setHeader("Access-Control-Allow-Headers", "origin, content-type, accept");
chain.doFilter(req, res);
}
public void init(FilterConfig filterConfig) {
}
public void destroy() {
}
}

You need to configure CORS on the methods of your RestController that you want to allow it. CORS is a server response.
#CrossOrigin(origins = "http://localhost:4200")
#GetMapping("/")
public List<Transaction> findAllTransactions() {
return transactionService.findAllTransactions(); }
}

Related

How to disable or remove Allow response header from OPTIONS?

I have SecurityConfig class and I have added code to disable headers but I want to disable the 'Allow' response header. I have tried many different ways but no luck. How to add a custom header to disable?
#Configuration
#Slf4j
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception
{
http.csrf().disable()
.authorizeRequests().anyRequest().authenticated()
.and()
.headers().xssProtection().disable()
.and().headers().frameOptions().disable()
.and().headers().contentTypeOptions().disable()
.and().headers().disable()
.httpBasic();
}
}
Rest Controller
{
#RequestMapping(value = Constants.API_BASE_MAPPING + Constants.API_EVENT, method = RequestMethod.OPTIONS)
public ResponseEntity<?> publishEventMessage() {
return getResponseEntity();
}
private ResponseEntity<?> getResponseEntity() {
return ResponseEntity
.ok().contentType(MediaType.APPLICATION_JSON)
.allow() // want to remove this
.build();
}
}
Below is the response header from my OPTIONS API call
If you want to set an empty Allow Header response in a particular method in your controller, you can use:
return ResponseEntity
.ok().contentType(MediaType.APPLICATION_JSON)
.header("Allow", "")
.build();
Also, you can disable the OPTIONS http method for a certain path in your security configuration adding:
.antMatchers(HttpMethod.OPTIONS,"path/to/deny").denyAll()
You can't delete headers after being set. One possible solution is prevent that by creating a Filter which skips the setHeader.
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
chain.doFilter(request, new HttpServletResponseWrapper((HttpServletResponse) response) {
public void setHeader(String name, String value) {
if (!name.equalsIgnoreCase("Allow")) {
super.setHeader(name, value);
}
}
});
}
Based on this: https://stackoverflow.com/a/7895292/3713193
How to define a filter in Spring Boot: https://www.baeldung.com/spring-boot-add-filter

How do I handle CORS in Spring Boot Oauth2 Resource Server with password grant

Details:
I am using spring boot oauth2 resource server which is giving me CORS even after trying different approaches to filter this off.
How do my code look ?
Its a simple resource server with spring boot with spring-cloud-starter-oauth2 and spring-cloud-starter-security as two major dependencies.
I have used java annotations to make this a resource server :
#CrossOrigin(origins = "*", maxAge = 3600, allowedHeaders = "*")
#RestController
#RequestMapping("/api/v1")
#EnableResourceServer
Here is how I tried to resolve this :
I tried to add a custom filter which skips further filter calls with code below. After this I got "Authorization Header not allowed in preflight request on browser". After adding CORS everyehere extension to my browser my requests succeeded.
#EnableWebSecurity(debug = true)
#Order(Ordered.HIGHEST_PRECEDENCE)
public class WebSecurityConfig implements Filter {
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE, PATCH");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
response.setHeader("Access-Control-Expose-Headers", "Location");
System.out.println(request.getMethod());
System.out.println("-----------------");
if(!request.getMethod().equals("OPTIONS")) {
chain.doFilter(req, res);
}
}
#Override
public void init(FilterConfig filterConfig) {}
#Override
public void destroy() {}
}
I had the same problem and
that was the resolution.
public class ResourceServerCustom extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable().cors().disable().authorizeRequests().antMatchers("/oauth/token/**").permitAll()
.anyRequest().authenticated().and().exceptionHandling()
.authenticationEntryPoint(new AuthExceptionEntryPoint());
http.cors().configurationSource(request -> new CorsConfiguration().applyPermitDefaultValues());
}
}
And others configs.
public class WebSecurityCustom extends WebSecurityConfigurerAdapter {
public TokenStore tokenStore;
#Bean
#Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/v2/api-docs", "/configuration/ui", "/swagger-resources/**",
"/configuration/security", "/swagger-ui.html", "/webjars/**");
web.ignoring().antMatchers(HttpMethod.OPTIONS);
}
}
public class CorsFilterCustom extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods",
"ACL, CANCELUPLOAD, CHECKIN, CHECKOUT, COPY, DELETE, GET, HEAD, LOCK, MKCALENDAR, MKCOL, MOVE, OPTIONS, POST, PROPFIND, PROPPATCH, PUT, REPORT, SEARCH, UNCHECKOUT, UNLOCK, UPDATE, VERSION-CONTROL");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept, Key, Authorization");
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
chain.doFilter(request, response);
}
}
}
public class AuthorizationServerCustom implements AuthorizationServerConfigurer {
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore()).authenticationManager(authenticationManager);
}
}
public class AuthExceptionEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException arg2)
throws ServletException, IOException {
final Map<String, Object> mapBodyException = new HashMap<>();
mapBodyException.put("error", "Error from AuthenticationEntryPoint");
mapBodyException.put("message", "Message from AuthenticationEntryPoint");
mapBodyException.put("exception", "My stack trace exception");
mapBodyException.put("path", request.getServletPath());
mapBodyException.put("timestamp", (new Date()).getTime());
response.setContentType("application/json");
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
final ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(response.getOutputStream(), mapBodyException);
}
}
You could configure cors by adding a configuration class with different variations like this
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedMethods(Collections.singletonList("*"));
http.cors().configurationSource(request -> config);
}
}
or just disable like this
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().disable();
}
}

CORS support in spring boot and security

I have configured spring security to enable cors.
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
#Autowired
private CustomLogoutHandler logoutHandler;
#Autowired
private HttpLogoutSuccessHandler logoutSuccessHandler;
#Override
protected void configure( HttpSecurity http ) throws Exception
{
http.cors().and().csrf().disable()
.authorizeRequests()
.antMatchers("/rest/noauth/**").permitAll()
.antMatchers("/rest/login").permitAll()
.antMatchers("/rest/logout").permitAll()
.antMatchers("/static/**").permitAll()
.antMatchers("/ws/**").permitAll()
.antMatchers("/rest/razorpay/hook").permitAll()
.antMatchers("/rest/user/cc").permitAll()
.antMatchers("/v2/api-docs/**", "/configuration/ui/**", "/swagger-resources/**",
"/configuration/security/**", "/swagger-ui.html/**", "/webjars/**")
.permitAll()
.anyRequest().authenticated()
.and()
.logout().addLogoutHandler(logoutHandler).logoutSuccessHandler(logoutSuccessHandler)
.logoutUrl("/rest/logout")
.and()
.addFilterBefore(
new JWTAuthenticationFilter("/rest/login", tokenService(), refreshTokenService,
authTokenModelRepository, userService, userActivitiesRepository,
handlerExceptionResolver, bCryptPasswordEncoder, redisTemplate),
UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(new JWTAuthorizationFilter(authenticationManager(), authTokenModelRepository,
userSubscriptionRepository, handlerExceptionResolver, redisTemplate),
UsernamePasswordAuthenticationFilter.class);
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
public void configure( AuthenticationManagerBuilder auth ) throws Exception
{
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
}
and I have a rest controller,
#RestController
#RequestMapping( RestEndPoints.NO_AUTH_CONTROLLER )
#CrossOrigin(origins = "http://0.0.0.0:3000")
public class OtpController extends AbstractController {
#Autowired
private AuthService authService;
#ApiOperation( value = "Generate OTP to login using registered email address", response = UIResponse.class, notes = "Please do validate and send only the organisation email address" )
#ApiResponses( value = { #ApiResponse( code = 200, message = "Otp generated for <email_address>" ),
#ApiResponse( code = 400, message = "Email address not registered as CC", response = UIErrorMessage.class ),
#ApiResponse( code = 500, message = "Something went wrong", response = UIErrorMessage.class ) } )
#PostMapping( "/otp/{email_address:.+}" )
public ResponseEntity generateOtpToLogin( #PathVariable( "email_address" ) String emailAddress )
{
try
{
return buildResponse(authService.generateOtpForTheEmailAddress(emailAddress));
}
catch( DataException e )
{
return buildError(e);
}
}
}
But, when an API request is made from a frontend application to the POST method, the browser is making an OPTIONS call and that response headers as Access-Control-Allowed-Origin:* even though I am setting it to 0.0.0.0:3000 and the browser is getting the error,
Access to XMLHttpRequest at
'http://192.168.1.3:8090/rest/noauth/otp/sandesha#test.com' from
origin 'http://0.0.0.0:3000' has been blocked by CORS policy: The
value of the 'Access-Control-Allow-Origin' header in the response must
not be the wildcard '*' when the request's credentials mode is
'include'. The credentials mode of requests initiated by the
XMLHttpRequest is controlled by the withCredentials attribute.
How to resolve this issue?
I faced same issue earlier, I suppose it expect some other parameter also.
So that i place following code and it's working file for me.
package com.ecommerce.auth_service.service.jwt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class SimpleCORSFilter implements Filter {
private final Logger log = LoggerFactory.getLogger(SimpleCORSFilter.class);
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String check = "xsrf-token,X-Total-Results,Authorization";
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With, remember-me, Authorization");
response.setHeader("Access-Control-Expose-Headers",check);
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
log.debug("Request get method call status SC_OK.");
response.setStatus(HttpServletResponse.SC_OK);
} else {
log.debug("Request get method not work chain start.");
chain.doFilter(req, res);
}
}
#Override
public void init(FilterConfig filterConfig) {
}
#Override
public void destroy() {
}
}

Request to one API endpoint causes 500 response; other API endpoints work

I want to accept origin as http://192.168.1.35:4200 and I created the CORSFilter like this in the springboot:
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class CORSFilter implements Filter {
public CORSFilter() {
}
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest request = (HttpServletRequest) req;
response.setHeader("Access-Control-Allow-Origin", "http://192.168.1.35:4200");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS, PATCH, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "X-Requested-With, Authorization, X-Auth-Token, Origin, Content-Type, Accept, Auth_Token");
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
chain.doFilter(req, res);
}
}
#Override
public void init(FilterConfig filterConfig) {
}
#Override
public void destroy() {
}
}
And I created the request like this to call the spring rest api from angular:
getAllUsers(): void {
this.accessToken = JSON.parse(window.sessionStorage.getItem('token')).access_token;
this.httpClient.get<User[]>(`${BASE_URL}/fam-users/user?access_token=${this.accessToken}`, httpOptions).subscribe(data => {
this.dataChange.next(data);
},
(err: ApiError) => {
this.snackBar.openSnackBar(err.error.message, 'close', 'red-snackbar');
});
}
When I send request, It says that
Access to XMLHttpRequest at 'http://192.168.1.35:8080/fam-users/user?access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NTAwNDMwODMsInVzZXJfbmFtZSI6ImFkbWluIiwiYXV0aG9yaXRpZXMiOlsiRkFfQU1FTkQiLCJGQV9ORVciXSwianRpIjoiOWViMzZjNzAtOGUwOS00YzViLWI0OWQtNDNmZTRhOTkzNDgzIiwiY2xpZW50X2lkIjoiY2xpZW50Iiwic2NvcGUiOlsicmVhZCIsIndyaXRlIiwidHJ1c3QiXX0.IETZOJE8tIqNc249HmTcJHuZpZFY1TP4PLcbqUOF3qc' from origin 'http://192.168.1.35:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
But all other requests from 'http://192.168.1.35:4200' are working fine.
Can Someone guide me to solve this issue?
There is a #CrossOrigin annotation in spring boo, for example:
#CrossOrigin(origins = "http://localhost:9000")
#GetMapping("/greeting")
public Greeting greeting(#RequestParam(required=false, defaultValue="World") String name) {
System.out.println("==== in greeting ====");
return new Greeting(counter.incrementAndGet(), String.format(template, name));
}
for more please have a look

Spring-Boot-Jersey Setup CORS

I serve my front- and backend from two different servers. Now I am trying to get CORS working on the Spring-Boot-Jersey backend. I tried everything I could find on the internet but nothing seem to work or I am missing something.
My current setup uses a ContainerResponseFilter. I tried registering it automatically with #Provider and manually in the Jersey configuration.
ContainerResponseFilter
#Provider
public class CORSFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext request,
ContainerResponseContext response) throws IOException {
response.getHeaders().add("Access-Control-Allow-Origin", "*");
response.getHeaders().add("Access-Control-Allow-Headers",
"origin, content-type, accept, authorization");
response.getHeaders().add("Access-Control-Allow-Credentials", "true");
response.getHeaders().add("Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, OPTIONS, HEAD");
}
}
Maybe it is important but I also used Spring-Security-oauth2 by adding the #EnableOAuth2Sso annotation. Please tell me if you need more of my setup.
I fixed it this way,
First create a class
public class CORSResponseFilter implements ContainerResponseFilter {
public void filter(ContainerRequestContext requestContext,ContainerResponseContext responseContext)
throws IOException {
MultivaluedMap<String, Object> headers = responseContext.getHeaders();
headers.add("Access-Control-Allow-Origin", "*");
//headers.add("Access-Control-Allow-Origin", "http://abcd.org"); //allows CORS requests only coming from abcd.org
headers.add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
headers.add("Access-Control-Allow-Headers", "X-Requested-With, Content-Type, X-Codingpedia");
}
}
The filter must inherit from the ContainerResponseFilter interface and must be registered as a provider:
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
register(CORSResponseFilter.class);
//other registrations
}
}
Fixed it by using the CORSFilter displayed in https://spring.io/blog/2015/01/20/the-resource-server-angular-js-and-spring-security-part-iii
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
class CorsFilter implements Filter {
void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with");
response.setHeader("Access-Control-Max-Age", "3600");
if (request.getMethod()!='OPTIONS') {
chain.doFilter(req, res);
} else {
}
}
void init(FilterConfig filterConfig) {}
void destroy() {}
}
Not sure if the #Provider annotation is supported by Spring. Try replacing the #Provider annotation with Springs #Component and the CORSFilter should extend org.springframework.web.filter.OncePerRequestFilter. This is the Spring way of configuring Filters and this will work for any application server.
You can also configure CORS via the WebMvcConfigurerAdapter, which might be more compact:
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedHeaders("...") // add headers
.allowedMethods(".."); // add methods
}
};
}
Check out this guide!

Categories

Resources