Response for preflight has invalid HTTP status code 401
Closed this issue ยท 17 comments
I have created angular 2 client
But i receive this error when i call /user method
Response for preflight has invalid HTTP status code 401
Are you running your Angular 2 client on a dev-server? Like is it running on a different port(4200)
?
Yes i do
I have tried with setting up CorsFilter, as explained in official spring guide to cors, but i still receive the message
What is interesting is that i log in and then receive this message when i call /user method
Sounds to me like the request header from Angular 2 is wrong. Have you set withCredentials: true
?
headers = new Headers({
'Accept': 'application/json'
});
...
this.http.get(
path,
{
headers: this.headers,
withCredentials: true
}
)
.map(this.extractData)
.catch(this.handleError);
Still the same problem
Here is my code
Spring Cors Filter:
@component
@order(Ordered.HIGHEST_PRECEDENCE)
public class SimpleCORSFilter implements Filter {
@Override
public void init(FilterConfig fc) throws ServletException {
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) resp;
HttpServletRequest request = (HttpServletRequest) req;
response.setHeader("Access-Control-Allow-Origin", "http://localhost:4200");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Headers",
"x-requested-with, authorization, Content-Type, Authorization, credential, X-XSRF-TOKEN");
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
chain.doFilter(req, resp);
}
}
@Override
public void destroy() {
}
}
Angular code
Login service:
sendCredential(model) {
let tokenUrl1 = "http://localhost:8080/auth";
let headers1 = new Headers({ 'Content-Type': 'application/json' });
return this.http.post(tokenUrl1, JSON.stringify(model), { headers: headers1 });
}
sendToken(token) {
let tokenUrl2 = "http://localhost:8080/user";
let headers = new Headers({ 'Accept': 'application/json' });
headers.append('Authorization', 'Bearer ' + token);
return this.http.get(tokenUrl2, { headers, withCredentials: true });
}
Login component
onSubmit() {
this.loginService.sendCredential(this.model)
.map(res => res.json())
.subscribe(
data => {
localStorage.setItem("token", data.token);
localStorage.setItem("currentUserName", this.model.username);
this.loginService.sendToken(localStorage.getItem("token")).subscribe(
data => {
this.currentUserName = this.model.username;
localStorage.setItem("currentUserName", this.model.username);
this.model.username = '';
this.model.password = '';
},
error => console.log(error)
);
},
error => console.log(error)
);
}
@igoravramovic
I see, you should remove the Bearer
. Like this: headers.append('Authorization', token);
.
If you really want to use Bearer
you can do something like this:
if ( authHeader != null && authHeader.startsWith("Bearer ")) {
return authHeader.substring(7);
}
@bfwg You're right, I didn't include the "Bearer" prefix, yet. But I've opened a ticket for that.
@igoravramovic could you fix your problem?
Sorry for not responding more quickly, i was not able to take time to try proposed solution
No, this didn't solve my problem i still receive same error message
I was having the same issue with the prefight OPTIONS request return a 401. When the OPTIONS request is sent from angular 2, it's sent without the Authorization header. Spring Security tries to authenticate the request without the header and returns a 401. In the JwtAuthenticationTokenFilter
, the token is null.
I added a filter before the JwtAuthenticationTokenFilter
filter to check for the OPTIONS request and return HttpServletResponse.SC_OK
. Here is the filter that I have in my code.
public class CorsFilter extends OncePerRequestFilter {
static final String ORIGIN = "Origin";
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String origin = request.getHeader(ORIGIN);
response.setHeader("Access-Control-Allow-Origin", "*");//* or origin as u prefer
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "PUT, POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "content-type, authorization");
if (request.getMethod().equals("OPTIONS"))
response.setStatus(HttpServletResponse.SC_OK);
else
filterChain.doFilter(request, response);
}
}
In my security config I added the bean for the filter and added it to the configuration before JwtAuthenticationTokenFilter
.
@Bean
public CorsFilter corsFilter() throws Exception {
return new CorsFilter();
}
http
.addFilterBefore(corsFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class)
.headers()
.cacheControl();
This seems to be working for me. Hope this helps you.
@igoravramovic is this helping you?
@jmw5598 - I'm not sure if this can help, but try putting
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
}
On your securityConfig.java
class. As this ignores and let http:options
request pass through.
@jrcastillo you are absolutely right, and this should solve OP's problem except he has to remove the Bearer part. To understand the original problem, please follow this link https://stackoverflow.com/questions/38368794/angular-2-basic-authentication-not-working
@igoravramovic is not replying since 19th Apr so I close this ticket now.
You can get through this very easy! Let's follow me right now
- Create a shortcut on your desktop
- Right-click on the shortcut and click Properties
- Edit the Target property
- Set it to "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --disable-web-security --user-data-dir="C:/ChromeDevSession"
- In VIsual Studio Code, run ionic serve -l
- You're gonna see new tab open http://localhost:8100/ionic-lab. You should be aware that this link is opened in the normal chrome tab, not the "disable-web-security" chrome we have set up.
- Double click to the shortcut that we have set up to open the "disable-web-security" chrome tab. Then paste http://localhost:8100/ionic-lab into this tab.
So the reason that we get multiple errors when working with woo-commerce-api is this "web-security" by Google. Then you just disable it and you actually don't need any CORS Extensions. So remove them right now if you have installed.
And this solution i write for people who learn this course https://www.udemy.com/ionic-3-apps-for-woocommerce-build-an-ecommerce-mobile-app/. This is an ionic e-commerce app that using woo-commerce-api to set and get data from Wordpress (local or live server). If you have trouble in other language not ionic, it still works fine.
Actually i have done a lot of searchings on Google to find this solution. I hope this helps all of you. Now, i need to go to bed because tomorrow i have a final report about this ionic project with my lecturer ๐
See ya!
Quy Le
@jrcastillo thanks for the tip
And if anyone is using HttpSecurity then we need to use this
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()//allow CORS option calls
.antMatchers("/resources/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.httpBasic();
}
I can not for the life of me get cors to work on this project. I tried all of the above & nothing works. The only change I've been able to get working is applying a filter which only works after the user is already logged in showing the new headers on the response, but still fails a preflight request.
POST http://localhost:8080/auth HTTP/1.1
Content-Type: application/json; charset=utf-8
{
"username":"admin",
"password":"admin"
}
GET http://localhost:8080/user HTTP/1.1
content-type: application/json; charset=utf-8
authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTUyMzk5NTUwNSwiaWF0IjoxNTIzMzkwNzA1fQ.ZJTUYWU4MVEIOR5EjLXqPiIsmBafATuSju6xSkBF8hmx6USM8q7qLXpCc4Wt2ZiIC3jKtSSFThP0sQfwp3xcRQ
update after trying a ton of things the solution for me ended up being the following:
package org.tci.filters;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
//import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
public class CorsConfig {
@Bean
public FilterRegistrationBean corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("OPTIONS");
config.addAllowedMethod("HEAD");
config.addAllowedMethod("GET");
config.addAllowedMethod("PUT");
config.addAllowedMethod("POST");
config.addAllowedMethod("DELETE");
config.addAllowedMethod("PATCH");
source.registerCorsConfiguration("/**", config);
// return new CorsFilter(source);
final FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(0);
return bean;
}
@Bean
public WebMvcConfigurer mvcConfigurer() {
return new WebMvcConfigurerAdapter() {
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedMethods("GET", "PUT", "POST", "GET", "OPTIONS");
}
};
}
}
and adding
@CrossOrigin(origins = { "*" }, maxAge = 6000)
at the top of each controller