Angular login request to Spring rest with security - angularjs

I am very new in Spring security. I am confused about the Spring rest security but could not find any full solution. I have following scenario:
1) I have created the angular js service which makes a $http call to the spring rest.
2) I want spring security to intercept this url(/login) and give me response back accordingly.
What I tried if I am accessing the url directly http://localhost:8123/SpringMVC/login then its working fine, it's asking for username and password and after entering the correct user, password I got the result but same thing I am doing from AngularJs; it's giving me the following error
angular.js:10514 OPTIONS http://localhost:8123/SpringMVC/rest/login/ (anonymous function) # angular.js:10514sendReq # angular.js:10333serverRequest # angular.js:10045processQueue # angular.js:14567(anonymous function) # angular.js:14583$eval # angular.js:15846$digest # angular.js:15657$apply # angular.js:15951bootstrapApply # angular.js:1633invoke # angular.js:4450doBootstrap # angular.js:1631bootstrap # angular.js:1651angularInit # angular.js:1545(anonymous function) # angular.js:28359trigger # angular.js:2996eventHandler # angular.js:3271
localhost/:1 XMLHttpRequest cannot load http://localhost:8111/SpringMVC/rest/categories/. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8234' is therefore not allowed access. The response had HTTP status code 401.
Please suggest how to configure the header correctly in fronend as well as backend both angular and rest application are running on different server.
This is in SecurityConfiguration.java
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.and()
.authorizeRequests()
.antMatchers("/index.html", "/home.html", "/login.html", "/").permitAll()
.anyRequest().authenticated();
}
This is what I am doing in Entry Point:
#Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
//prevent default behaviour
if (request.getMethod().equals("OPTIONS")) {
response.addHeader("Access-Control-Allow-Origin", "*");
response.addHeader("Access-Control-Allow-Methods", "POST,PUT, GET, OPTIONS, DELETE");
response.addHeader("Access-Control-Max-Age", "3600");
response.addHeader("Access-Control-Allow-Headers",
" Origin, X-Requested-With, Content-Type, Accept,AUTH-TOKEN");
}
else
{
System.out.println("hello from server");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException.getMessage());
}
}

Try adding-
response.setHeader("Access-Control-Request-Headers", "X-Requested-With, Content-Type, Accept");
Also update
response.addHeader("Access-Control-Allow-Headers",
" Origin, X-Requested-With, Content-Type, Accept,AUTH-TOKEN");
to
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept,AUTH-TOKEN, Authorization");`

Related

Camel Rest DSL with jetty component fails to populate CORS headers correctly

I am having a hard time figuring out how camel rest dsl populates the CORS response headers when using jetty component as container. I have set the cors headers as follows
camel.rest.cors-headers.Access-Control-Allow-Credentials=true
camel.rest.cors-headers.Access-Control-Max-Age=3600
camel.rest.cors-headers.Access-Control-Allow-Methods=GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, CONNECT, PATCH
camel.rest.cors-headers.Access-Control-Allow-Headers=Origin, X-Requested-With, Content-Type, Accept, Accept-Encoding, Accept-Language, Host, Referer, User-Agent, Authorization, x-omit
camel.rest.cors-headers.Access-Control-Allow-Origin=*.example.com,*.example1.com,*.example2.com
However it always returns the list of origins rather than the value passed in Origin header.
I have tried enabling the CORS on jetty component and writing a custom JettyHttpBinding however, camel rest is still somehow overriding the values. My expected result is if origin value is in list of allowed origins then it should return Access-Control-Allow-Origin header as origin value otherwise return an error/throw an exception. Is there a way to customize this? Any help would be really appreciated
I was finally able to do this after getting some idea from following question.
Add CORSHandler to a camel-jetty component
However, I had to extend HandlerWrapper class instead of AbstractHandler
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.HandlerWrapper;
import javax.servlet.DispatcherType;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class CORSHandler extends HandlerWrapper {
#Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
if (baseRequest.getDispatcherType() == DispatcherType.REQUEST) {
// Custom code for adding CORS headers - similar to org.eclipse.jetty.servlets.CrossOriginFilter::handle;
}
if (this._handler != null) {
this._handler.handle(target, baseRequest, request, response);
}
}
}

Option instead of Post: Response to preflight request doesn't pass access control check.

