This question already has answers here:
How to create cross-domain request?
(7 answers)
Closed 5 years ago.
My project uses spring at the back-end and angular2 at the front-end. I have a json file under webapp folder of spring project. And I am trying to access it from angular.
I am able to access the file if I just type "http://localhost:8080/project1/test.json"
But if I use the same link from angular, I am getting an error message saying "no access control allow origin header"
My angular code :
1. Function getJson() defined in service.ts:
getJson(){
return this.http.get('http://localhost:8080/project1/test.json')
.map((response:Response) => response.json());
}
Calling getJson():
results=[];
this._manoService.getJson().subscribe(resJsonData => this.results =
resJsonData);
I created proxy.conf.json and added the following lines:
{
"/project1": {
"target": "http://localhost:8080",
"secure": false
}
}
And also added "start": "ng serve --proxy-config proxy.conf.json", to package.json
Still I am getting the same issue. Am I doing anything wrong?
For security reasons, browsers enforce a same origin policy. Your angular page is on an origin other than localhost:8080 (most likely localhost:3000). So the browser is disallowing access.
The SOP is a very important concept of the web. For example you may be logged in to your bank account and then open another website. The SOP prevents that website from accessing your bank account.
There are several ways to grant cross origin access.
By far the easiest way, is to put everything onto the same origin. That is provide the Angular application on the same domain and port as the service for production. For development, you can configure ng serve to act as a proxy server. Thus you will make your service request to http://localhost:3000/project1/test.json and let ng service forward it to localhost:8080. This is explained in detail in Angular proxy documentation.
If you need cross origin request even in production, Spring makes it relatively easy to allow that: You need toannotate your service methods with #CrossOrigin as explained in the Spring REST tutorial. In this case authentication via cookie will not work any longer. In case you need authentication, you should look into oauth. Adding the #CrossOrigin annotication to your service method, will make Spring-REST add the Access-Control-Allow-Origin http-header.
Another alternative to using proper CORS is to use jsonp, but this is a hack that should be avoided nowadays. JSONP exploits use of the fact, that you can include a <script> tag to any origin and the provided javascript is downloaded and execute. So the idea behind JSONP is that you define a callback function and the server will generate and return JavaScript code that invokes this function with the actual data as parameter.
Let's say that the service is implemented by a Spring #RestController and you need to consume it throught a web site that is outside of the server where the rest service is running. In that scenario just add a #CrossOrigin annotation to the handler method.
For example (in this example the #CrossOrigin is enable only to the addSite handler method.
#RestController
public class SiteController {
#Autowired
private SiteServiceImpl siteService;
#CrossOrigin(origins = "*")
#RequestMapping(method = RequestMethod.POST, value = "/api/sites")
public void addSite(#RequestBody Site site){
siteService.addSite(site);
}
But also you can enable #CrossOrigin to the whole controller enabling the #CrossOrigin at the #RestController level.
For example:
#RestController
#CrossOrigin(origins = "*")
public class SiteController {
#Autowired
private SiteServiceImpl siteService;
#RequestMapping(method = RequestMethod.POST, value = "/api/sites")
public void addSite(#RequestBody Site site){
siteService.addSite(site);
}
#CrossOrigin(origins = "*") annotation allows to all the resources that are outside of the rest service to consume it, in the case that the service is only enabled to be consumed for resources that comes from one specific origin then just change the origins value to the server name where the resources resides, for example #CrossOrigin(origins = "http://myothersite.com")
There is more related information at the Spring site: cors-support-in-spring-framework
Related
I'm recently working with microservices, developed as Spring Boot applications (v 2.2) and in my company we're using Keycloak as authorization server.
We chose it because we need complex policies, roles and groups, and we also need the User Managed Authorization (UMA) to share resources between users.
We configured Keycloak with a single realm and many clients (one client per microservice).
Now, I understand that I need to explicitly define Resources within Keycloak and this is fine, but the question is: do I really need to duplicate all of them in my microservice's property file?
All the documentation, examples and tutorials end up with the same thing, that is something like:
keycloak.policy-enforcer-config.enforcement-mode=PERMISSIVE
keycloak.policy-enforcer-config.paths[0].name=Car Resource
keycloak.policy-enforcer-config.paths[0].path=/cars/create
keycloak.policy-enforcer-config.paths[0].scopes[0]=car:create
keycloak.policy-enforcer-config.paths[1].path=/cars/{id}
keycloak.policy-enforcer-config.paths[1].methods[0].method=GET
keycloak.policy-enforcer-config.paths[1].methods[0].scopes[0]=car:view-detail
keycloak.policy-enforcer-config.paths[1].methods[1].method=DELETE
keycloak.policy-enforcer-config.paths[1].methods[1].scopes[0]=car:delete
(this second example fits better our case because it also uses different authorization scopes per http method).
In real life each microservice we're developing has dozens of endpoints and define them one by one seems to me a waste of time and a weakness in the code's robustness: we change an endpoint, we need to reconfigure it in both Keycloak and the application properties.
Is there a way to use some kind of annotation at Controller level? Something like the following pseudo-code:
#RestController
#RequestMapping("/foo")
public class MyController {
#GetMapping
#KeycloakPolicy(scope = "foo:view")
public ResponseEntity<String> foo() {
...
}
#PostMapping
#KeycloakPolicy(scope = "bar:create")
public ResponseEntity<String> bar() {
...
}
}
In the end, I developed my own project that provides auto-configuration capabilities to a spring-boot project that needs to work as a resource server.
The project is released under MIT2 license and it's available on my github:
keycloak-resource-autoconf
I have REST API with Java Springboot 2.1.5.RELEASE and need to prevent every controller to check first the RequestBody contain XSS or not. if yes I want to return like BadRequest. The App only for API no HTML, JSP and etc
Here is sample my controller
#PostMapping("/test1")
public ResponseEntity<ResponseModel> process(#Valid #RequestBody Student request)
I've tried with some library to check Student to Json and clean the XSS like https://github.com/OWASP/json-sanitizer/ and etc
but still, doesn't work
and Already check https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html
but still not found about implement in java api
I expect every request to API reject/return 400(BadRequest)
The validation can be on Controller, Filter, Interceptor or Spring Security
I am confused about how an infinite loop of feign calls might behave.
An example:
Assume I have 2 APIs, A & B.
if I call API A, which in turn calls API B via a feign HTTP call, which in turn calls API A again via feign, will it recognize this and break the call chain?
Quick flowchart of calls:
A -> B -> A -> B ... Repeat infinitely?
I have not tried this code, it is just an idea。
But I am assuming that spring-cloud-starter-feign will provide some methods to resolve this problem? Is this assumption correct?
#PostMapping(RestJsonPath.API_A)
ResponseEntity<byte[]> apiA();
#PostMapping(RestJsonPath.API_B)
ResponseEntity<byte[]> apiB();
Will it execute until it times out or hystrix will stop it?
TL;DR:
Feign will keep the connection open on the initial request from A to B until the pre-configured timeout kicks in. At this point, Feign will time out the request and if you have specified a Hystrix fallback, Spring will use your Hystrix fallback as the response.
Explanation:
spring-boot-starter-feign provides an abstraction layer for writing the HTTP request code. It will not handle potential loops or cycles in your code.
Here is an example spring boot feign client from their tutorials website for demonstration:
#FeignClient(value = "jplaceholder",
url = "https://jsonplaceholder.typicode.com/",
configuration = ClientConfiguration.class,
fallback = JSONPlaceHolderFallback.class)
public interface JSONPlaceHolderClient {
#RequestMapping(method = RequestMethod.GET, value = "/posts")
List<Post> getPosts();
#RequestMapping(method = RequestMethod.GET, value = "/posts/{postId}", produces = "application/json")
Post getPostById(#PathVariable("postId") Long postId);
}
Notice first that this is an interface - all the code is auto generated by Spring at startup time, and that code will make RESTful requests to the urls configured via the annotations. For instance, the 2nd request allows us to pass in a path variable, which Spring will ensure makes it on the URL path of the outbound request.
The important thing to stress here is that this interface is only responsible for the HTTP calls, not any potential loops. Logic using this interface (which I can inject to any other Spring Bean as I would any other Spring Bean), is up to you the developer.
Github repo where this example came from.
Spring Boot Docs on spring-boot-starter-openfeign.
Hope this helps you understand the purpose of the openfeign project, and helps you understand that it's up to you to deal with cycles and infinite loops in your application code.
As for Hystrix, that framework comes in to play (if it is enabled) only if one of these generated HTTP requests fails, whether it's a timeout, 4xx error, 5xx error, or a response deserialization error. You configure Hystrix, as a sensible default or fallback for when the HTTP request fails.
This is a decent tutorial on Hystrix.
Some points to call out is that a Hystrix fallback must implement your Feign client interface, and you must specify this class as your Hysterix fallback in the #FeignClient annotation. Spring and Hystrix will call your Hystrix class automatically if a Feign request fails.
I am using Angular6 as FrontEnd running on http:// localhost:4200 and Spring Boot (which has in built Tomcat server) as backend (exposing a GET Rest API) running on https:// localhost:8083/ldap . When I run this, I am getting CORS policy error on the browser. So I searched on internet and tried multiple fixes which were suggested on internet. I am not sure what I am missing on each of the solution below.
Unsuccessful Fix 1: I tried to run it via proxy.
-> Created a proxy.config.json in parallel to package.json with below
content.
{
"/ldap/": {
"target": "https://localhost:8083",
"secure": false,
"logLevel": "debug",
"changeOrigin": true
}
}
-> Added below entry in package.json inside script block there.
"start":"ng serve --proxy-config proxy.config.json",
-> In the service class, tried calling my spring boot backend rest API
like below.
return this.http.get('/ldap');
Now when I run my app, I got below error:
GET http:// localhost:4200/ldap 404 (Not Found) : zone.js:3243
Unsuccessful Fix 2: I added below headers before calling the Rest API in my frontend.
getUserAuthenticatedFromLDAP() {
const httpOptions = {
headers: new HttpHeaders({
'crossDomain': 'true',
'mode' : 'cors',
'allowCredentials': 'true',
'origins': '',
'allowedHeaders': '',
'Access-Control-Allow-Origin': '',
'Access-Control-Allow-Methods': 'GET',
'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept',
'Access-Control-Max-Age': '86400'
})
};
return this.http.get('https://localhost:8083' , httpOptions);
}
Access to XMLHttpRequest at 'https://localhost:8083/' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
Unsuccessful Fix 3: Rather than making changes at front end, I tried to make changes at API level by adding below code at controller level.
#Controller
#CrossOrigin(origins = "http://localhost:4200")
public class WelcomeController {
// This is for LDAP Authentication
#GetMapping("/ldap")
#ResponseBody
public Authentication hello() {
return LdapSecurity.getAuthentication();
}
}
Here I am getting below error again:
Access to XMLHttpRequest at 'https://localhost:8083/' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
More unsuccessful fixes:
I even tried to change headers of Tomcat using application.properties file but could not find sufficient example or piece of code to make any change.
On internet, some people suggested to implement filter on API level but I am not sure that in whcih class I need to add those overriden filter method. I am stuck in this issue for last 2 days.
PS: I see some people have implemented CORS filter at API layer or implemented class like below. Now my question is if I implement below class then where do I need to refer this class ? Is it web.xml or any other place.
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
I was using Spring security feature for LDAP authentication. But now I removed Spring security feature for LDAP and used a basic program in java to make a connection with LDAP. After that I used CrossOrigin tag in controller layer and now I am not getting CORS issue. Thanks you all for your help.
please have a look here, you need to enable cors in your method/controller
#CrossOrigin(origins = "http://localhost:9000")
#GetMapping("/greeting")
public Greeting greeting(#RequestParam(required=false, defaultValue="World") String name) {
System.out.println("==== in greeting ====");
return new Greeting(counter.incrementAndGet(), String.format(template, name));
}
I have a java web application which publish a service that returns an object in JSON format, and I have another java web app just to consume that service through a JSONP call and show the response. In my local machine it's working fine, but now that I want to test it in a web environment (Layershift in my case), I can't get the JSON object. I don't see any errors on Chrome developer tools, but when I look into the Response tab (in Network option) I see the source code of the login page of my application. Let me show you the my code
Controller with the service:
#RestController
public class MyController {
#RequestMapping(value="/myservice/get/{somevar}")
public MappingJacksonValue getMyObject (#RequestParam String callback, #PathVariable String somevar, HttpServletRequest request, HttpServletResponse response) {
MyObject obj = new MyObject();
//some logic
MappingJacksonValue value = new MappingJacksonValue(obj);
value.setJsonpFunction(callback);
return value;
}
}
javascript code for call the service:
$.fn.callWithJsonP = function(somevar) {
var url = "/myservice/get/" + somevar + "?callback=myCallback";
$.getJSON(url, function(data) {
if (data.value.status == "OK") {
//shows the data contained
}
});
}
Apache configuration (added to avoid CORS error), present in both applications
Header add Access-Control-Allow-Origin "*"
Header add Access-Control-Allow-Headers "origin, x-requested-with, content-type"
Header add Access-Control-Allow-Methods "PUT, GET, POST, DELETE, OPTIONS"
Header always unset X-Frame-Options
This is working perfectly on muy local machine (except for the Apache, I don't use it locally), but as I said, on a web environment I receive the source code of my login page. In the Headers tab I can see the headers added in Apache, and the status code of the response is OK, any clues about what's goin on?
Kind regards
UPDATEI've removed the Apache web server, and even tested the web service with Postman (meaning, no second application), and still the same result. I've tried changing #RestController for #Controller and returning an instance of MyObject (instead of MappingJacksonValue), with no results, please help me
I am probably way off here, but is it possible that you have a servlet filter or other part of your web app config that is routing your get request to your login page before your REST framework is able to map it to your endpoint? I use Jersey for this usually, so I am unfamiliar with Spring's RestController, but I assume it is doing similar thing - routes URLs that match to the java code. If you are seeing login page in response it sounds like something is interfering and trying to force user to login before Spring directs to your endpoint method.
It seems like you have a fallback set in your server, maybe configured for a single page application. This is normally used for routing with HTML5 mode using routers like angular's.
you are getting the login page code as response because the login is failed and you are redirected to the same page.. so before calling the service first you need to do the authentication and then by using the authentication token call the service..