1

I have created a SpringBoot application which has an AngularJS front-end and I am not using any Spring Security modules.

I have added the @CrossOrigin annotation in my controller as follows

@CrossOrigin(origins = "http://localhost:3000", maxAge = 3600)
@RestController
public class MyController {
   public static final Logger logger = LoggerFactory.getLogger(MyController.class);
   .....
   .....

I have also created a CORS filter as shown below.

@Component
public class CORSFilter 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, PUT, OPTIONS, DELETE");
    response.setHeader("Access-Control-Max-Age", "3600");
    response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
    chain.doFilter(req, res);
}

public void init(FilterConfig filterConfig) {}
public void destroy() {}

}

I am getting the following error for OPTIONS in my angularJS front-end

XMLHttpRequest cannot load http://localhost:8080/abc/books/. Response for preflight has invalid HTTP status code 401 (edited)

[12:10] 
zone.js:2744 OPTIONS http://localhost:8080/abc/books/ 401 ()

My SpringBoot app works fine with PostMan and this preflight error happens when I use Chrome. How can I fix it?

EDIT

I also use activiti-spring-boot-starter-rest-api which has following:

import org.activiti.engine.IdentityService;
import org.activiti.engine.identity.User;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

@Component
public class AuthenticationService {

@Bean
InitializingBean usersAndGroupsInitializer(final IdentityService identityService) {

    return new InitializingBean() {
        public void afterPropertiesSet() throws Exception {
            User admin = identityService.newUser("admin");
            admin.setPassword("admin");
            identityService.saveUser(admin);
            }
        };
    }

}

2
  • @g00glen00b I do not have spring security enabled Commented Sep 28, 2017 at 6:11
  • @g00glen00b That makes sense. I have edited my question. I didn't realise the activiti dependency could be causing the issue. Is there a way I can disable requiring authorizations for OPTIONS ? Commented Sep 28, 2017 at 6:35

2 Answers 2

2

As mentioned in the comments, the issue is that activiti-spring-boot-starter-rest-api comes with security out of the box. The issue here is that they apply security on the root context, which means that any other endpoint will also have authentication.

Option 1: Adding your own security

To solve this, you can create your own SecurityConfig class with a higher precedence and allow permit all requests going to /abc/** (or anything else), for example:

@Order(Ordered.HIGHEST_PRECEDENCE) // This should "overrule" Activiti's security
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.antMatcher("/abc/**")
            .authorizeRequests().anyRequest().permitAll();
    }
}

Option 2: Disabling Activiti's security

Alternatively, you can disable Activiti's security auto configuration:

@EnableAutoConfiguration(exclude = {
    org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration.class,
    org.activiti.spring.boot.SecurityAutoConfiguration.class
})

This will disable the security auto configuration of both Activiti and Spring boot itself. If you would only disable Activiti's security configuration, Spring boot's autoconfiguration would kick in, and you would still have authentication on all your endpoints.

However, you probably still want to apply security on some of Activiti's endpoints, but this allows you to customise it by yourself, rather than relying on the auto configuration. In your case, you could choose to apply security to all /identity/**, /management/**, /history/**, /repository/**, /query/** and /runtime/** paths, which are being used by Activiti.


Option 3: Overriding the filter chain in case of OPTIONS

A third option is to bypass the entire security filterchain. As shown in this answer, you can break out of the filter chain by not calling chain.doFilter(req, res). This can be done by adding the following in your filter:

if (HttpMethod.OPTIONS.name().equalsIgnoreCase(req.getMethod()) {
    res.setStatus(HttpServletResponse.SC_OK);
} else {
    chain.doFilter(req, res); // Only apply doFilter() when not OPTIONS
}

NOTE: Be aware though, this will only allow the OPTIONS call to go through. The security also affects the other endpoints you defined in your controller, and not just the OPTIONS call.

By conditionally calling doFilter(), you can skip the entire security filter chain in case an OPTIONS request comes in. You have to cast req and res to HttpServletRequest and HttpServletResponse respectively.

Additionally, you have to make sure this filter is called before the security filterchain. To do that, change the order of the CORSFilter to a chosen value, for example:

@Component
@Order(1) // You can choose the value yourself
public class CORSFilter implements Filter {
    // ...
}

Now change the security filter chain order by setting the security.filter-order property in application.properties to a higher value:

security.filter-order=2
Sign up to request clarification or add additional context in comments.

4 Comments

I tried the option 1. but it gave me the same error. I enabled the debug mode on my app and this is what I could see. DispatcherServlet with name 'dispatcherServlet' processing OPTIONS request for [/books/error]. How ever it returned the correct url when you try with POSTMAN
That endpoint is /books/error, and doesn't fall under the /abc/** matcher I guess? So this might be the issue.
I went with the option 2 for the time being. If I want to enable it for certain urls do I have to create a separate security config with those endpoints?
If they don't have a similar path, probably yes. In those cases it might be better to go for option 2 and to define your own SecurityConfig protecting the paths of Activiti itself.
1

Here is a Spring CORS filter which works fine with AngularJS

public class CORSFilter extends OncePerRequestFilter {

    private final Logger LOG = LoggerFactory.getLogger(CORSFilter.class);

    @Override
    protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws ServletException, IOException {
        LOG.info("Adding CORS Headers ........................");        
        res.setHeader("Access-Control-Allow-Origin", "*");
        res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
        res.setHeader("Access-Control-Max-Age", "3600");
        res.setHeader("Access-Control-Allow-Headers", "X-PINGOTHER,Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization");
        res.addHeader("Access-Control-Expose-Headers", "xsrf-token");
        if ("OPTIONS".equals(req.getMethod())) {
            res.setStatus(HttpServletResponse.SC_OK);
        } else { 
            chain.doFilter(req, res);
        }        
    }

I found this from the post Spring cors allow all origins

1 Comment

This will only work if the filter is ordered before the security filter chain as far as I know.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.