Spring ws SOAP endpoint responds with missing fault details - java

I have soap-to-soap proxy server in spring using org.springframework.ws.* Both sides have identical wsdls.
I am able to pass the success response from external service to proxy consumer however there's a problem when fault message from external service gets returned.
The problem is my proxy server removes soap detail from the original response (I have no idea why). My goal is to pass the response from external service just as it is to proxy client without shortening. Anyone could help how to avoid fault detail being deleted ? Thank you in advance.
External Server response:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<soap:Fault>
<faultcode>soap:Server</faultcode>
<faultstring>Fault occurred while processing.</faultstring>
<detail>
<ns2:getCFUSubscriberStateFaultBusiness xmlns="example.system.messaging.common.v1.datatypes" xmlns:ns2="example.system.ot.managepcccfu.v2.datatypes">
<ns2:messageContext>
<requestId>273140800423344000</requestId>
<transactionId>8200</transactionId>
<timeLeft>10000</timeLeft>
<timestamp>2022-09-30T14:08:00</timestamp>
<user>x_turan</user>
<consumingComponent>urn:example.business.intr:SELFCARE3.</consumingComponent>
<providingService>urn:example.system.ot.managepccprovisioning.v1.service:ManagePccProvisioning</providingService>
<providingOperation>modifycontroffer</providingOperation>
</ns2:messageContext>
<ns2:messageDataBusinessException>
<errorCode>3001</errorCode>
<errorMessage>ESE Problem</errorMessage>
</ns2:messageDataBusinessException>
</ns2:getCFUSubscriberStateFaultBusiness>
</detail>
</soap:Fault>
</soap:Body>
</soap:Envelope>
Proxy Client Receives:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:Server</faultcode>
<faultstring xml:lang="en">Fault occurred while processing.</faultstring>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Endpoint:
#Endpoint
public class ServiceEndpoint {
public static final String NAMESPACE_URI="example.system.ot.managepcccfu.v2.datatypes";
#Autowired
CFUSoapClient soapClient;
#PayloadRoot(namespace = NAMESPACE_URI, localPart = "getCFUSubscriberState" )
#ResponsePayload
public GetCFUSubscriberStateResponse getCFUSubscriberState(#RequestPayload GetCFUSubscriberState request) throws GetCFUSubscriberStateFaultBusinessMessage, GetCFUSubscriberStateFaultSystemMessage {
final GetCFUSubscriberStateResponse response = soapClient.getCFUSubscriberStateResponse(request);
return response;
}
}
Soap Client:
public class CFUSoapClient extends WebServiceGatewaySupport {
public GetCFUSubscriberStateResponse getCFUSubscriberStateResponse(GetCFUSubscriberState request) throws GetCFUSubscriberStateFaultBusinessMessage {
Object response = getWebServiceTemplate().marshalSendAndReceive(request);
return (GetCFUSubscriberStateResponse) response;
}
}
Config:
#Configuration
#EnableWs
public class Config extends WsConfigurerAdapter {
#Bean
public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
return new ServletRegistrationBean(servlet, "/ws/CFU/*");
}
#Bean(name="CFU")
public Wsdl11Definition defaultWsdl11Definition() {
SimpleWsdl11Definition wsdl11Definition = new SimpleWsdl11Definition();
wsdl11Definition.setWsdl(new ClassPathResource("/wsdl/CFU.wsdl"));
return wsdl11Definition;
}
#Bean(name = "cfuDatatypesV2")
public XsdSchema cfuDatatypesV2() {
return new SimpleXsdSchema(
new ClassPathResource("wsdl/cfuDatatypesV2.xsd"));
}
#Bean(name = "common")
public XsdSchema common() {
return new SimpleXsdSchema(
new ClassPathResource("wsdl/common.xsd"));
}
#Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller();
jaxb2Marshaller.setContextPath("com.ot.cfu");
return jaxb2Marshaller;
}
#Bean
public CFUSoapClient soapClient() {
CFUSoapClient client = new CFUSoapClient();
client.setDefaultUri("http://localhost:41420/CFU");
client.setMarshaller(marshaller());
client.setUnmarshaller(marshaller());
// ClientInterceptor [] interceptors = new ClientInterceptor[]{new SoapClientInterceptor()};
// client.setInterceptors(interceptors);
return client;
}
}