I am trying to access an rest api source and it is working finde with postman or http requester. What is wrong with my code?
let keyUrl ="yourUrl"
getAPIKey(){
let headers = new Headers();
// headers.append('Content-Type', 'text/plain');
headers.append('Authorization', 'Bearer ' + btoa(this.cred.user + ":" + this.cred.pw));
headers.append('Access-Control-Allow-Methods', 'GET, POST, PUT');
headers.append("Access-Control-Allow-Headers","*");
let options = new RequestOptions({
headers: headers,
method: RequestMethod.Post,
});
return this.http.post(this.keyUrl,options)
.map((res: Response) => {
console.log('##############')
console.log(res.json())
})
.catch(this.handleError)
.subscribe(
status => console.log(status),
error => this.handleError(error),
() => console.log('DONE')
);
}
As error I am always getting:
Response to preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:4200' is therefore not allowed
access.
In my backend the post request is as "Options" even if I sent http.post.
I had this problem too and it took me soo long to find the right solution
You will have to set the headers on your server-side, too. They essentially Need to accept "OPTIONS"-requests.
This may depend on your Server that Hosts the REST-API; if you are using an Apache-Server, you can add this to your httpd.conf and it should work:
<Directory />
Options Indexes FollowSymLinks MultiViews
AllowOverride All
Order allow,deny
allow from all
Header set Access-Control-Allow-Origin "jakartab3:16090, epbtesti:16090, localhost:16090"
Header set Access-Control-Allow-Credentials "true"
Header add Access-Control-Allow-Headers "Content-Type, Accept"
Header set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
Alternatively, it should also work at the Point where you handle the security (if you do). In this case, set the headerys of your HttpServletResponse like
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Origin-Methods", "GET, POST, OPTIONS");
Hope this fixes your Problem
EDIT: trying to explain why you would need to set that:
Access-Control-Allow-Origin prevents other Servers from accessing your REST-Service - and your angular-application runs on it's own server, on port 4200. For the angular-application to be able to Access your REST-service, as I've already said, you need to set the headers in the server that is hosting your REST-Service according to my answer; It can't work elsewhere since this security-feature prevents other servers from accessing your REST-service

No 'Access-Control-Allow-Origin' header is present on the requested resource - error

I'm trying to get a JSON file from instagram, and I got an error when I make an $http.get :
insta.ctrl :
insta.controler 'instaCtrl' ($scope, $http), ->
$http.get('http://api.instagram.com/publicapi/oembed/?url=http://instagr.am/p/fA9uwTtkSN/')
.success(data), ->
#done !
.error(e), ->
#nah !
my apache2's conf
Header always set Access-Control-Allow-Origin "*"
Header always set Access-Control-Allow-Headers "X-Requested-With, Content-Type, Origin, Authorization, Accept, Client-Security-Token, Accept-Encoding"
Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS, DELETE, PUT"
Error message on chrome :
XMLHttpRequest cannot load #url_of_intagram_api_here. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin '' is therefore not allowed access.
It work when I disable the internet security in chrome.
Any ideas?
Apparently Instagram API doesn't implement CORS. However they provide JSONP interface for data retrieving. So what you can do is to use jsonp method:
$http.jsonp('http://api.instagram.com/publicapi/oembed/?url=http://instagr.am/p/fA9uwTtkSN/&callback=JSON_CALLBACK').success(function(data) {
$scope.data = data;
});
Note callback=JSON_CALLBACK GET parameter you need to send.
Demo: http://plnkr.co/edit/OG1sT7A9OM1hWBqCvSmC?p=preview

How to post JSON data to REST Webservice in AngularJS

How to post JSON data to web-service via AngularJS
here is the code snippet
.controller('MessagePostCtrl', function($scope, $http) {
$scope.postMessage = function() {
var msg = document.getElementById('message').value;
var msgdata = {
message : msg
};
var res = $http.post('http://<domain-name>/messenger/api/posts/savePost',msgdata);
res.success(function(data, status, headers, config) {
console.log(data);
});
}
})
OPTIONS http:///messenger/api/posts/savePost
ionic.bundle.js:16185(anonymous function) ionic.bundle.js:16185
sendReq ionic.bundle.js:15979 serverRequest ionic.bundle.js:15712
wrappedCallback ionic.bundle.js:19197 wrappedCallback
ionic.bundle.js:19197(anonymous function) ionic.bundle.js:19283
Scope.$eval ionic.bundle.js:20326 Scope.$digest ionic.bundle.js:20138
Scope.$apply ionic.bundle.js:20430(anonymous function)
ionic.bundle.js:43025(anonymous function) ionic.bundle.js:10478
forEach ionic.bundle.js:7950 eventHandler ionic.bundle.js:10477
triggerMouseEvent ionic.bundle.js:2648 tapClick ionic.bundle.js:2637
tapMouseUp ionic.bundle.js:2707
XMLHttpRequest cannot load
http:///messenger/api/posts/savePost. Invalid HTTP
status code 404
But when I remove the msgdata from $http.post method, everything is working fine.
Can anyone tell me where the issue is or else guide me how to send JSON data to web-service
Thanks for the help
**Edited:
The Issue was with the CORS, Im using codeigniter REST Controller for web-services.
Modified the headers. If anyone has the same issue add the below header in the construct
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, DELETE");
header("Access-Control-Allow-Headers: X-API-KEY, Origin, X-Requested-With, Content-Type, Accept, Access-Control-Request-Method");
if ( "OPTIONS" === $_SERVER['REQUEST_METHOD'] ) {
die();
}
Thanks to Linial for the break-through, telling me where the issue is.**
Okay,
You mixed up couple of things:
First as I can see your request has changed from POST to OPTIONS.
Why?
You are performing Cross-site HTTP requests ( CORS ), which means that your WebApp and your backend API are not in the same domain.
What happens live is that the request is being preflighted.
Preflighted request: by Mozilla MDN:
It uses methods other than GET, HEAD or POST. Also, if POST is used
to send request data with a Content-Type other than
application/x-www-form-urlencoded, multipart/form-data, or text/plain,
e.g. if the POST request sends an XML payload to the server using
application/xml or text/xml, then the request is preflighted.
Which means, any request beside GET, HEAD or POST will be change to OPTIONS
AND: Also POST if used to send data with Content-Type other than application/x-www-form-urlencoded, multipart/form-data, or text/plain
I now understand, but what to do? I have to make POST request!
You don't have many options, since CORS is defined on the server.
But on the client you could do so (Example):
change the encode type in angular like so:
$http.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded";
OR
Set your server to approve CORS like so:
Access-Control-Allow-Headers: Content-Type \\ This will allow you to set content type header in the client.
Access-Control-Allow-Methods: GET, POST, OPTIONS \\ This will allow you to send GET POST and OPTIONS, which is necessary because of preflighted requests.
Access-Control-Allow-Origin: * \\ This will allow anyone to perform CORS requests.
Good Luck!

