How to solve multiple CORS origin error in Springboot - spring-boot

I am using Springboot as Backend which is running on tomcat server in Linode. The front end is a React.js webapp running in localhost. While some rest calls are getting fetched without issue some rest calls throws CORS errors. How to solve this.
My Config file:
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception
{
httpSecurity.cors().and().csrf().disable()
.authorizeRequests()
.antMatchers("/api/auth/public/**","/recordings/**", "/api/public/**", "/api/location/**","/version/**","/topic/**","/zoom/meetings/all", "/meetings/**","/zoom/getDetails/**","/getDetails","/zoom/**").permitAll()
.requestMatchers(CorsUtils::isPreFlightRequest,CorsUtils::isCorsRequest).permitAll()
.antMatchers("/zoom/**").hasAuthority("student")
.anyRequest().authenticated().and()
.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPointConfiguration).and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
/*Add a filter to validate the tokens with every request*/
httpSecurity.addFilterBefore(jwtRequestFilterConfiguration, UsernamePasswordAuthenticationFilter.class);
}
My Controller that throws CORS error:
#RestController
#RequestMapping("/zoom/")
#CrossOrigin("*")
public class zoomWebhookController extends ZoomService{
#Autowired
zoomWebhookService service;
#PostMapping("meetings/user")
public ResponseEntity<?> getMeeting () {
return service.getMeeting();
}
#GetMapping("recordings/user")
public ResponseEntity<?> getZoomRecordedMeetingsOfUser(#PageableDefault(page = 0, size = 10, sort = "createdOn", direction = Sort.Direction.DESC) Pageable pageableFromRequest){
return getAllListOfRecordingsOfUser(pageableFromRequest);
}
}
The call without CORS error:
#RestController
#RequestMapping("/api/public/achievers/")
public class AchieversController {
private static final Logger log = LoggerFactory.getLogger(AchieversController.class);
#Autowired
private AchieversRepository achieversRepository;
#Autowired
private AchieverTypeDateRepository achieverTypeDateRepository;
#GetMapping("get/list")
protected ResponseEntity<Object> getAchieversTypeAndYear(){
try {
log.info("Inside AchieversController.getAchieversTypeAndYear()");
List<AchieverTypeDateModel> achieverTypeDateModelList = achieverTypeDateRepository.findAll();
return ResponseEntity.ok().body(new ResponseEntity<Object>(achieverTypeDateModelList, HttpStatus.OK));
}catch (Exception e){
log.error("Error info : " + e);
return ResponseEntity.badRequest()
.body(new ResponseEntity<Object>("Error info : " + e, HttpStatus.INTERNAL_SERVER_ERROR));
}
}
The issue is while i try to annotate with #CrossOrigin(" * ") the FrontEnd throws multiple allowed cross origin (" * "," * ") Cors error. If I remove the annotation it thorws no CORS origin header exist.
(Note : Whilst checking locally it has a single "Access-Control-Allow-Origin header", after pushing to server I got two "Access-Control-Allow-Origin" header)

Related

Get 403 when trying to make post request to controller endpoint

Summary: I want users who are not logged in to be able to make a post request to a certain endpoint, /users/registration/create specifically. I've tried to configure my security filter chain to permit requests to /users/registration/**. I can make a GET request to /users/registration/form, fill out the fields, and then click submit. When I submit the form (to /users/registration/create), the browser responds with a 403.
I have the following configuration:
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((requests) -> requests
.requestMatchers( "/users/registration/**").permitAll()
);
return http.build();
}
I can load /users/registration/form in my browser just fine, but once I submit the form, which sends the data to /users/registration/create, I get a 403 as a response.
In case it's needed...
Here's my controller:
#Controller
public class UserRegistrationsController {
#Autowired
private AppUserService appUserService;
#GetMapping("/users/registration/form")
public String newRegistration(WebRequest request, Model model) {
AppUserDto appUserDto = new AppUserDto();
model.addAttribute("user", appUserDto);
return "users/registration";
}
#PostMapping("/users/registration/create")
public void createRegistration(
#ModelAttribute("user") #Valid AppUserDto appUserDto,
HttpServletRequest request,
Errors errors
) {
try {
AppUser registered = appUserService.registerNewUserAccount(appUserDto);
} catch (AppUserAlreadyExistsException ex) {
}
}
}
Not sure if this is the right way to do it, but I tried disabling CSRF and doing the same request again:
#Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((authorize) -> {
authorize.requestMatchers("/", "/users/registration/**").permitAll();
});
http.csrf().disable();
return http.build();
}

Spring Boot Feign client - interceptor not working