After hours I managed to workaround this issue following wsdl definition and implemented custom exception classes wrapping the wsdl generated fault details:
#WebFault(name = "getCFUSubscriberStateFaultBusiness", targetNamespace = "example.system.ot.managepcccfu.v2.datatypes")
public class GetCFUSubscriberStateFaultBusinessMessage extends Exception {
private GetCFUSubscriberStateFaultBusiness getCFUSubscriberStateFaultBusiness;
public GetCFUSubscriberStateFaultBusinessMessage() {
super();
}
public GetCFUSubscriberStateFaultBusinessMessage(String message) {
super(message);
}
public GetCFUSubscriberStateFaultBusinessMessage(String message, java.lang.Throwable cause) {
super(message, cause);
}
public GetCFUSubscriberStateFaultBusinessMessage(String message, GetCFUSubscriberStateFaultBusiness getCFUSubscriberStateFaultBusiness) {
super(message);
this.getCFUSubscriberStateFaultBusiness = getCFUSubscriberStateFaultBusiness;
}
public GetCFUSubscriberStateFaultBusinessMessage(String message, GetCFUSubscriberStateFaultBusiness getCFUSubscriberStateFaultBusiness, java.lang.Throwable cause) {
super(message, cause);
this.getCFUSubscriberStateFaultBusiness = getCFUSubscriberStateFaultBusiness;
}
public GetCFUSubscriberStateFaultBusiness getFaultInfo() {
return this.getCFUSubscriberStateFaultBusiness;
}
}
#WebFault(name = "getCFUSubscriberStateFaultSystem", targetNamespace = "example.system.ot.managepcccfu.v2.datatypes")
public class GetCFUSubscriberStateFaultSystemMessage extends Exception {
private GetCFUSubscriberStateFaultSystem getCFUSubscriberStateFaultSystem;
public GetCFUSubscriberStateFaultSystemMessage() {
super();
}
public GetCFUSubscriberStateFaultSystemMessage(String message) {
super(message);
}
public GetCFUSubscriberStateFaultSystemMessage(String message, java.lang.Throwable cause) {
super(message, cause);
}
public GetCFUSubscriberStateFaultSystemMessage(String message, GetCFUSubscriberStateFaultSystem getCFUSubscriberStateFaultSystem) {
super(message);
this.getCFUSubscriberStateFaultSystem = getCFUSubscriberStateFaultSystem;
}
public GetCFUSubscriberStateFaultSystemMessage(String message, GetCFUSubscriberStateFaultSystem getCFUSubscriberStateFaultSystem, java.lang.Throwable cause) {
super(message, cause);
this.getCFUSubscriberStateFaultSystem = getCFUSubscriberStateFaultSystem;
}
public GetCFUSubscriberStateFaultSystem getFaultInfo() {
return this.getCFUSubscriberStateFaultSystem;
}
}
I filled the received soap fault detail into exception placeholder upon SoapFaultClientException based on type of detail:
public class CFUSoapClient extends WebServiceGatewaySupport {
public GetCFUSubscriberStateResponse getCFUSubscriberStateResponse(GetCFUSubscriberState request) throws GetCFUSubscriberStateFaultBusinessMessage, IOException, GetCFUSubscriberStateFaultSystemMessage {
try {
return (GetCFUSubscriberStateResponse) getWebServiceTemplate().marshalSendAndReceive(request);
} catch (SoapFaultClientException e) {
final Iterator<SoapFaultDetailElement> detailEntries = e.getSoapFault().getFaultDetail().getDetailEntries();
if (detailEntries.hasNext()) {
final SoapFaultDetailElement next = detailEntries.next();
final Source source = next.getSource();
final Object faultDetail = getWebServiceTemplate().getUnmarshaller().unmarshal(source);
if (faultDetail instanceof GetCFUSubscriberStateFaultBusiness) {
throw new GetCFUSubscriberStateFaultBusinessMessage(e.getSoapFault().getFaultStringOrReason(), (GetCFUSubscriberStateFaultBusiness) faultDetail, e.getCause());
} else if (faultDetail instanceof GetCFUSubscriberStateFaultSystem) {
throw new GetCFUSubscriberStateFaultSystemMessage(e.getSoapFault().getFaultStringOrReason(), (GetCFUSubscriberStateFaultSystem) faultDetail, e.getCause());
}
}
throw new RuntimeException("Unexpected error", e);
}
}
}
Eventually, I marshalled the details in the resolver class:
#Component
public class Resolver extends AbstractEndpointExceptionResolver {
private final Jaxb2Marshaller marshaller;
#Autowired
public Resolver(Jaxb2Marshaller marshaller) {
this.marshaller = marshaller;
}
#Override
protected boolean resolveExceptionInternal(MessageContext messageContext, Object endpoint, Exception e) {
if (e instanceof GetCFUSubscriberStateFaultBusinessMessage) {
final GetCFUSubscriberStateFaultBusinessMessage getCFUSubscriberStateFaultBusinessMessage = (GetCFUSubscriberStateFaultBusinessMessage) e;
final GetCFUSubscriberStateFaultBusiness faultInfo = getCFUSubscriberStateFaultBusinessMessage.getFaultInfo();
final Result result = createFaultDetailResult(messageContext, getCFUSubscriberStateFaultBusinessMessage);
marshaller.marshal(faultInfo, result);
return true;
} else if (e instanceof GetCFUSubscriberStateFaultSystemMessage) {
final GetCFUSubscriberStateFaultSystemMessage getCFUSubscriberStateFaultSystemMessage = (GetCFUSubscriberStateFaultSystemMessage) e;
final GetCFUSubscriberStateFaultSystem faultInfo = getCFUSubscriberStateFaultSystemMessage.getFaultInfo();
final Result result = createFaultDetailResult(messageContext, getCFUSubscriberStateFaultSystemMessage);
marshaller.marshal(faultInfo, result);
return true;
}
return false;
}
private Result createFaultDetailResult(MessageContext messageContext, Exception exception) {
final SoapMessage response = (SoapMessage) messageContext.getResponse();
final SoapBody soapBody = response.getSoapBody();
final SoapFault soapFault = soapBody.addServerOrReceiverFault(exception.getMessage(), Locale.ENGLISH);
final SoapFaultDetail faultDetail = soapFault.addFaultDetail();
return faultDetail.getResult();
}
}

