We are currently trying to access a REST-Endpoint of a Spring Boot app in an Angular2-App, but despite the fact that the Authorization header works in Postman (Authorization/Basic dXNlcjp1c2Vy, for a basic test user with the password user), it does not work when the app tries to access the REST-Endpoint. The Spring-Security is configured as such:
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ApplicationSecurity extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder builder) throws Exception {
builder.inMemoryAuthentication().withUser("user").password("user").roles("USER").and().withUser("admin")
.password("admin").roles("ADMIN");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().fullyAuthenticated();
http.httpBasic();
http.csrf().disable();
}
}
The angular app asks for credentials before making the first request, like so:
loginTest() {
let headers: Headers = new Headers({'Content-Type': 'application/json', 'Accept': 'application/json'});
headers.append('Authorization', 'Basic ' + window.btoa(this._username + ':' + this._password));
console.log(headers);
return this.http.get('http://localhost:8080/', {headers: headers})
.map((res:any) => res.json);
}
With localhost:8080 being the Spring Boot app. We already tried adding Content-Policy tags because there were similar problems with an earlier project, like so:
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'unsafe-inline' 'unsafe-eval' 'self';
connect-src 'self' http://localhost:3000/ http://localhost:8080/ ws://localhost:3000/; img-src 'self' data:;
style-src 'unsafe-inline' 'self'; font-src 'self'"/>
but that didn't work either. The error we get:
XMLHttpRequest cannot load http://localhost:8080/. Response for preflight has invalid HTTP status code 401
When looking at the request via Chrome, this is the Response:
HTTP/1.1 401 Unauthorized
Server: Apache-Coyote/1.1
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Set-Cookie: JSESSIONID=4659A74A950FE6C99948D8F4CB245E27; Path=/; HttpOnly
WWW-Authenticate: Basic realm="Realm"
Access-Control-Allow-Origin: http://localhost:3000
Vary: Origin
Access-Control-Allow-Methods: GET
Access-Control-Allow-Headers: accept, authorization, content-type
Access-Control-Allow-Credentials: true
Allow: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
Content-Length: 0
Date: Mon, 04 Jul 2016 07:54:30 GMT
I don't see what the Angular2-App is doing different when compared to PostMan here.
I think that your problem is related to CORS. Postman is a chrome plugin so it has the permission to execute CORS requests. It's not the case of your Angular application: it's more restricted.
See this question:
In the case of Angular, you need to handle CORS. You have a preflighted request (OPTIONS) that is sent before sending the actual request. I think that in your case, credentials aren't sent into this preflighted request so the server can't authenticate it and returns a 401 status code. This prevents from executing the target request.
I see two solutions:
Use the withCredentials parameter to true (available in Angular2 from RC2)
this.http.get('http://...', { withCredentials: true })...
Only check security on target requests and not on preflighted ones. It's a configuration to do on the server side.
See these articles for more details:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With