I have feign client interceptor which adds Auth header (bearer token being fetched by RestTemplate). If the server responds with 401 (expired token) I want to reauthenticate and try the request again but the interceptor is not getting triggered 2nd time.
Interceptor code:
#Override
public void apply(RequestTemplate requestTemplate) {
if (AuthenticationService.bearerToken == null)
authenticationService.authenticate();
requestTemplate.header(AUTHORIZATION, BEARER_TOKEN_PREFIX + AuthenticationService.bearerToken );
}
Error decoder:
#Override
public Exception decode(String s, Response response) {
FeignException exception = feign.FeignException.errorStatus(s, response);
switch (response.status()) {
case 401:
authenticationService.authenticate();
return new RetryableException(response.status(), exception.getMessage(), response.request().httpMethod(), exception, null, response.request());
case 500:
throw new BadActionException(s, response.reason());
default:
break;
}
return exception;
}
Client config class:
#Bean
public RequestInterceptor requestInterceptor() {
return new RequestInterceptor (authenticationService);
}
#Bean
public RestClientDecoder restClientDecoder() {
return new RestClientDecoder(authenticationService);
}
Feign client:
#FeignClient(value = "server", url = "${server.base-url}", configuration = RestClientConfig.class)
public interface RestClient {
#PostMapping("api/test/{id}/confirm")
void test(#PathVariable Long id);
}
Side note: is there built in interceptor for authentication other than oAuth and BasicAuth? The server I am communicating with has simple jwt auth with expiration.

Spring 5 Cors and Csrf integration for angular js frontend http 401 [duplicate]

This question already has answers here:
CORS issue - No 'Access-Control-Allow-Origin' header is present on the requested resource
(8 answers)
Closed 3 years ago.
I am trying to execute requests from angular js frontend to spring boot middle ware (spring boot 2.1.4) . The setup used to work as expected before I migrated the app to spring boot.
Post spring boot migration all the filter and security config from web XML has been configured in the form of annotated classes.
Now my requests from UI are getting rejected by spring boot with http 401 error with cors policy (Allowed-Origin)
My current project setup looks like this
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomAuthenticationProvider customAuthenticationProvider;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customAuthenticationProvider);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().and().authorizeRequests().antMatchers("/**").hasAnyRole("ROLE_USER").anyRequest()
.authenticated().and().csrf().csrfTokenRepository(csrfTokenRepository());
}
private CsrfTokenRepository csrfTokenRepository() {
CustomDomainCookieCsrfTokenRepository repository = new CustomDomainCookieCsrfTokenRepository();
repository.setCookieHttpOnly(false);
return repository;
}
}
#WebFilter("/*")
public class ForceCORSFilter extends OncePerRequestFilter {
protected final Logger log = Logger.getLogger(this.getClass());
private CacheService cacheService;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
try {
List<String> originList = getCacheService().getValidOriginUrI();
String clientOrigin = request.getHeader("origin");
if (clientOrigin == null) {
// process the request even if origin is null
processValidRequest(request, response, filterChain, clientOrigin);
}
if (clientOrigin != null) {
// Origin should be validated if not null
if (originList.contains(clientOrigin)) {
processValidRequest(request, response, filterChain, clientOrigin);
} else {
log.info("####################### ORIGIN IS INVALID #######################" + clientOrigin);
filterChain.doFilter(request, response);
}
}
} catch (Exception e) {
response.getWriter()
.write("An error has occured while processing the request. Please retry with proper request.");
log.info("An error has occured in the request " + e.getMessage());
}
}
private void processValidRequest(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain,
String clientOriginAllowed) throws IOException, ServletException {
response.addHeader("Access-Control-Allow-Origin", clientOriginAllowed);
response.addHeader("Access-Control-Allow-Credentials", "true");
if (request.getHeader("Access-Control-Request-Method") != null && "OPTIONS".equals(request.getMethod())) {
response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, HEAD");
response.addHeader("Access-Control-Allow-Headers",
"Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers,Authorization, X-XSRF-TOKEN");
} else {
filterChain.doFilter(request, response);
}
}
public CacheService getCacheService() {
return cacheService;
}
public void setCacheService(CacheService cacheService) {
this.cacheService = cacheService;
}
}
Can someone point out what is wrong here. Why I am still getting
http 401 "No 'Access-Control-Allow-Origin' header is present on the
requested resource" errors.
One issue might be precedence -- your filter isn't run at the right order. You can use #Order(Ordered.HIGHEST_PRECEDENCE) so it is run before the Spring Security filters.
Having said that, Spring has first-class support for CORS already, so there is no need to tediously define a filter at all. See the documentation and an example.

Keycloak spring boot microservices