Related

How to get request payload class type and response body class type in RestTemplateInterceptor to mask sensitive information using custom annotation?

In my spring boot application I've used a RestTemplateInterceptor to log request and response details in debug mode. To mask the sensitive information in request payload and response body, I've created a custom annotation #LogMaskedStringValue and annotated some fields in request DTO and response DTO. I've created a Serializer MaskStringSerializer to mask the annotated fields with the help of object mapper.
I tried to set the request payload type and expected response body type in request headers and I'm retrieving it in interceptor. But it is not the legitimate way to do, cause the header dependency prevents to use this interceptor in other applications, I tried using RestTemplateRequestCustomizer , Unfortunately it didn't work. Is there any way to get the request payload type and response body type in RestTemplateInterceptor ?
```
#Slf4j
public class RestTemplateLoggingInterceptor implements ClientHttpRequestInterceptor {
private final LogDetailsStorage logDetailsStorage;
private final static ObjectMapper objectMapper = new ObjectMapper();
static {
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
public RestTemplateLoggingInterceptor(LogDetailsStorage logDetailsStorage, String message) {
this.logDetailsStorage = logDetailsStorage;
this.message = message;
}
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
logDetailsStorage.setOutboundStartTime(System.currentTimeMillis());
String requestType = request.getHeaders().getFirst("requestType");
String responseType = request.getHeaders().getFirst("responseType");
request.getHeaders().remove("requestType");
request.getHeaders().remove("responseType");
logRequest(request, body, requestType);
ClientHttpResponse response = execution.execute(request, body);
logResponse(response, responseType);
return response;
}
private void logRequest(HttpRequest request, byte[] body, String requestType) {
if (log.isDebugEnabled()) {
logDetailsStorage.setOutboundRequestUrl(request.getURI().toString());
logDetailsStorage.setOutboundRequestMethod(request.getMethodValue());
MDC.put(MdcKey.OUTBOUND_REQUEST_METHOD.getMdcKey(), logDetailsStorage.getOutboundRequestMethod());
MDC.put(MdcKey.OUTBOUND_REQUEST_URL.getMdcKey(), logDetailsStorage.getOutboundRequestUrl());
if (body != null && body.length > 0) {
String requestPayload = new String(body, StandardCharsets.UTF_8);
logDetailsStorage.setOutboundRequestPayload(getMaskedPayload(requestType, requestPayload));
MDC.put(MdcKey.OUTBOUND_REQUEST_PAYLOAD.getMdcKey(), logDetailsStorage.getOutboundRequestPayload());
}
log.debug("Making request for " + logDetailsStorage.getOutboundRequestUrl());
MDC.remove(MdcKey.OUTBOUND_REQUEST_METHOD.getMdcKey());
MDC.remove(MdcKey.OUTBOUND_REQUEST_URL.getMdcKey());
MDC.remove(MdcKey.OUTBOUND_REQUEST_PAYLOAD.getMdcKey());
}
}
private void logResponse(ClientHttpResponse response, String responseType) throws IOException {
if (log.isDebugEnabled()) {
String responsePayload = StreamUtils.copyToString(response.getBody(), Charset.defaultCharset());
logDetailsStorage.setOutboundResponsePayload(getMaskedPayload(responseType, responsePayload));
logDetailsStorage.setOutboundStatusCode(String.valueOf(response.getRawStatusCode()));
logDetailsStorage.setOutboundExecutionTime((System.currentTimeMillis() - logDetailsStorage.getOutboundStartTime()) / 1000d + " seconds");
MDC.put(MdcKey.OUTBOUND_REQUEST_METHOD.getMdcKey(), logDetailsStorage.getOutboundRequestMethod());
MDC.put(MdcKey.OUTBOUND_REQUEST_URL.getMdcKey(), logDetailsStorage.getOutboundRequestUrl());
MDC.put(MdcKey.OUTBOUND_RESPONSE_PAYLOAD.getMdcKey(), logDetailsStorage.getOutboundResponsePayload());
MDC.put(MdcKey.OUTBOUND_STATUS_CODE.getMdcKey(), logDetailsStorage.getOutboundStatusCode());
if (logDetailsStorage.getOutboundRequestPayload() != null) {
MDC.put(MdcKey.OUTBOUND_REQUEST_PAYLOAD.getMdcKey(), logDetailsStorage.getOutboundRequestPayload());
}
MDC.put(MdcKey.OUTBOUND_EXECUTION_TIME.getMdcKey(), logDetailsStorage.getOutboundExecutionTime());
log.debug("Got Response for "+ logDetailsStorage.getOutboundRequestUrl());
MDC.remove(MdcKey.OUTBOUND_REQUEST_METHOD.getMdcKey());
MDC.remove(MdcKey.OUTBOUND_REQUEST_URL.getMdcKey());
MDC.remove(MdcKey.OUTBOUND_REQUEST_PAYLOAD.getMdcKey());
MDC.remove(MdcKey.OUTBOUND_EXECUTION_TIME.getMdcKey());
MDC.remove(MdcKey.OUTBOUND_STATUS_CODE.getMdcKey());
MDC.remove(MdcKey.OUTBOUND_RESPONSE_PAYLOAD.getMdcKey());
}
}
private String getMaskedPayload(String classType, String payload) {
if (!StringUtils.isEmpty(classType)) {
try {
Object obj = objectMapper.readValue(payload, Class.forName(classType));
payload = LogUtil.getObjectAsMaskedJsonString(obj);
} catch (JsonProcessingException e) {
log.error("'Failed to parse the payload : {}", payload, e);
} catch (ClassNotFoundException e) {
log.error("Class not found exception occurred : {}", classType, e);
}
}
else {
log.warn("ClassType is empty during getMaskedPayload : {}", classType);
}
return payload;
}
}
```
```
public class MaskStringSerializer extends StdSerializer<String> implements ContextualSerializer {
private String mask;
public MaskStringSerializer() {
super(String.class);
}
public MaskStringSerializer(String mask) {
super(String.class);
this.mask = mask;
}
#Override
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
Optional<String> maskValue = Optional.ofNullable(property)
.map(p -> p.getAnnotation(LogMaskStringValue.class))
.map(LogMaskStringValue::value);
return maskValue.map(MaskStringSerializer::new).orElseGet(MaskStringSerializer::new);
}
#Override
public void serialize(String value, JsonGenerator gen, SerializerProvider provider) throws IOException {
if (mask != null) {
gen.writeString(mask);
} else {
gen.writeString(Optional.ofNullable(value).orElse("null"));
}
}
}
```
```
#UtilityClass
#Slf4j
public class LogUtil {
private final static ObjectMapper sensitiveMapper = new ObjectMapper();
static {
SimpleModule module = new SimpleModule();
module.addSerializer(new MaskStringSerializer());
sensitiveMapper.registerModule(module);
}
public static String getObjectAsMaskedJsonString(Object object) {
String requestBody;
try {
requestBody = sensitiveMapper.writeValueAsString(object);
} catch (JsonProcessingException jsonProcessingException) {
log.error("Error while parsing object: {}", object, jsonProcessingException);
requestBody = object.toString();
}
return requestBody;
}
}
```
```
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
public class Card {
#LogMaskStringValue
private String id;
private String type;
private String last4;
private Integer expirationMonth;
private Integer expirationYear;
}
```
```
`