setting HTTP Header for angularjs REST calls

I'm trying to set a HTTP Header for all my REST calls with following code:
app.factory('authInterceptor', function ($rootScope, $q, $window) {
return {
request: function (config) {
config.headers = config.headers || {};
config.headers.Authorization = '12345678';
return config;
},
response: function (response) {
if (response.status === 401) {
// handle the case where the user is not authenticated
}
return response || $q.when(response);
}
};
});
app.config(function ($httpProvider) {
$httpProvider.interceptors.push('authInterceptor');
});
I currently don't have any authorization enabled on the server.
when I leave out the line "config.headers.Authorization = '12345678';" , then the REST call works well and I get my results. In the JS console I see
GET http://localhost:8080/rest/club/1 [HTTP/1.1 200 OK 7ms]
But when I put this line in to set the Header field, then I see following request in the javascript console
OPTIONS http://localhost:8080/rest/club/1 [HTTP/1.1 200 OK 2ms]
Why does setting Authorization Header change my method from "GET" to "OPTIONS"? And how can I set a custom Header and my request still work?
changing it to
config.headers["X-Testing"] = '12345678';
had the same result.
EDIT:
I tried the answer, I'm setting following HTTP Headers in the server:
response.getHeaders().putSingle("Access-Control-Allow-Origin", "http://localhost");
response.getHeaders().putSingle("Access-Control-Allow-Header", "X-Testing");
response.getHeaders().putSingle("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
response.getHeaders().putSingle("Access-Control-Max-Age", 1728000);
my REST server is running on port 8080, the webserver for the html/JS on port 8000 (initially worked with file://... but moved to a separate webserver because Origin was null)
response.getHeaders().putSingle("Access-Control-Allow-Origin", "*");
or
response.getHeaders().putSingle("Access-Control-Allow-Origin", "http://localhost:8000");
didn't work either.
Must I return any content in the OPTIONS response? I tried 200 OK with the same content as the GET, but I also tried 204 No Content.
2nd EDIT:
here is what firefox sends and receives for the OPTIONS method:
You need to enable CORS in your REST service. As explained in MDN, once you add a custom header, the http protocol specifies performing a preflight,
Preflighted requests
Unlike simple requests (discussed above), "preflighted" requests first
send an HTTP request by the OPTIONS method to the resource on the
other domain, in order to determine whether the actual request is safe
to send. Cross-site requests are preflighted like this since they may
have implications to user data. In particular, a request is
preflighted if:
It uses methods other than GET, HEAD or POST. Also, if POST is used
to send request data with a Content-Type other than
application/x-www-form-urlencoded, multipart/form-data, or text/plain,
e.g. if the POST request sends an XML payload to the server using
application/xml or text/xml, then the request is preflighted. It sets
custom headers in the request (e.g. the request uses a header such as
X-PINGOTHER)
Addition to enabling CORS you also need to add a Access-Control-Allow-Headers header tag to accept your custom header (for the OPTIONS response). This is visible in the MDN Example,
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER
Access-Control-Max-Age: 1728000
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain
UPDATE
As mentioned in the comments, the OPTION response's Access-Control-Allow-Headers is missing the last "s".

Resources