i have a few java micro services deployed on open shift . all of them are protected by a api-gateway application which uses keycloak for authentication & Authorization.
Down stream services need to log which user perform certain actions.
in my api-gateway application properties i have already set zuul.sensitiveHeaders to empty
zuul.sensitiveHeaders:
i can see bearer token in the downstream applications .
but how do i get the principal/user from token as downstream applications don't have keycloak dependency in gradle. ( if i add the dependency , i need to reconfigure realm and other properties ) .. is this the right way to do ?
i also tried adding a filter in api-gateway to separately set the user_name in header
#Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
System.out.println(" Filter doFilter "+req.getUserPrincipal());
if(req.getUserPrincipal() != null ){
res.setHeader("MYUSER",req.getUserPrincipal()==null?"NULL":req.getUserPrincipal().getName());
}
chain.doFilter(request, response);
}
But when i try to get the header in downstream microservices is null.
I wouldn't recommend doing this, or assuming that your non-web facing apps are completely secure. Realistically you should be re-validating the bearer token.
What you need is a zuul filter to add a header to the request. This is mostly from memory and you could update the filter to check if it should filter or not, that the request doesn't already contain an expected header etc.
#Component
public class AddUserHeader extends ZuulFilter {
private static final Logger LOG = LoggerFactory.getLogger(AddUserHeader.class);
#Override
public String filterType() {
return "pre";
}
#Override
public int filterOrder() {
return 0;
}
#Override
public boolean shouldFilter{
return true;
}
#Override
public Object run() {
RequestContext.getCurrentContext().addZuulRequestHeader("MYUSER", SecurityContextHolder.getAuthentication().getPrincipal().getName());
return null;
}

How to enable CORS for Error Response in Spring MVC?

I'm working on application where I use Spring MVC for the Back-end and Angular5 for the Front-end. I have been stuck with implementation of Auth2 security layer including Cross-Origin Resource Sharing. My CORS filter implementation looks like this:
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
#WebFilter("/*")
public class WebSecurityCorsFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletResponse res = (HttpServletResponse) response;
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
res.setHeader("Access-Control-Max-Age", "3600");
res.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type, Accept, x-requested-with, Cache-Control");
if ("OPTIONS".equalsIgnoreCase(((HttpServletRequest) request).getMethod())) {
res.setStatus(HttpServletResponse.SC_OK);
} else {
chain.doFilter(request, res);
}
}
#Override
public void destroy() {
}
}
I works almost properly, I'm able to obtain access_token and use it to get protected data from ResourcesServer:
{"access_token":"4fcef1f8-4306-4047-9d4d-1c3cf74ecc44","token_type":"bearer","refresh_token":"397016eb-dfb0-4944-a2e0-50c3bd07c250","expires_in":29,"scope":"read
write trust"}
Browser console screenshot
The problem starts when I try to handle the request using expired token. In such case I'm not able to catch the correct ErrorResponeCode by Angular. Instead of 401 i Angular HttpClient got "Unknown Error" with status:0.
It looks like the problem is with CORS policy where the ErrorResponse doesn't include neccessery headers like Access-Control-Allow-Origin (...)
Failed to load http://localhost:8030/api/xxxx: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:8070' is therefore not allowed
access. The response had HTTP status code 401.
ErrorResponse Headers - Screenshot
I have searched for how to enable CORS for ErorrResponse (InvalidTokenException etc.) in Spring MVC . I tried with various approach: accessDeniedHandler and setExceptionTranslator but without success. I really made effort to find the solution myself but I'm a beginner in Spring. I am not sure if this is possible at all.
ANGULAR (UPDATE)
#hrdkisback, it's rather not angular issue, anyway this my code :
#Injectable()
export class HttpInterceptorService implements HttpInterceptor {
addToken(req: HttpRequest<any>, oauthService: AuthenticationService): HttpRequest<any> {
if(oauthService.isTokenExist()){
return req.clone({ setHeaders: { Authorization: 'Bearer ' + oauthService.getAccessToken() }})
}
return req;
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpSentEvent | HttpHeaderResponse | HttpProgressEvent | HttpResponse<any> | HttpUserEvent<any>> {
let oauthService = this.inj.get(AuthenticationService);
return next.handle(this.addToken(req,oauthService))
.do((event: HttpEvent<any>) => {
if (event instanceof HttpResponse) {
// process successful responses here
}
}, (error: any) => {
if (error instanceof HttpErrorResponse) {
// Error
console.log(error);
}
});
}
}
Issue solved after I added my CORS filter on ResourcesServer configuration level like this:
The correct configuration that works for me!
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.addFilterAfter(new WebSecurityCorsFilter(), CsrfFilter.class)
...
}
....
}
In my previous configuration I added the filter in the same way but on the top level of MVC Security Configuration and it was the root couse of my issue:
The previous configuration that caused my issue
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.addFilterAfter(new WebSecurityCorsFilter(), CsrfFilter.class)
...
}
....
}
I faced the same problem..I was trying Basic Auth with Angular 5.
The problem is that you don't add the CORS header on error response.
Here is what I did
#Component
public class AuthEntryPoint extends BasicAuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authEx)
throws IOException, ServletException {
response.addHeader("WWW-Authenticate", "Basic realm=" +getRealmName());
response.addHeader("Access-Control-Allow-Origin", "http://localhost:4200");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
PrintWriter writer = response.getWriter();
writer.println("HTTP Status 401 - " + authEx.getMessage());
}
}
That would do the trick!

Resources