Disable Spring Security for OPTIONS Http Method
JavaSpringSpring MvcSpring SecurityCorsJava Problem Overview
Is it possible to disable Spring Security for a type of HTTP Method?
We have a Spring REST application with services that require Authorization token to be attached in the header of http request. I am writing a JS client for it and using JQuery to send the GET/POST requests. The application is CORS enabled with this filter code.
doFilter(....) {
HttpServletResponse httpResp = (HttpServletResponse) response;
httpResp.setHeader("Access-Control-Allow-Origin", "*");
httpResp.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
httpResp.setHeader("Access-Control-Max-Age", "3600");
Enumeration<String> headersEnum = ((HttpServletRequest) request).getHeaders("Access-Control-Request-Headers");
StringBuilder headers = new StringBuilder();
String delim = "";
while (headersEnum.hasMoreElements()) {
headers.append(delim).append(headersEnum.nextElement());
delim = ", ";
}
httpResp.setHeader("Access-Control-Allow-Headers", headers.toString());
}
But when JQuery sends in the OPTIONS request for CORS, the server responds with Authorization Failed token. Clearly the OPTIONS request, lacks Authorization token. So is it possible to let the OPTIONS escape the Security Layer from the Spring Security Configuration?
Java Solutions
Solution 1 - Java
If you're using an annotation based security config file (@EnableWebSecurity
& @Configuration
) you can do something like the following in the configure()
method to allow for the OPTION
requests to be permitted by Spring Security without authentication for a given path:
@Override
protected void configure(HttpSecurity http) throws Exception
{
http
.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS,"/path/to/allow").permitAll()//allow CORS option calls
.antMatchers("/resources/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.httpBasic();
}
Solution 2 - Java
Allow all OPTIONS in context:
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
}
Solution 3 - Java
Have you tried this
> You can use multiple
<http auto-config="true">
<intercept-url pattern="/client/edit" access="isAuthenticated" method="GET" />
<intercept-url pattern="/client/edit" access="hasRole('EDITOR')" method="POST" />
</http>
Above means you need to select the url pattern to intercept and what methods you want
Solution 4 - Java
In case someone is looking for an easy solution using Spring Boot. Just add an additional bean:
@Bean
public IgnoredRequestCustomizer optionsIgnoredRequestsCustomizer() {
return configurer -> {
List<RequestMatcher> matchers = new ArrayList<>();
matchers.add(new AntPathRequestMatcher("/**", "OPTIONS"));
configurer.requestMatchers(new OrRequestMatcher(matchers));
};
}
Please note that depending on your application this may open it for potential exploits.
Opened issue for a better solution: https://github.com/spring-projects/spring-security/issues/4448
Solution 5 - Java
The accepted answer is not recommended and you shold not do that.
Below is the correct way for CORS setup of Spring Security and jQuery's ajax.
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(userAuthenticationProvider);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors() // <-- This let it use "corsConfigurationSource" bean.
.and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
...
}
@Bean
protected CorsConfigurationSource corsConfigurationSource() {
final CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Collections.singletonList("http://localhost:3000"));
configuration.setAllowedMethods(Arrays.asList("HEAD", "GET", "POST", "PUT", "DELETE", "PATCH"));
// NOTE: setAllowCredentials(true) is important,
// otherwise, 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'.
configuration.setAllowCredentials(true);
// NOTE: setAllowedHeaders is important!
// Without it, OPTIONS preflight request will fail with 403 Invalid CORS request
configuration.setAllowedHeaders(Arrays.asList(
"Authorization",
"Accept",
"Cache-Control",
"Content-Type",
"Origin",
"ajax", // <-- This is needed for jQuery's ajax request.
"x-csrf-token",
"x-requested-with"
));
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
And from jQuery side.
$.ajaxSetup({
// NOTE: Necessary for CORS
crossDomain: true,
xhrFields: {
withCredentials: true
}
});
Solution 6 - Java
If you're using annotation-based security config then you should add spring's CorsFilter
to the application context by calling .cors()
in your config, something like this:
@Override
protected void configure(HttpSecurity http) throws Exception
{
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/resources/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.httpBasic()
.and()
.cors();
}
Solution 7 - Java
In some cases, it is needed add configuration.setAllowedHeaders(Arrays.asList("Content-Type"));
to corsConfigurationSource()
when using WebSecurityConfigurerAdapter
to solve the cors problem.