When calling my REST Service in Angular, there are no response headers.
Login method in Angular
login(username: string, password: string) {
const credentials = { "username": username, "password": password };
return this.http.post(this.url, credentials)
.subscribe(
data => console.log(data), // JSON.stringify(data.headers) also is empty
error => console.log(error)
);
}
Output in the Chrome dev tools console
Response {_body: "", status: 200, ok: true, statusText: "OK", headers:
Headers…}headers: Headers_headers: Map(0)_normalizedNames:
Map(0)proto: Objectok: truestatus: 200statusText: "OK"type: 2url:
"http://localhost:8080/backend/rest/login"_body: ""proto: Body
But when I send the same post request with postman, I get the expected result:
Access-Control-Allow-Credentials →true
Access-Control-Allow-Origin →chrome-extension://fhbjgbiflinjbdggehcddcbncdddomop
Authorization →Bearer eyJ[...]
Connection →keep-alive
Content-Length →0
Date →Mon, 12 Jun 2017 13:19:54 GMT
Server →WildFly/10
Vary →Origin
X-Powered-By →Undertow/1
The REST Service
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response authenticateUser(CredentialsDTO credentialsDTO) {
try {
authService.login(credentialsDTO.getUsername(), credentialsDTO.getPassword());
} catch (WrongCredentialsException e) {
return Response.status(Status.FORBIDDEN).entity("WrongCredentialsException").build();
}
// Issue token
String token = issueToken(credentialsDTO.getUsername());
// Return the token on the response
return Response.ok().header(AUTHORIZATION, "Bearer " + token).build();
}
Why can't I see the headers in chrome ?
UPDATE
I am also using a CORSFilter that allowed Javascript to contact my backend in the first place. This is how it is configured in my web.xml
<filter>
<filter-name>CORS</filter-name>
<filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>
<init-param>
<param-name>cors.allowGenericHttpRequests</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>cors.allowOrigin</param-name>
<param-value>*</param-value>
</init-param>
<init-param>
<param-name>cors.allowSubdomains</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>cors.supportedMethods</param-name>
<param-value>GET, HEAD, POST, DELETE, OPTIONS</param-value>
</init-param>
<init-param>
<param-name>cors.supportedHeaders</param-name>
<param-value>*</param-value>
</init-param>
<init-param>
<param-name>cors.supportsCredentials</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>cors.maxAge</param-name>
<param-value>3600</param-value>
</init-param>
</filter>
<filter-mapping>
<!-- CORS Filter mapping -->
<filter-name>CORS</filter-name>
<url-pattern>*</url-pattern>
</filter-mapping>
Although I thought it was configured to allow everything, I am not sure if this is related to my problem.
By default CORS responses only exposes these 6 headers to scripts:
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
To allow scripts to access other headers sent by the sever, the server needs to send the Access-Control-Expose-Headers Header.
The Access-Control-Expose-Headers response header indicates which headers can be exposed to scripts as part of the response by listing their names.
eg: Access-Control-Expose-Headers: Authorization, X-Foobar
You can tweak your web.xml file including this to allow the Authorization header to be accessed from the script that made the XHR:
<init-param>
<param-name>cors.exposedHeaders</param-name>
<param-value>Authorization</param-value>
<init-param>
I used the following solution to allow my custom header in Owin self-hosted web api
using Owin;
using Microsoft.Owin.Cors;
using System.Threading.Tasks;
using System.Web.Cors;
namespace App.Web.App_Start
{
public static class CorsConfig
{
public static void Configure(IAppBuilder app)
{
var allowedOrigins = "comma,separated,list,of,origins";
var corsPolicy = new CorsPolicy
{
AllowAnyHeader = true,
AllowAnyMethod = true,
AllowAnyOrigin = false,
SupportsCredentials = true
};
corsPolicy.ExposedHeaders.Add("Custom-Header");
foreach (string origin in allowedOrigins.Split(','))
corsPolicy.Origins.Add(origin);
app.UseCors(new CorsOptions
{
PolicyProvider = new CorsPolicyProvider
{
PolicyResolver = context => Task.FromResult(corsPolicy)
}
});
}
}
}
I call the method like this CorsConfig.Configure(app); // app is Owin.IAppBuilder
The headers in an HttpResponse object are lazy-loaded, so headers will appear to be empty until you force the values to be loaded. Try calling response.headers.keys() to see all available header names. By the way, this also forces all values to be loaded into the map response.headers.headers.
Be sure to use headers.get('my-header-name') if you only need to access one particular header value.
Related
I am trying to retrieve data from 3rd party xml api through jquery ajax call. But it is showing Access Control Allow Origin CORS error. After some twiking I a getting ERR_CONNECTION_RESET error on Chrome.
This is the the Api link from where I am trying to retrieve data
http://202.61.117.90/GRIPS/ChallanDetails/query.do?GRN_NO=192021225551499528
This is my own server url to access the api
http://103.240.91.230:8080/CRM/checkApi.do?method=checkApi
Below is the html/jquery code i am using
<script>
$(document).ready(function(){
$.ajax({
type: "GET" ,
url: "http://202.61.117.90/GRIPS/ChallanDetails/query.do?GRN_NO=192021225551499528" ,
headers: { 'Access-Control-Allow-Origin': 'http://202.61.117.90/*' },
dataType: "xml" ,
success: function(xml) {
$(xml).find('GRIPS_EPAYMENT').each(function(){
var grnNo = $(this).find('GRN_NO').text();
var grnDate = $(this).find('GRN_DATE').text();
var brnNo = $(this).find('BRN_NO').text();
$("#grndate").append(grnDate);
$("#brnno").append(brnNo);
});
}
});
});
</script>
I am using tomcat 8.5 and added following filter in web.xml
<filter>
<filter-name>CorsFilter</filter-name>
<filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CorsFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Note: when I am calling same api from my local server - it works fine
I have a Jersey REST interface that I want to send cookies in its responses to each request but no cookies are ever present in any response.
I have a feeling it's a CORS issue and I'm not sure what CORSResponseFilter configuration I need to allow cookies to be set and successfully sent in a response.
My project is built with SpringBoot under Kotlin.
Response code:
#POST
fun put(): Response {
val cookie = NewCookie(Cookie("RESERVATION", "TEST"),
"Session", 60 * 60, false)
return ok()
.cookie(cookie)
.entity(Result("OK", "Success"))
.build()
}
Filter code:
open class CORSResponseFilter : ContainerResponseFilter {
override fun filter(req: ContainerRequestContext?, res: ContainerResponseContext?) {
res?.headers?.add("Access-Control-Allow-Origin", "*")
res?.headers?.add("Access-Control-Allow-Methods", "POST, GET")
res?.headers?.add("Access-Control-Allow-Headers", "origin, content-type, accept, authorization")
res?.headers?.add("Access-Control-Expose-Headers", "Set-Cookie")
res?.headers?.add("Access-Control-Allow-Credentials", "true")
}
}
Requests are successful and the responses send otherwise as intended but all without any cookies. Prior to setting up the response filter I was able to receive cookies in each response.
Any help is appreciated.
im trying to integrate spring security with a custom angular 2 login, that is a specific endpoint of my app is protected with spring security, trying to access it will redirect to /login that is handled in angular 2. as things stands now i have no clue as to how to perform the login and grant access to the backend API once logged.
i am configuring spring security as follows:
#Override
protected void configure(final HttpSecurity http) throws Exception {
http
.csrf().disable()
.cors().and()
.authorizeRequests()
.antMatchers("/api/someEndpoint/**")
.hasRole(ADMIN_ROLE).and().formLogin()
.loginPage("/login").and().logout();
}
#Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
}
as I had the default login everything worked fine, but I have found myself unable to create a working angular 2 login integration.
I tried the following code in angular 2 to no avail:
login(loginDetails:Object) {
console.log(loginDetails)
const headers = new Headers({ 'Content-Type': 'application/json' });
const options = new RequestOptions({ headers: headers });
const body = JSON.stringify(loginDetails);
console.log(headers);
console.log(body);
return this.http.post(this.loginUrl, body, options)
}
as far as I know spring security defaults for username and password variable names are "username" and "password", which i am sure are being passed in the request body so when passing some invalid user data like {"username":"admin", "password" : "pass"}I should be redirected to /login?error or something, and when successfully authenticated I should be redirected to /welcome and stay authenticated
I have the user and pass defined in my db and my custom userDetailsService checks against it
any answers, comments or questions are welcome
Once you're working with an API you've to use the HTTP Basic authentication.
It's also required to use HTTPS to prevent the main-in-middle attack.
To implement HTTP Basic with Angular the login service would look like this:
login (loginDetails: any): Observable<LoginResponse> { // custom class, may be empty for now
let headers = new Headers({
'Authorization': 'Basic ' + btoa(loginDetails.login + ':' + loginDetails.pass),
'X-Requested-With': 'XMLHttpRequest' // to suppress 401 browser popup
});
let options = new RequestOptions({
headers: headers
});
return this.http.post(this.loginUrl, {}, options)
.catch(e => this.handleError(e)); // handle 401 error - bad credentials
}
... then you subscribe this in the caller component:
loginNow() {
this
.loginService
.login(this.loginDetails)
.subscribe(next => {
this.router.navigateByUrl("/"); // login succeed
}, error => {
this.error = "Bad credentials"; // or extract smth from <error> object
});
}
Then you can use the loginNow() method inside component templates like (click)="loginNow().
As soon as the server will accept an authorization, JSESSIONID will be stored in your browser automatically because of Spring Security features and you won't be forced to send the credentials each time you access private resources.
Your login server method may look like this:
#PreAuthorize("hasRole('USER')")
#PostMapping("/login")
public ResponseEntity login() {
return new ResponseEntity<>(HttpStatus.OK);
}
... it would reject with 401 UNAUTHORIZED when the authorization fails or accept with 200 SUCCESS when it's not.
How to setup a server in the proper way there's a number of Spring Security demo projects present: https://github.com/spring-guides/tut-spring-security-and-angular-js
Your spring security config needs to look like this
http!!
.cors().and()
.csrf().disable()
.authorizeRequests()
.requestMatchers(object: RequestMatcher {
override fun matches(request: HttpServletRequest?): Boolean {
return CorsUtils.isCorsRequest(request)
}
}).permitAll()
.antMatchers("/api/**").authenticated()
.anyRequest().permitAll()
.and()
.formLogin().permitAll()
I had a similar issue, but I had to override the successlogout handler as mentioned here.
I'm using Spring Data Neo4j Rest backend served on Tomcat (8.0.27) with Angular frontend, all served locally at http://localhost:8080/. When I try to delete a record with either $http.delete() or with $resource().delete(), tomcat returns a 405 not supported error.
However, using $http({method: 'DELETE', url: '/competitors/13'}) works fine. Unfortunately, this does not allow me to use the angular $resource service instead of $http, which is my goal. (As an aside, PUT creates the same 405 response as DELETE)
I am also able to use DELETE from command line. This works fine:
curl -X DELETE http://localhost:8080/competitors/13
I have disable csrf in WebSecurityConfig in case that has anything to do with it.
I have also added readonly init-param to web.xml to try to force tomcat to accept DELETE, like so:
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>Proto v2 Application</display-name>
<servlet>
<servlet-name>proto</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>readonly</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>proto</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
Since I've verified that the server is able to perform the delete, I will omit all Java code. Here is my complete .js file with the angular module:
var magneticApp = angular.module('magneticApp', ['ngResource'])
.controller('defaultCtrl', function ($scope, $http, $resource, baseUrl) {
$scope.Competitor = $resource("/competitors/:id", {id: "#id"});
$scope.listItems = function () {
var query = $scope.Competitor.get(function () {
console.log('inside query');
});
query.$promise.then(
function successCallback(data) {
$scope.competitors = data._embedded.competitors;
},
function errorCallback(data) {
console.log('error:');
console.dir(data);
}
);
};
/*
* THIS DELETE FUNCTION RETURNS A 405 NOT SUPPORTED ERROR
*/
$scope.deleteItem = function (item) {
var query = $scope.Competitor.delete({}, item.itemId);
query.$promise.then(
function successCallback(data) {
console.log('success:');
$scope.competitors.splice($scope.competitors.indexOf(item), 1);
},
function errorCallback(data) {
console.log('error:');
console.dir(data);
}
);
};
/*
* THIS DELETE FUNCTION WORKS PROPERLY
*/
$scope.deleteItemWorksCorrectly = function (item) {
$http({method: 'DELETE', url: '/competitors/' + item.itemId})
.then(
function successCallback(data) {
$scope.competitors.splice($scope.competitors.indexOf(item), 1);
},
function errorCallback(data) {
console.log('delete error');
console.dir(data);
}
);
};
$scope.adjustActive = function (item) {
item.id = item.itemId;
console.log('adjustActive');
console.dir(item);
$http.post('/competitors', item)
.success(function (data) {
$http.get('/competitors').success(function (data) {
console.dir(data._embedded.competitors);
$scope.competitors = data._embedded.competitors;
});
});
};
$scope.addItem = function (itemName) {
var item = {name: itemName};
$http.post('/competitors', item)
.success(function (data) {
$http.get('/competitors').success(function (data) {
console.dir(data._embedded.competitors);
$scope.competitors = data._embedded.competitors;
//magneticApp.controller('defaultCtrl').$scope.competitors = data._embedded.competitors;
});
});
};
$scope.listItems();
});
Can anyone shed some light on this? Thanks.
I am working on ExtJS and need to access the REST services. I tried doing that with Spring's MVC REST supporting functionality and it worked well.
Now its kind of mandatory that I have to go with Jersey(JAX-RS). When tried with Jersey, I keep getting 404 error. I think the annotations, URL mapping, etc are fine (since the similar ones worked for Spring)
Below are the relevant code snippets.
Web.xml
<servlet>
<servlet-name>spring-jersey</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>com.myProducts.controller</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring-jersey</servlet-name>
<url-pattern>/jersey/*</url-pattern>
</servlet-mapping>
App.js
Ext.onReady(function() {
var store = Ext.create('Ext.data.Store', {
autoLoad: true,
autoSync: true,
model: 'Product',
proxy: {
type: 'rest',
url: 'products',
format: 'json',
headers: {
'Content-Type': 'application/json'
},
reader: {
type: 'json',
root: 'data'
},
writer: {
type: 'json'
},
api: {
create: 'products/createJ/',
read: 'products/readJ/',
update: 'products/editJ/',
destroy: 'products/deleteJ/'
}
}
});
Controller :
#Component
#Path("products/jersey/products")
public class JerseyProductsController {
#Autowired
private ProductService productService;
#POST
#Path("createJ/{id}")
#Consumes(MediaType.APPLICATION_JSON_VALUE)
#Produces(MediaType.APPLICATION_JSON_VALUE)
public Product createJ(#PathParam("id") int id, #RequestBody Product myProduct) {
myProduct.setId(id);
return productService.create(myProduct);
}
#PUT
#Path("editJ/{id}")
#Consumes(MediaType.APPLICATION_JSON_VALUE)
#Produces(MediaType.APPLICATION_JSON_VALUE)
public Product editJ(#PathParam("id") int id, #RequestBody Product myProduct) {
myProduct.setId(id);
return productService.update(myProduct);
}
#DELETE
#Path("deleteJ/{id}")
#Consumes(MediaType.APPLICATION_JSON_VALUE)
#Produces(MediaType.APPLICATION_JSON_VALUE)
public Product deleteJ(#PathParam("id") int id) {
return productService.delete(id);
}
#GET
#Path("readJ/")
#Consumes(MediaType.APPLICATION_JSON_VALUE)
#Produces(MediaType.APPLICATION_JSON_VALUE)
public List<Product> allProductsJ() {
return productService.getAll();
}
}
I get 404 for below URL:
Request URL:http://localhost:4080/MyProducts/products/jersey/products/readJ/.json?_dc=1407930853131&page=1&start=0&limit=25
Request Method:GET
Status Code:404 Not Found
Kindly let me know what is being missed.
You may need to use /jersey/products/jersey in your URL since you are having <url-pattern>/jersey/*</url-pattern>
Not sure what's the issue but you could try below one :
<url-pattern>*.do</url-pattern>
I believe its because it could capture requests ending with .do and it reduced the headache of having some pattern in between the URL.
I think your servlet mapping is incorrect. Your Url-Pattern is /jersey/* but in your url is /MyProducts/*
put this in your web.xml
<servlet-mapping>
<servlet-name>spring-jersey</servlet-name>
<url-pattern>/MyProducts/*</url-pattern>