CXF 3.2.2 CORS failed with "No 'Access-Control-Allow-Origin' header is present" - spring-boot

I was developing a Restful service using CXF + spring boot. and had problems with CORS setting
I set CORS on service definition interface (GateRs)
...
#Service
#CrossOriginResourceSharing(
allowAllOrigins = true,
allowHeaders = {
"Accept", "Accept-Charset", "Accept-Encoding", "Accept-Datetime",
"Accept-Language", "Authorization", "Content-Language", "Content-Length",
"Content-Type", "Origin", "User-Agent"},
exposeHeaders = {
"Accept", "Accept-Charset", "Accept-Encoding", "Accept-Datetime",
"Accept-Language", "Authorization", "Content-Language", "Content-Length",
"Content-Type", "Origin", "User-Agent"},
allowCredentials = true,
maxAge = 1209600 )
public interface GateRs {
#POST
#Path("/require")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
#VoluntarySecure
public Res require(Req req);
...
implement interface with a class
#Component
public class Gate implements GateRs {
#Override
public Res require(Req req) {
...
attach the 'CrossOriginResourceSharingFilter' to the service bean
#Bean
public CrossOriginResourceSharingFilter cors() {
return new CrossOriginResourceSharingFilter();
}
#Bean(destroyMethod = "destroy") #DependsOn(Bus.DEFAULT_BUS_ID)
public Server jaxRsServer() {
final JAXRSServerFactoryBean factory = new JAXRSServerFactoryBean();
factory.setServiceBean(new Gate());
factory.setProviders(Arrays.asList(jackson(), cors()));
factory.setBus(springBus());
factory.setAddress("/Gate");
return factory.create();
}
...
the client is a mobile application based on Ionic, and emulated by Chrome browser.
the '/Gate/require' returned '404' becasue CORS failed with no "
Access-Control-Allow-Origin" header.
I looked into the detail; and found out that the preflight ('OPTIONS') actually succeed; the "POST" failed, triggered the above mentioned messages
the preflight(OPTIONS)
the POST failed; it appears that the server side had not presented 'Access-Control-Allow-Origin' header in the response message
does anyone has any idea or suggestion?
thanks

I finally solved the problem by changing the '#CrossOriginResourceSharing' as follows:
...
#CrossOriginResourceSharing(
allowAllOrigins = true,
allowCredentials = true,
maxAge = 1209600 )
public interface GateRs {
...

Related

Testing routingfunction with webmvc in springmvc in kotlin

I'm trying to test a routerfunction in webmvc using kotest and mockk. I think the way it's written that only the router function and the test itself should be executed. Everything else is mocked. The routerfunction is configured as follows:
#Configuration
class DownloadRoutes(private val dnldCtrllr : DownloadController,
) {
private var baseUrl : String = "download"
#Bean
fun router(): RouterFunction<ServerResponse> {
return router {
baseUrl.nest{
accept(MediaType.TEXT_PLAIN).nest {
"/asset_request".nest {
POST(dnldCtrllr::downloadPost)
}
}
}
}
}
}
The test uses the WebMvcTest annotation. I mock the POST handler so that if it is called at it's entry point, it simply returns a status of OK.
The test looks as follows:
#WebMvcTest
#ContextConfiguration(classes = [DownloadRoutes::class])
class DownloadRoutesTest( #Autowired val mockMvc : MockMvc,
#MockkBean val mockDwnLd : DownloadController,
#Autowired val ctx : ApplicationContext
) : DescribeSpec({
describe("Download Service Routes") {
it("should route POSTs to the appropriate handler") {
val bean = ctx.getBean("router")
println(bean.toString())
every { mockDwnLd.downloadPost(any())} returns ServerResponse.status(HttpStatus.OK).build()
mockMvc.perform(MockMvcRequestBuilders
.post("/download/asset_request")
.accept(MediaType(MediaType.TEXT_PLAIN))
)
.andDo(MockMvcResultHandlers.print()) // prints the request and response; for debugging only
.andExpect(MockMvcResultMatchers.status().isOk)
}
}
})
It doesn't pass. I've printed the router bean obtained from the application context to be sure it's there and I think it looks right. I also added a print to the mockMvc chain so I can see what happens.
Here's the prints:
/download => {
Accept: text/plain => {
/asset_request => {
POST -> org.springframework.web.servlet.function.RouterFunctionDslKt$sam$org_springframework_web_servlet_function_HandlerFunction$0#1d9488b
GET -> org.springframework.web.servlet.function.RouterFunctionDslKt$sam$org_springframework_web_servlet_function_HandlerFunction$0#dca44a2
}
}
}
MockHttpServletRequest:
HTTP Method = POST
Request URI = /download/asset_request
Parameters = {}
Headers = [Accept:"text/plain"]
Body = null
Session Attrs = {org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository.CSRF_TOKEN=org.springframework.security.web.csrf.DefaultCsrfToken#3840636a}
Handler:
Type = null
MockHttpServletResponse:
Status = 403
Error message = Forbidden
Headers = [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"]
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
Status expected:<200> but was:<403>
Expected :200
Actual :403
I'm assuming that the 403 means it's never getting to the router function. Does the "handler = null" mean that the router is not getting invoked (why)? Does the mockMvc not properly deal with the router function (as opposed to the old annotation methods)? I'm assuming it's the mocked DownloadController that's getting injected into the DownloadRoutes, but I'm not entirely convinced.
Anyone have any thoughts?
I should have paid more attention to the fact it was a 403 error. It wasn't saying it couldn't find the route. It was saying that I didn't have access to the route. That's because I had security enabled for the app (I had "spring-boot-starter-security" in the list of dependencies). I added
#AutoConfigureMockMvc(addFilters = false)
to the annotations. This prevents the security filters from being added and the test now passes. https://www.baeldung.com/spring-security-disable-profile may be an alternative.

Getting null body in response from feign client, even though a direct request is returning an entity

I have this Feign Client in my spring boot application :
#Component
#FeignClient(value = "apiKeyManager", url = "http://localhost:8081/", configuration = FileUploadConfiguration.class)
public interface ApiKeyClient {
#RequestMapping(method = RequestMethod.POST, value = "/api/public/getAppName", consumes = "application/json", produces = "application/json")
ResponseEntity getAppName(#RequestBody AppNameRequestDto appNameRequestDto);
}
And I have this code in my service, which calls it :
AppNameRequestDto request = new AppNameRequestDto(apiKey);
ResponseEntity verification = apiKeyClient.getAppName(request);
return verification;
The actual endpoint being called by the feign client looks like this :
#PostMapping(value = "getAppName", consumes = "application/json", produces = "application/json")
public ResponseEntity getAppName(#RequestBody AppNameRequestDto appNameRequestDto){
try {
return new ResponseEntity(apiKeyManagementService.getAppName(appNameRequestDto.getApiKey()), HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity("Failed to locate application by API_KEY : " + appNameRequestDto.getApiKey(), HttpStatus.NOT_FOUND);
}
}
When I run this code - I get this response :
{
"headers": {
<REMOVED FOR BREVITY>
},
"body": null,
"statusCode": "OK",
"statusCodeValue": 200
}
But when I make the call to the underlying API directly, I get the response I am expecting - an entity with an accompanies 200 status :
{
"applicationName": "MyNewFileUploadServiceApplication6"
}

How to handle CORS error with an angular app and a java backend?

I have an angular App that tries to send ajax requests to a java backend (API built with Jersey).
Here is the client request :
let settings = {
"url": "http://localhost:8080/simulator/config",
"method": "POST",
"timeout": 0,
"headers": {
"Content-Type": "application/json"
},
"data": JSON.stringify({
"fromDate": fromDate,
"tsIntervalTag": tsIntervalTag,
"tsIntervalDevice": tsIntervalDevice,
"devicePerMinute": devicePerMinute,
"tagPerMinute": tagPerMinute,
"quantityOfTags": quantityOfTags,
"quantityOfDevices": quantityOfDevices
}),
};
$.ajax(settings).done(function (response) {
console.log(response);
});
And here is the java backend request handler:
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response postIt(String body) {
try {
//do stuff
return Response.ok(body, MediaType.APPLICATION_JSON).header("Access-Control-Allow-Origin", "*").build();
} catch (Exception e) {
return Response.serverError().entity(e).header("Access-Control-Allow-Origin", "*").build();
}
}
As you can see, as nearly all answers about CORS state, the Access-Control-Allow-Origin header is set to * wildcard, so from my understanding, the response should get back to the browser.
But the browser console returns the following:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading
the remote resource at http://localhost:8080/simulator/config.
(Reason: CORS header ‘Access-Control-Allow-Origin’ missing).
Cross-Origin Request Blocked: The Same Origin Policy disallows reading
the remote resource at http://localhost:8080/simulator/config.
(Reason: CORS request did not succeed).
So what did I miss ?
It seems like with Jersey, it is better to use filters to set CORS. (At least in my scenario where all requests should be accepted from anywhere (so far)).
So I created the following class (in its own java file):
package com.example;
import java.io.IOException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.Provider;
#Provider
public class CORSFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
throws IOException {
System.out.println("FILTER HERE ");
MultivaluedMap<String, Object> headers = responseContext.getHeaders();
headers.add("Access-Control-Allow-Origin", "*"); // Allow Access from everywhere
headers.add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
headers.add("Access-Control-Allow-Headers", "X-Requested-With, Content-Type");
}
}
And that's is basically it. All requests will go through this and get the correct headers. I do not know why setting the headers in method directly did not work though.

Spring Boot with basic authentication and cors

I'm working on learning spring boot, and I have some questions about basic authentication and cors.
I created two pages and ajax to backend side.
The first page ajax username and password to backend and the method is POST.
Besides, it used basic authentication.
If its successes, the first page will redirect to the second page.
The second page will ajax to backend after the second page was loaded.
It uses GET and it will get no data except HTTP.Status.
This is my ajax function in the first page.
function login () {
var username = document.getElementById("username").value;
var password = document.getElementById("password").value;
alert(btoa(username + ":" + password));
var settings = {
"async": true,
"crossDomain": true,
"url": "http://localhost:10000/login",
"method": "POST",
"headers": {
"content-type": "application/json",
"accept": "application/json",
"authorization": "Basic " + btoa(username + ":" + password),
"cache-control": "no-cache",
}
}
alert(settings);
$.ajax(settings).done(function (response) {
console.log(response);
localStorage.setItem("token", btoa(username + ":" + password));
window.location = "file:///home/cyl/SecurityTest/pages/getEmployeePage.html"
});
}
This is my ajax function in the second page.
function getData () {
alert(localStorage.getItem("token"));
var settings = {
"async": true,
"crossDomain": true,
"url": "http://localhost:10000/getAllEmployee",
"method": "GET",
"headers": {
"authorization": "Basic " + localStorage.getItem("token"),
"accept": "application/json",
"content-type": "application/json",
"cache-control": "no-cache"
}
}
$.ajax(settings).done(function (response, textStatus, xhr) {
console.log(response);
});
}
This is my RestController
#RestController
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class EmployeeController {
#CrossOrigin(origins="*", allowedHeaders = "*")
#PostMapping(path = "/login")
public ResponseEntity<String> login() {
return new ResponseEntity<String>(HttpStatus.ACCEPTED);
}
#CrossOrigin(origins="*", allowedHeaders = "*")
#GetMapping(path = "/getAllEmployee")
public ResponseEntity<String> getAllEmployee() {
//List<Employee> employeeList = this.employeeDAO.getAllEmployee();
return new ResponseEntity<String>(HttpStatus.OK);
}
}
CorsConfig
#Configuration
public class CorsConfig implements WebMvcConfigurer {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST")
.allowCredentials(true);
}
}
But at the second page step, I got an error
"Access to XMLHttpRequest at 'http://localhost:10000/getAllEmployee' from origin 'null' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status."
I can't deal with this problem despite I search some relevant issues.
Except for this question, The way I store the authentication token in the client side is a right way?
If not, how can I do that?
Thanks!
you can use this Spring annotation if you run the same spring project on your local machine and the JS project with this tag will allow you to access the rest service
#CrossOrigin(origins = "*", maxAge = 3600)
public class controllerRest{}
regards!

swagger doesn't recognize api description

I instatiate docket like this
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com.config.internal"))
.paths(Predicates.or(PathSelectors.ant("/api**/**")))
.build();
}
I created a set of stub endpoints that imitate the real one for /login or /oauth.
#Api("Authentication")
#RequestMapping("/api")
public interface LoginEndpointApi {
#ApiOperation(value = "Github SSO endpoint", notes = "Endpoint for Github SSO authentication")
#ApiResponses({
#ApiResponse(code = 200, message = "HTML page of main application")
})
#GetMapping("/oauth/github")
default void oauthGithub() {
throw new UnsupportedOperationException();
}
#ApiOperation(value = "Get CSRF token", notes = "Returns current CSRF token")
#ApiResponses({
#ApiResponse(code = 200, message = "CSRF token response", response = String.class,
examples = #Example({#ExampleProperty(value = "015275eb-293d-4ce9-ba07-ff5e1c348092")}))
})
#GetMapping("/csrf-token")
default void csrfToken() {
throw new UnsupportedOperationException();
}
#ApiOperation(value = "Login endpoint", notes = "Login endpoint for authorization")
#ApiResponses({
#ApiResponse(code = 200, message = "Successful authentication")
})
#PostMapping("/login")
default void login(
#ApiParam(required = true, name = "login", value = "login body")
#RequestBody LoginRequest loginRequest) {
throw new UnsupportedOperationException();
}
}
But it doesn't recognize it. It is located in the same com.config.internal package as I described.
But the page swagger ui is empty and shows that No operations defined in spec!
What is the problem?
If you want to provide swagger documentation for your request mappings specified above you could simply describe it with .paths(Predicates.or(PathSelectors.ant("/api/**"))) path matchers. But if your path includes something more complicated like api + text without backslash separator then you should get known with
https://docs.spring.io/spring/docs/3.1.x/javadoc-api/org/springframework/util/AntPathMatcher.html

Resources