Spring Boot: Throwing exception in WebClient does not caught on my exception controller handler

I'm creating a component class that overrides a reactive method that calls another microservice "uaa" that validates a token, but when I verify that the token is invalid I throw an exception, but that exception does not catch in my exception controller handler
here is my component class
#Slf4j
#Component
#RequiredArgsConstructor
public class AuthFilter implements GlobalFilter {
private final JwtTokenProviderService jwtTokenProviderService;
private final TokenStatusDaoService tokenStatusDaoService;
private final WebClient webClient;
#Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("something in the way");
List<String> headers = exchange.getRequest().getHeaders().get(HttpHeaders.AUTHORIZATION);
if(CollectionUtils.isEmpty(headers)) {
log.trace("Request came without token");
return chain.filter(exchange);
} else {
String authToken = headers.get(0);
log.trace("Request holds a token");
log.debug("Check if token has expired ...");
if(jwtTokenProviderService.isTokenExpired(authToken)) {
log.debug("Token has expired will throw an error");
throw new AuthorizationForbiddenException(AuthorizationForbiddenExceptionTitleEnum.TOKEN_HAS_EXPIRED, "Token has expired");
}else {
log.debug("Check if token is valid and already saved");
String userId = jwtTokenProviderService.getClaimsFromToken(authToken).get(SecurityUtils.IDENTIFIER_KEY).toString();
if(!tokenStatusDaoService.exists(TokenStatusSpecification.withToken(authToken).and(TokenStatusSpecification.withUserId(Long.parseLong(userId))))) {
return webClient.get()
.uri("http://uaa", uriBuilder -> uriBuilder
.path("/validate-token")
.queryParam("token", authToken).build()).retrieve()
.bodyToMono(TokenValidationGetResource.class)
.map(tokenValidationGetResource -> {
if (!tokenValidationGetResource.isValid()) {
log.debug("token is not valid");
throw new AuthorizationForbiddenException(AuthorizationForbiddenExceptionTitleEnum.TOKEN_NOT_VALID, "Token is not valid");
} else {
log.debug("token is valid");
TokenStatusEntity tokenStatusEntity;
try {
tokenStatusEntity = tokenStatusDaoService.findOne(TokenStatusSpecification.withUserId(Long.parseLong(userId)));
} catch (Exception e) {
log.debug("No token defined for user: {}. Will save a new one ...", userId);
tokenStatusEntity = new TokenStatusEntity();
}
tokenStatusEntity.setToken(authToken);
tokenStatusEntity.setUserId(Long.parseLong(userId));
tokenStatusEntity.setStatus(TokenStatusEnum.VALID);
tokenStatusDaoService.save(tokenStatusEntity);
log.debug("Token status entity: {}", tokenStatusEntity);
return exchange;
}
}).flatMap(chain::filter);
} else {
log.debug("Token exists in DB");
return chain.filter(exchange);
}
}
}
}
}
and here is my exception controller handler:
#ControllerAdvice
public class ExceptionControllerImpl implements ExceptionController {
#Override
#ExceptionHandler({
AuthorizationForbiddenException.class
})
public ResponseEntity<ErrorDetailResource> handleGenericExceptions(
AbstractBaseException e, HttpServletRequest request) {
ErrorDetailResource errorDetailResource = new ErrorDetailResource();
errorDetailResource.setTimestamp(Instant.now().toEpochMilli());
errorDetailResource.setTitle(e.getTitle().toString());
errorDetailResource.setCode(e.getTitle().getCode());
errorDetailResource.setDeveloperMessage(e.getClass().getName());
errorDetailResource.setStatus(e.getStatus().value());
errorDetailResource.setDetail(e.getMessage());
return new ResponseEntity<>(errorDetailResource, e.getStatus());
}
}
Hello Those exceptions are thrown on a mono method in a reactive manner, so they can not be caught by controller advice, instead of doing that create a class which will extends the abstract class AbstractErrorWebExceptionHandler
#Component
#Order(-2)
public class GlobalErrorWebExceptionHandler extends AbstractErrorWebExceptionHandler {
public GlobalErrorWebExceptionHandler(GlobalErrorAttributes globalErrorAttributes,
ApplicationContext applicationContext,
ServerCodecConfigurer serverCodecConfigurer) {
super(globalErrorAttributes, new WebProperties.Resources(), applicationContext);
super.setMessageWriters(serverCodecConfigurer.getWriters());
super.setMessageReaders(serverCodecConfigurer.getReaders());
}
#Override
protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
}
private Mono<ServerResponse> renderErrorResponse(ServerRequest request) {
final Map<String, Object> errorPropertiesMap = getErrorAttributes(request, ErrorAttributeOptions.defaults());
Throwable error = null;
// here is your abstract base exception
AbstractBaseException baseException = null;
try {
baseException = (AbstractBaseException) getError(request);
} catch (Exception e) {
error = getError(request);
}
HttpStatus statusCode = baseException != null ? baseException.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR;
return ServerResponse.status(statusCode)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(errorPropertiesMap));
}
}
And of course do not forget to add DefaultErrorAttributes
#Component
public class GlobalErrorAttributes extends DefaultErrorAttributes {
#Override
public Map<String, Object> getErrorAttributes(ServerRequest request, ErrorAttributeOptions options) {
Throwable error = null;
// here is your abstract base exception
// cast the error to your exception class
AbstractBaseException baseException = null;
try {
baseException = (AbstractBaseException) getError(request);
} catch (Exception e) {
error = getError(request);
}
Map<String, Object> errorResources = new HashMap<>();
// Define the attribute that you want to return in response body
errorResources.put("attribute1", Instant.now().toEpochMilli());
errorResources.put("attribute2", baseException != null ? baseException.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR);
return errorResources;
}
}

