A typical SOAP client request using JAX-WS might be
FooService service = new FooService();
FooPort port = service.getFooPort();
FooPayload payload = new FooPayload();
payload.setHatSize(3);
payload.setAlias("The Hat");
...
port.processRequest(payload);
This generates an HTTP request content something like
<?xml ... ?>
<S:Envelope xmlns:S="http://...soap-envelope">
<S:Body>
<!-- payload -->
</S:Body>
</S:Envelope>
By manipulating the arguments to the port.processRequest() call you can only affect the "payload" part. You can't affect the outer part of the XML message.
I want to insert a SOAP header just before the SOAP Body
<S:Header>
<X:Security xmlns:X="http://...wsssecurity...>
<X:BinarySecurityToken>kjh...897=</X:BinarySecurityToken>
</X:Security>
</S:Header>
How do I do that?
Thanks Nuno,
Just as soon as I work out how to log in properly to stackoverflow.com I'll do the right thing with your reply.
In the mean time here's the code I ended up with:
FooService service = new FooService();
service.setHandlerResolver(new HandlerResolver() {
public List<Handler> getHandlerChain(PortInfo portInfo) {
List<Handler> handlerList = new ArrayList<Handler>();
handlerList.add(new RGBSOAPHandler());
return handlerList;
}
});
FooPort port = service.getFooPort();
FooPayload payload = new FooPayload();
payload.setHatSize(3);
payload.setAlias("The Hat");
...
port.processRequest(payload);
and
class RGBSOAPHandler implements SOAPHandler<SOAPMessageContext> {
public Set<QName> getHeaders() {
return new TreeSet();
}
public boolean handleMessage(SOAPMessageContext context) {
Boolean outboundProperty =
(Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outboundProperty.booleanValue()) {
SOAPMessage message = context.getMessage();
try {
SOAPEnvelope envelope = context.getMessage()
.getSOAPPart().getEnvelope();
SOAPFactory factory = SOAPFactory.newInstance();
String prefix = "X";
String uri = "http://...wsssecurity...";
SOAPElement securityElem =
factory.createElement("Security",prefix,uri);
SOAPElement tokenElem =
factory.createElement("BinarySecurityToken",prefix,uri);
tokenElem.addTextNode("kjh...897=");
securityElem.addChildElement(tokenElem);
SOAPHeader header = envelope.addHeader();
header.addChildElement(securityElem);
} catch (Exception e) {
System.out.println("Exception in handler: " + e);
}
} else {
// inbound
}
return true;
}
public boolean handleFault(SOAPMessageContext context) {
throw new UnsupportedOperationException("Not supported yet.");
}
public void close(MessageContext context) {
//
}
}
you might want to look at handlers and handler chains.- I recently had to add a cookie to a given Webservice call and that was how i did it, just created a handler that intercepted the initial call and injected the cookie, you can also manipulate the call headers with a Pivot Handler
for add Soap header, if you implement the WS on the web application server, the Was will add security part at header , after you have configure as per WS-SECURITY standard , such as web-policy etc. I don't understand why need add yourself except the encrypted content part , such as encrypted password etc
Related
Here is my csrf and cors handler of my vertx application
#Log4j2
public class CsrfVerticle extends AbstractVerticle {
private final Set<HttpMethod> httpMethodSet =
new HashSet<>(Arrays.asList(GET, POST, OPTIONS, PUT, DELETE, HEAD));
private final Set<String> headerSet = new HashSet<>(
Arrays.asList("Content-Type", "Authorization", "Origin", "Accept", "X-Requested-With",
"Cookie", "X-XSRF-TOKEN"));
private Connection dbConnection;
private WebClient webClient;
private Vertx vertx;
public void start() throws Exception {
super.start();
HttpServer httpServer = TestService.vertx.createHttpServer();
Router router = Router.router(TestService.vertx);
SessionStore store = LocalSessionStore.create(vertx);
SessionHandler sessionHandler = SessionHandler.create(store)
.setCookieSameSite(CookieSameSite.STRICT)
.setCookieHttpOnlyFlag(false);
router.route().handler(LoggerHandler.create());
if (TestService.serviceConfiguration.isEnableCSRF()) {
router.route()
.handler(CorsHandler.create("*").allowedMethods(httpMethodSet).allowedHeaders(headerSet)
.allowCredentials(true).addOrigin(TestService.serviceConfiguration.getFrontendUrl()));
router.route().handler(
CSRFHandler.create(vertx, csrfSecret()).setCookieHttpOnly(false))
.handler(sessionHandler);
} else {
router.route()
.handler(CorsHandler.create("*").allowedMethods(httpMethodSet).allowedHeaders(headerSet)
.allowCredentials(true)).handler(sessionHandler);
}
dbConnection = createConnection(TestService.serviceConfiguration.getJdbcConfig());
TestAuth testAuth = new TestAuth(TestService.serviceConfiguration.getUsername(),
TestService.serviceConfiguration.getPassword());
AuthenticationHandler basicAuthHandler = BasicAuthHandler.create(testAuth);
router.route("/student/*").handler(basicAuthHandler);
router.route("/student/add").method(HttpMethod.POST).handler(this::handleAddUser);
router.route("/student/get").method(HttpMethod.GET).handler(this::handleGetUser);
router.route("/student/delete").method(HttpMethod.DELETE)
.handler(this::handleDeleteUser);
router.route("/student/update").method(HttpMethod.PUT).handler(this::handleUpdateUser);
httpServer.requestHandler(router).listen(TestService.serviceConfiguration.getPort());
log.info("Console Server Verticle Started Successfully. Listening to {} port",
TestService.serviceConfiguration.getPort());
}
I am able to receive cookies in browser and send it back along with updated X-XSRF-TOKEN attached to the header
Everything works fine in my local but when deploying in VM I get the below error for all post requests
ctx.fail(403, new IllegalArgumentException("Token signature does not match"));
from csrf handler of vertx.
Here are the frontend code to add x-xsrf-token when sending requests to backend
createXsrfHeader(headers: HttpHeaders) {
let xsrfToken = Cookies.get('XSRF-TOKEN')
let sessionToken = Cookies.get('vertx-web.session')
if(xsrfToken)
headers = headers.append('X-XSRF-TOKEN', xsrfToken);
// if(xsrfToken && sessionToken)
// headers = headers.append('Cookie', `XSRF-TOKEN=${xsrfToken}; vertx-web.session=${sessionToken}`);
return headers;
}
[Adding header to post request]
callPostRequest(subUrl: string,reqData: any) {
let headers = new HttpHeaders();
headers = this.createAuthorizationHeader(headers);
headers = this.createXsrfHeader(headers);
return this.http.post<any>(this.basicApiUrl+subUrl, reqData, {
headers: headers,
withCredentials : true
}).pipe(map(resData => {
// console.log(resData);
return resData;
}));
}
[Adding header to put request]
callPutRequest(subUrl: string,reqData: any) {
let headers = new HttpHeaders();
headers = this.createAuthorizationHeader(headers);
headers = this.createXsrfHeader(headers);
return this.http.put<any>(this.basicApiUrl+subUrl, reqData,{
headers: headers,
withCredentials : true
}).pipe(map(resData => {
// console.log(resData);
return resData;
}));
}
[Adding header to delete request]
callDeleteRequest(subUrl: string,reqData?: any) {
let headers = new HttpHeaders();
headers = this.createAuthorizationHeader(headers);
headers = this.createXsrfHeader(headers);
return this.http.delete<any>(this.basicApiUrl+subUrl, {
headers: headers,
withCredentials : true
}).pipe(map(resData => {
// console.log(resData);
return resData;
}));
}
Is there any ways to solve it.
I believe your problem is here:
router.route() // <--- HERE
.handler(
CSRFHandler.create(vertx, csrfSecret())
.setCookieHttpOnly(false))
.handler(sessionHandler);
You are telling the application to create a new CSRF token for each request that is happening, instead of being specific of which end points are really form-based endpoints.
Imagine the following, your form is on /student/form your browser may request:
/student/form (new CSRF token: OK)
/images/some-image-in-the-html.png (new CSRF token: probably Wrong)
/css/styles.css (new CSRF token: probably Wrong)
...
Now the issue is that the 1st call did correctly generated a token, but the following 2+ will generate new tokens too and these won't match the 1st so your tokens are always misaligned.
You probably need to be more specific with the resources you want to protect, from your code I am assuming that you probably want something like:
router.route(""/student/*") // <--- Froms are always here under
.handler(
CSRFHandler.create(vertx, csrfSecret())
.setCookieHttpOnly(false))
.handler(sessionHandler);
Be careful if calling other endpoints would affect the forms too. Note that you can add multiple handlers per route, so you can be more explicit with:
router.route(""/student/add")
// 1st always CSRF checks
.handler(
CSRFHandler.create(vertx, csrfSecret())
.setCookieHttpOnly(false))
// and now we the handler that will handle the form data
.handler(this::handleAddUser)
Using Grails 3.0.9, and grabbing the freshest REST API with this snippet in gradle.build:
compile 'org.grails:grails-datastore-rest-client:4.0.7.RELEASE', {
['commons-codec', 'grails-async', 'grails-core',
'grails-plugin-converters', 'grails-web', 'groovy'].each {
exclude module: it
}
}
I am trying to make the following POST request:
def rest = new RestBuilder(headers:["X-LSS-Env":"devmo"], connectTimeout:10000, readTimeout:20000)
response = rest.post("http://..../..") {
accept "application/json"
contentType "application/json"
json jsonBuilder
}
Now, the POST receiver gets the json okay, give back a response okay, but this is the problem: it receives the headers as an empty map or as null!
So, what is the correct way of passing header data to the POST receiver? This is needed because the environment key X-LSS-Env could have different values, which instructs the receiver to do further routing based on it. Same with the GET request of course.
* UPDATE *
The consumer of my POST requests is actually a Java application, running on Apache Tomcat/8.0.26. The is how the service looks on the other side:
private javax.servlet.http.HttpServletRequest hsr;
#POST
#Path("/na")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response postSomething(Ggfp ggfp ){
try {
Enumeration<String> hnames = hsr.getHeaderNames();
int i = 0;
while (hnames.hasMoreElements()) {
String headerName = hnames.nextElement();
System.out.println(++i+ " headerName: " + headerName);
String val = hsr.getHeader(headerName);
System.out.println(" val: " + val);
}
String hval = hsr.getHeader("X-LSS-Env");
return Response.status(Status.OK).entity("X-LSS-Env is " + hval).build();
} catch (Exception e) {
}
}
Calling this service from Postman works, headers are identified. Calling it from the Grails app results into an empty map - like I am sending no headers!
The RestBuilder constructor never liked the way I used (or abused) it. Here is a clean way of achieving what I set out to do, with tryCatch logic if a timeout transpires.
def makePostWsr(serviceUrl, jsonBuilder) {
try {
def rest = new RestBuilder(connectTimeout:connectTimeout, readTimeout:readTimeout)
def response = rest.post("$wsUrl/$serviceUrl") {
header 'X-LSS-Env', 'devmo'
accept "application/json"
contentType "application/json"
json jsonBuilder
}
response
} catch (Exception e) {
println "== problem makePostWsr on $serviceUrl"
null
}
}
I need help with spring handling an error.
a client service is sending a request accepting two different content types - binary and json. when everything works fine I prefer communicating to my server with binary encoding to save bandwidth. but on error I would like serialise ResponseEntity to json as my binary serialiser do not know how to serialise it to binary format, plus it is better for logging, etc.
I configured instance of ResponseEntityExceptionHandler and I am handling different exceptions from that implementation. but spring always choses binary format as it is first on the accept (or produces) list.
all I get is (because spring do not know how to serialise ResponseEntity to my custom binary format. see AbstractMessageConverterMethodProcessor#writeWithMessageConverters)
org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
client sends
headers {Accept: [application/custom-binary, application/json]
server's controller is configured to
// pseudo code
#RequestMapping(method = GET, produces = {"application/custom-binary", APPLICATION_JSON_VALUE})
public BannerMetaCollection get(#RequestParam(value = "q") UUID[] q) {
if (q != null) {
return service.getAllDataWith(q);
} else {
throw new IllegalArgumentException("invalid data");
}
}
// pseudo code
public class RestExceptionResolverSupport extends ResponseEntityExceptionHandler {
#ExceptionHandler
public ResponseEntity<Object> illegalArgumentException(IllegalArgumentException ex, WebRequest request {
Object body = errorResponse()
.withCode(BAD_REQUEST)
.withDescription("Request sent is invalid")
.withMessage(ex.getMessage())
.build());
return new ResponseEntity<Object>(body, new HttpHeaders(), HttpStatus.BAD_REQUEST);
}
}
any hints?
What I do to get this to work is that a let my endpoint method return a ResponseEntity and I don't declare what content is produced in the #RequestMapping annotation. I then set the Content-type header myself before returning the response, e.g.
// pseudo code
#RequestMapping(method = GET)
public ResponseEntity<BannerMetaCollection> get(#RequestParam(value = "q") UUID[] q) {
if (q != null) {
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_TYPE, "application/custom-binary");
return new ResponseEntity<>(service.getAllDataWith(q),
headers,
HttpStatus.OK);
} else {
throw new IllegalArgumentException("invalid data");
}
}
I have already tried a lot of approaches to send a xml file as string + pictures with a POST request using GWT on the client side. I can send the strings successfully, but I do not know how to send files (pictures) using the RequestBuilder, I am just able to send the strings.
Do someone know how to send files with a multipart/form-data POST request using GWT Client (RequestBuilder)?
P.S.: As I am not wishing to upload files, I don't need a uploader or something similar. I am developing a mobile app with Phonegap, and making pictures, which should be sent per POST request to a server (a third party service).
Thanks in advance!
Here some code:
public void sendPost() throws RequestException {
String boundary = createBoundary();
String xml = "<note> <to>Müller</to> <from>Jani</from> <heading>Erinnerung</heading> <body>Ich wohne in der Leipzigerstraße</body> </note>";
String requestData = getRequestData(boundary, xml);
RequestBuilder builder = new RequestBuilder(RequestBuilder.POST, "http://localhost:8080/xxx/yyy");
builder.setHeader("Content-Type", "multipart/form-data; charset=UTF-8; boundary=" + boundary);
builder.setHeader("Content-Length", Long.toString(requestData.length()));
try {
builder.sendRequest(requestData, new RequestCallback() {
public void onResponseReceived(Request request, Response response) {
}
public void onError(Request request, Throwable exception) {
exception.printStackTrace();
}
});
} catch (RequestException e) {
e.printStackTrace();
}
}
private String getRequestData(String boundary, String xml) {
String s = "";
s += "--" + boundary + "\r\n";
s += getRequestParameter("xml", xml + "");
s += "--" + boundary + "--\r\n"; // end
return s;
}
private String getRequestParameter(String key, String value) {
return "Content-Disposition: form-data; name=\"" + key + "\"\r\n\r\n"
+ value + "\r\n";
}
private String createBoundary() {
return "----GoneVerticalBoundary" + getRandomStr() + getRandomStr();
}
private String getRandomStr() {
return Long.toString(random.nextLong(), 36); //random -> DEFINED IN THE CLASS BODY
}
If you are using gwt + phonegap you should be using gwt-phonegap, right?
What I do in my apps, is to use gwtupload for the desktop version, and phonegap file transfer in the mobile. I use gwtupload servlet in the server side for both cases.
This is my code using gwt-phonegap:
FileUploadOptions options = new FileUploadOptions();
options.setFileKey("file");
options.setFileName("my_file.txt");
options.setMimeType("text/plain");
phonegap.getFile().createFileTransfer().upload(
"file:///my_path/my_file.txt",
"http://my-gwtupload-servlet/servlet.gupld",
options,
new FileUploadCallback() {
#Override
public void onSuccess(FileUploadResult result) {
if (result.getResponseCode() == 200) {
} else {
}
}
#Override
public void onFailure(FileTransferError error) {
Window.alert("Error sending the file, error-code: " + error.getCode());
}
});
I use deferred binding for selecting the appropriate implementation using phonegap.env:
<replace-with class="...UploadFilePhoneGap">
<when-type-is class="....UploadFile" />
<when-property-is name="phonegap.env" value="yes" />
</replace-with>
If you are looking for pure gwt solution then you need to work with FileUpload
If you do not mind using third party open source jar then you can try gwtupload
For locale issues just ensure you are using UTF-8 and GWT locale cookies and locale settings.
For all the people who wants to manage files and pictures on the client side, before sending them to the server, as Manolo has recommended, I suggest lib-gwt-file.
For people working with PhoneGap, I suggest enconding the pictures using Base64, and decoding on the server side (see class Base64Util)
I am trying to call remote servlet from GWT, actually the GWT-RPC doesn't seems to work, so I am trying to do it using the RequestBuilder.
Here's the code snippet:
String url = "http://some-remote-host:8888/GWTJSTest/SomeServlet?name=" + textBox.getText();
RequestBuilder requestBuilder = new RequestBuilder(RequestBuilder.GET, url);
// requestBuilder.setHeader("Origin", "*");
// requestBuilder.setHeader("Access-Control-Allow-Origin", "*");
try
{
requestBuilder.sendRequest(null, new RequestCallback()
{
public void onResponseReceived(Request request, Response response)
{
if (response.getStatusCode() == 200)
{
Window.alert(response.getText());
}else
{
Window.alert(response.getText() + " : " + response.getStatusCode() + response.getStatusText());
}
}
public void onError(Request arg0, Throwable arg1)
{
Window.alert(arg1.toString());
}
});
} catch (RequestException e)
{
Window.alert("CATCH BLOCK: " + e.getMessage());
e.printStackTrace();
}
Actually, IE8 returns the data but after a warning message, but Firefox doesn't! Why is this?
As you see, I am trying to set some request headers but no way.
If you're trying to make a request to your own server and port (the same one that your GWT page is on), replace the first line with:
String url = "/GWTJSTest/SomeServlet?name=" + textBox.getText();
If you're trying to talk to a different server, or a different port on your own server, the Same Origin Policy will keep you from doing that. You'll need to proxy it from your own server.
The remote servlet is the one that needs to set the CORS header that you have:
Access-Control-Allow-Origin: *
Alternatively you can specify just your own domain instead of * if you don't want other domains interacting with the remote servlet.
I've added :
<add-linker name="xs" /> to .gwt.xml
And then replaces GWT-PRC by JsonpRequestBuilder (transforming JSONP between the server and the client)