How to make custom exception handlers working properly

I want to use custom exception handlers for my application. But, it is not working properly.
Here is my code
AuthenticationFilter.java
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (!bAuthorize) {
chain.doFilter(request, response);
return;
}
HttpServletRequest req = (HttpServletRequest) request;
String namespace = getPathParamFromRequest(req, NAMESPACE_PATH_PREFIX);
String userId = getPathParamFromRequest(req, USER_PATH_PREFIX);
AuthContext auth = null;
RequestPathContext rpc = pathsList.getMatchingContext(req.getRequestURI(), HttpMethod.valueOf(req.getMethod()));
if (rpc != null)
auth = rpc.getAuthContext();
if (auth != null) {
// Authentication process
} else {
throw new UnauthorizedException();
}
}
ApplicationExceptionHandler.java
public class ApplicationExceptionHandler {
#ExceptionHandler(UnauthorizedException.class)
public ResponseEntity<ErrorEntity> applicationxception(final UnauthorizedException e) {
ErrorEntity errorEntity = new ErrorEntity(e.getNumericErrorCode(), e.getErrorCode(), e.getErrorMessage());
return new ResponseEntity<>(errorEntity, HttpStatus.valueOf(e.getHttpStatus()));
}
}
AuthFilterRegistration.java
#Configuration
public class AuthFilterRegistration {
#Autowired
private ApplicationContext context;
#Bean
public FilterRegistrationBean<AuthenticationFilter> loggingFilter() {
FilterRegistrationBean<AuthenticationFilter> registrationBean
= new FilterRegistrationBean<>();
registrationBean.setFilter(context.getBean(AuthenticationFilter.class));
registrationBean.addUrlPatterns( "/public/*");
return registrationBean;
}
#Bean
public AuthenticationFilter getAuthFilter() {
return new AuthenticationFilter();
}
#Bean
public ApplicationExceptionHandler getErrorHandler() {
return new ApplicationExceptionHandler();
}
}
ErrorEntity.java
public class ErrorEntity extends BaseErrorEntity {
String errorMessage;
Map<String, String> messageVariables;
public ErrorEntity() {
}
public ErrorEntity(int numericErrorCode, String errorCode, String errorMessage) {
this(numericErrorCode, errorCode, errorMessage, null);
}
public ErrorEntity(int numericErrorCode, String errorCode, String errorMessage, Map<String, String> messageVariables) {
this.numericErrorCode = numericErrorCode;
this.errorCode = errorCode;
this.errorMessage = errorMessage;
this.messageVariables = messageVariables;
}
}
Using those code, I want to have an exception error like this
{
"numericErrorCode": 2001,
"errorCode": "errors.net.myproject.platform.unauthorized",
"errorMessage": "unauthorized"
}
which is the instance of ErrorEntity, but I got this output
{
"timestamp": "2019-02-01T04:41:14.337+0000",
"status": 500,
"error": "Internal Server Error",
"message": "unauthorized",
}
From the example it is clear that I cannot override the default Java exception completely. Only the message part that is altered successfully. Do I miss something here?
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
#ControllerAdvice
public class ApplicationExceptionHandler extends ResponseEntityExceptionHandler {
#ResponseBody
#ExceptionHandler(UnauthorizedException.class)
public ResponseEntity<ErrorEntity> applicationxception(final UnauthorizedException e) {
ErrorEntity errorEntity = new ErrorEntity(e.getNumericErrorCode(), e.getErrorCode(), e.getErrorMessage());
return new ResponseEntity<>(errorEntity, HttpStatus.valueOf(e.getHttpStatus()));
}
#ResponseBody
#ExceptionHandler(RetrievedProfileException.class)
public ResponseEntity<ErrorEntity> applicationexception(final RetrievedProfileException e) {
ErrorEntity errorEntity = new ErrorEntity(e.getNumericErrorCode(), e.getErrorCode(), e.getErrorMessage());
return new ResponseEntity<>(errorEntity, HttpStatus.valueOf(e.getHttpStatus()));
}
I just extend this class org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler because its pre- requsite
Secondly i used #ControllerAdvice
and lastly i used #ResponseBody
Use Exception Handler this way plus you should override the exception in this way.

Add CDATA on request's string parameter using only JAX-WS

I have a jax-ws client ganerated with CXF
The request have a string-parameter (MGRequest) that contains an xml, all work's but the generated request is like this:
<S:Body>
<ns5:MGRequest><mytag>hello</mytag></ns5:MGRequest>
</S:Body>
I need to generate the body like:
<S:Body>
<ns5:MGRequest><![CDATA[<mytag>hello</mytag>]]></ns5:MGRequest>
</S:Body>
(because i can't control the server..)
The client is like a standard jax-ws:
#WebService(name = "ServiceSoap")
#XmlSeeAlso({ ObjectFactory.class})
#SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
public interface ServiceSoap {
#WebMethod(operationName = "ProcessMessage")
#WebResult(name = "MGResponse")
public String processMessage(
#WebParam(partName = "input", name = "MGRequest") String input);
}
And i call like this:
Service client = new Service(url);
client.setHandlerResolver(HandlerFactory.build(new LoggerHandler()));
ServiceSoap service = client.getServiceSoap();
String msgToSend = JaxbUtil.jaxbObjToString(xmlObj, false);
String response = service.processMessage(msgToSend);
I have tried adding #XmlJavaTypeAdapter(CDataAdapter.class) before #WebParam but the result was:
<S:Body>
<ns5:MGRequest><![CDATA[<mytag>hello</mytag>]]></ns5:MGRequest>
</S:Body>
Where CDataAdapter:
public class CDataAdapter extends XmlAdapter<String, String> {
#Override
public String marshal(String v) throws Exception {
return "<![CDATA[" + v + "]]>";
}
#Override
public String unmarshal(String v) throws Exception {
return v;
}
}
Any idea how to archive that?
Thanks
After a working night i've found the solution:
adding a javax.xml.ws.handler.Handler to the client like this:
client.setHandlerResolver(HandlerFactory.build(new LoggerHandler(), new CDataHandler()));
where my HandlerFactory build a Handler:
public static HandlerResolver build(final Handler... handlers) {
return new HandlerResolver() {
#Override
public List<Handler> getHandlerChain(PortInfo portInfo) {
List<Handler> handlerChain = new ArrayList<Handler>();
if (handlers != null) {
for (Handler handler : handlers) {
handlerChain.add(handler);
}
}
return handlerChain;
}
};
}
import javax.xml.namespace.QName;
import javax.xml.soap.Node;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
public class CDataHandler implements SOAPHandler<SOAPMessageContext> {
#Override
public void close(MessageContext context) {
}
#Override
public boolean handleMessage(SOAPMessageContext soapMessage) {
try {
SOAPMessage message = soapMessage.getMessage();
boolean isOutboundMessage = (Boolean) soapMessage
.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
// is a request?
if (isOutboundMessage) {
// build a CDATA NODE with the text in the root tag
Node cddata = (Node) message.getSOAPPart().createCDATASection(
message.getSOAPBody().getFirstChild().getTextContent());
// add the CDATA's node at soap message
message.getSOAPBody().getFirstChild().appendChild(cddata);
// remove the text tag with the raw text that will be escaped
message.getSOAPBody().getFirstChild()
.removeChild(message.getSOAPBody().getFirstChild().getFirstChild());
}
} catch (Exception ex) {
// fail
}
return true;
}
#Override
public boolean handleFault(SOAPMessageContext soapMessage) {
return true;
}
#Override
public Set<QName> getHeaders() {
return Collections.EMPTY_SET;
}
}
This is a simple class, i had only one tag with text, but in more complex scenario you can take the necessary action navigating the DOM.

Spring Boot Web Service fails with mustUnderstand headers: {http://www.w3.org/2005/08/addressing}Action

Using the example provided here https://spring.io/guides/gs/producing-web-service/ I built a web service from a provided WSDL file. The service needed to support SOAP12 as shown in the code, this is done.
#EnableWs
#Configuration
public class WebServiceConfig extends WsConfigurerAdapter {
#Bean
public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
servlet.setTransformWsdlLocations(true);
servlet.setMessageFactoryBeanName("soap12");
return new ServletRegistrationBean(servlet, "/myEvents/*");
}
#Bean
public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema mySchema) {
DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
wsdl11Definition.setPortTypeName("EventService");
wsdl11Definition.setLocationUri("/myEvents");
wsdl11Definition.setTargetNamespace("http://event");
wsdl11Definition.setSchema(ctipsSchema);
return wsdl11Definition;
}
#Bean
public XsdSchema mySchema() {
return new SimpleXsdSchema(new ClassPathResource("mySchema.xsd"));
}
#Bean(name = "soap12")
public SaajSoapMessageFactory soap12MessageFactory() throws SOAPException {
SaajSoapMessageFactory factory = new SaajSoapMessageFactory(MessageFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL));
return factory;
}
// -- NO longer needed
#Override
public void addInterceptors(List<EndpointInterceptor> interceptors){
PayloadValidatingInterceptor validatingInterceptor = new PayloadValidatingInterceptor();
validatingInterceptor.setXsdSchema(ctipsSchema());
validatingInterceptor.setValidateRequest(true);
validatingInterceptor.setValidateResponse(true);
interceptors.add(validatingInterceptor);
try {
interceptors.add(wss4jSecurityInterceptor());
} catch (Exception e) {
e.printStackTrace();
}
}
#Bean
public Wss4jSecurityInterceptor wss4jSecurityInterceptor() throws Exception{
Wss4jSecurityInterceptor interceptor = new Wss4jSecurityInterceptor();
interceptor.setValidationActions("UsernameToken");
interceptor.setValidationCallbackHandler(new Wss4jSecurityCallbackImpl());
return interceptor;
}
}
The problem I am facing is that the I get an error related to mustUnderstand
SoapMessageDispatcher : Could not handle mustUnderstand headers:
{http://www.w3.org/2005/08/addressing}Action,
{http://www.w3.org/2005/08/addressing}To. Returning fault
I was working on the below solution but I cannot get it to work.
public static final String SOAP_HEADER_ACTION = "Action";
public static final String SOAP_HEADER_TO = "To";
public static final String NS_WS_ADDRESSING = "http://www.w3.org/2005/08/addressing";
public static final String PREFIX = "wsa";
#Bean(name = "soap12")
public SaajSoapMessageFactory soap12MessageFactory() throws SOAPException {
MessageFactory messageFactory = MessageFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL);
SOAPMessage request = messageFactory.createMessage();
SOAPHeaderElement header = request.getSOAPHeader().addHeaderElement(new QName(NS_WS_ADDRESSING, SOAP_HEADER_ACTION, PREFIX));
header.setMustUnderstand(true);
header = request.getSOAPHeader().addHeaderElement(new QName(NS_WS_ADDRESSING, SOAP_HEADER_TO, PREFIX));
header.setMustUnderstand(true);
SaajSoapMessageFactory factory = new SaajSoapMessageFactory(messageFactory);
MessageContext context = new DefaultMessageContext(new SaajSoapMessage(request), factory);
return factory;
}
The Endpoint class is
#Endpoint
public class HeartbeatEndpoint {
private static final String NAMESPACE_URI = "http://myevent";
#Action("http://myevent/Heartbeat")
#ResponsePayload
public HeartbeatResponse Heartbeat(#RequestPayload Heartbeat arg) {
HeartbeatResponse response = new HeartbeatResponse();
response.setHeartbeatResult(5);
return response;
}
}
Is anyone familiar with how this may be resolved using Spring Boot ?
*** Endpoint updated to be correct ****

Categories

Resources