I would like to authenticate all the requests at Zuul Gateway. i,e., each API request will send accessToken and zuul should authenticate the accessToken.
I have implemented this by extending ZuulFilter.
My code is below:
#Component
public class ZuulPreFilter extends ZuulFilter {
#Autowired
private DiscoveryClient discoveryClient;
#Override
public String filterType() {
return PRE_TYPE;
}
#Override
public int filterOrder() {
return PRE_DECORATION_FILTER_ORDER + 1;
}
#Override
public boolean shouldFilter() {
return true;
}
#Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
String accessToken = request.getParameter("accessToken");
if (accessToken == null || accessToken.isEmpty()) {
return sendUnauthorizedResponse(ctx, request, "Required AccessToken");
}
List<ServiceInstance> instances = this.discoveryClient.getInstances("security_service");
if (instances != null && instances.size() > 0) {
ServiceInstance securityService = instances.get(0);
String authAccessTokenAPI = securityService.getUri().toString().concat("/users/?accessToken=")
.concat(accessToken);
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<ValidationResponse> response = restTemplate.getForEntity(authAccessTokenAPI,
ValidationResponse.class);
ValidationResponse authUserResponse = response.getBody();
if (authUserResponse == null || !authUserResponse.isSuccess()) {
return sendUnauthorizedResponse(ctx, request, "Invalid AccessToken");
}
}
return null;
}
private Object sendUnauthorizedResponse(RequestContext ctx, HttpServletRequest request, String responseMessage) {
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
ValidationResponse validationResponse = new ValidationResponse();
validationResponse.setSuccess(false);
validationResponse.setMessage(responseMessage);
String contentType = request.getContentType();
if (contentType == null) {
contentType = MediaType.APPLICATION_JSON;
}
// To handle Json contentType
if (contentType.equalsIgnoreCase(MediaType.APPLICATION_JSON)) {
ObjectMapper mapper = new ObjectMapper();
try {
String responseBody = mapper.writeValueAsString(validationResponse);
ctx.setResponseBody(responseBody);
ctx.getResponse().setContentType(MediaType.APPLICATION_JSON);
} catch (JsonProcessingException e) {
}
}
// To handle XML contentType
if (contentType.equalsIgnoreCase(MediaType.APPLICATION_XML)) {
String responseBody = XmlUtil.marshal(validationResponse);
ctx.setResponseBody(responseBody);
ctx.getResponse().setContentType(MediaType.APPLICATION_XML);
}
return ctx;
}
}
I felt its NOT an effective way. Is there any other way to implement this scenario ?
Thanks.
Related
I want to use my own custom deserializer in Spring's default ObjectMapper whenever I have a class of type OAuth2AccessToken. The interface is annotated with
JsonDeserialize(using = OAuth2AccessTokenJackson2Deserializer.class)
and this is what it's using at the moment to deserialize but I want to use my own.
So far I have created my own custom deserializer
public class MyCustomDeserializer extends StdDeserializer<OAuth2AccessToken> {
public MyCustomDeserializer() {
super(OAuth2AccessToken.class);
}
#Override
public OAuth2AccessToken deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
String tokenValue = null;
String tokenType = null;
String refreshToken = null;
Long expiresIn = null;
Set<String> scope = null;
Map<String, Object> additionalInformation = new LinkedHashMap<String, Object>();
while (jp.nextToken() != JsonToken.END_OBJECT) {
String name = jp.getCurrentName();
jp.nextToken();
if (OAuth2AccessToken.ACCESS_TOKEN.equals(name)) {
tokenValue = jp.getText();
} else if (OAuth2AccessToken.TOKEN_TYPE.equals(name)) {
tokenType = jp.getText();
} else if (OAuth2AccessToken.REFRESH_TOKEN.equals(name)) {
refreshToken = jp.getText();
} else if (OAuth2AccessToken.EXPIRES_IN.equals(name)) {
try {
expiresIn = jp.getLongValue();
} catch (JsonParseException e) {
expiresIn = Long.valueOf(jp.getText());
}
} else if (OAuth2AccessToken.SCOPE.equals(name)) {
scope = parseScope(jp);
} else {
additionalInformation.put(name, jp.readValueAs(Object.class));
}
}
DefaultOAuth2AccessToken accessToken = new DefaultOAuth2AccessToken(tokenValue);
accessToken.setTokenType(tokenType);
if (expiresIn != null) {
accessToken.setExpiration(new Date(System.currentTimeMillis() + (expiresIn * 1000)));
}
if (refreshToken != null) {
accessToken.setRefreshToken(new DefaultOAuth2RefreshToken(refreshToken));
}
accessToken.setScope(scope);
accessToken.setAdditionalInformation(additionalInformation);
return accessToken;
}
private Set<String> parseScope(JsonParser jp) throws JsonParseException, IOException {
Set<String> scope;
if (jp.getCurrentToken() == JsonToken.START_ARRAY) {
scope = new TreeSet<String>();
while (jp.nextToken() != JsonToken.END_ARRAY) {
scope.add(jp.getValueAsString());
}
} else {
String text = jp.getText();
scope = OAuth2Utils.parseParameterList(text);
}
return scope;
}
}
My own custom class by extending DefaultOAuth2AccessToken
#com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = MyCustomDeserializer.class)
public class MyCustomOAuth2AccessToken extends DefaultOAuth2AccessToken {
public MyCustomOAuth2AccessToken(String value) {
super(value);
}
public MyCustomOAuth2AccessToken(OAuth2AccessToken accessToken) {
super(accessToken);
}
}
and at the moment I am registering a bean of type Jackson2ObjectMapperBuilderCustomizer like this
#Bean
public Jackson2ObjectMapperBuilderCustomizer addCustomDeserialization() {
return new Jackson2ObjectMapperBuilderCustomizer() {
#Override
public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) {
SimpleModule m = new SimpleModule();
m.addDeserializer(OAuth2AccessToken.class, new MyCustomDeserializer());
jacksonObjectMapperBuilder.modules(m);
}
};
}
#Bean
public OAuth2ClientContext getOAuth2ClientContext() {
DefaultOAuth2ClientContext defaultOAuth2ClientContext = new DefaultOAuth2ClientContext();
defaultOAuth2ClientContext.setAccessToken(new MyCustomOAuth2AccessToken("test"));
return defaultOAuth2ClientContext;
}
You can simply annotate your deserialization classes with #JsonComponent.
The annotation allows us to expose an annotated class to be a Jackson serializer and/or deserializer without the need to add it to the ObjectMapper manually.
To configure ObjectMapper globally just create a bean of type Jackson2ObjectMapperBuilder and use deserializerByType method :
#Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder() {
return new Jackson2ObjectMapperBuilder()
.deserializerByType(OAuth2AccessToken.class, new MyCustomDeserializer());
}
Reference of configuring ObjectMapper in SpringBoot can be found here.
ctually when we call API and send request in JSON format we are expecting response also come into JSON format. But here back end team sending me response in String format therefore my onErrorResponse () method get called. Here my status code is 200. But due to format of response not executed onResponse () method. So will you please help me to handle this? Might be I have to use CustomRequest here. Any suggestoin will be appreciated. Thanks
public class SampleJsonObjTask {
public static ProgressDialog progress;
private static RequestQueue queue;
JSONObject main;
JsonObjectRequest req;
private MainActivity context;
private String prd,us,ver,fha,ve,ves,sz,cat,pa,h,t,en,pha,pur,dip;
public SampleJsonObjTask(MainActivity context, JSONObject main) {
progress = new ProgressDialog(context);
progress.setMessage("Loading...");
progress.setCanceledOnTouchOutside(false);
progress.setCancelable(false);
progress.show();
this.context = context;
this.main = main;
ResponseTask();
}
private void ResponseTask() {
if (queue == null) {
queue = Volley.newRequestQueue(context);
}
req = new JsonObjectRequest(Request.Method.POST, "", main,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
progress.dismiss();
Log.e("response","response--->"+response.toString());
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
progress.dismiss();//error.getMessage()
/*back end team sending me response in String format therefore my onErrorResponse () method get called. Here my status code is 200.*/
}
})
{
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
params.put("Content-Type", "application/json");
return params;
}
};
req.setRetryPolicy(new DefaultRetryPolicy(20 * 1000, 0, 1f));
queue.add(req);
}
}
Here the Response coming like string format that is Value OK,
com.android.volley.ParseError: org.json.JSONException: Value OK of type java.lang.String cannot be converted to JSONObject
You can use StringRequest for that:
StringRequest request = new StringRequest(StringRequest.Method.POST, url, new Response.Listener<String>() {
#Override
public void onResponse(String response) { }
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
}) {
#Override
public String getBodyContentType() {
return "application/json; charset=utf-8";
}
#Override
public byte[] getBody() {
try {
JSONObject jsonObject = new JSONObject();
/* fill your json here */
return jsonObject.toString().getBytes("utf-8");
} catch (Exception e) { }
return null;
}
};
Writing JUnit Integrtaion tests for a REST endpoint which sets secure cookies, can't get past the ResourceAccessException error.
Requirement is to do a https://localhost:8443 request.
Have tried using the customRestTemplate
Getting the folloiwng exception.
org.springframework.web.client.ResourceAccessException: I/O error on GET request for "https://localhost:8443/dcs": Connect to localhost:8443 [localhost/127.0.0.1, localhost/0:0:0:0:0:0:0:1] failed: Connection refused: connect; nested exception is org.apache.http.conn.HttpHostConnectException
Below is the code.
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class DcsServiceTests {
#Autowired
RestTemplateBuilder restTemplateBuilder;
#Autowired
private TestRestTemplate testRestTemplate;
#Test
public void testGet_ImageResponse() throws Exception {
//Arrange
//Act
ResponseEntity<byte[]> response = testRestTemplate.getForEntity(url, byte[].class);
//Assert
//Response Status
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
//Response has cookie
assertThat(response.getHeaders().containsKey("Set-Cookie")).isTrue();
}
#PostConstruct
public void initialize() {
// Lambda expression not working, TBD - Java version used.
//TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;
final TrustStrategy acceptingTrustStrategy = new TrustStrategy() {
#Override
public boolean isTrusted(java.security.cert.X509Certificate[] arg0, String arg1)
throws CertificateException {
return true;
}
};
HttpComponentsClientHttpRequestFactory requestFactory =
new HttpComponentsClientHttpRequestFactory();
try {
SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom()
.loadTrustMaterial(null, acceptingTrustStrategy)
.build();
SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext);
CloseableHttpClient httpClient = HttpClients.custom()
.setSSLSocketFactory(csf)
.build();
requestFactory.setHttpClient(httpClient);
}
catch (Exception e) {
System.out.println("Exception occured creating Request Factory");
}
RestTemplate customTemplate = restTemplateBuilder
.requestFactory(requestFactory)
.rootUri("https://localhost:8443")
.build();
this.testRestTemplate = new TestRestTemplate(
customTemplate,
null,
null, // Not using basic auth
TestRestTemplate.HttpClientOption.ENABLE_COOKIES); // Cookie support
}
}
Disabling SSL and then using testRestTemplate with exchange method worked. Secured cookies works as well, just that the headers needs to be parsed to validate results in Unit test cases
#Bean
public Boolean disableSSLValidation() throws Exception {
final SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[] { new X509TrustManager() {
#Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
#Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
#Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
} }, null);
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
return true;
}
public void hostNameVerifier() {
final HostnameVerifier defaultHostnameVerifier = javax.net.ssl.HttpsURLConnection.getDefaultHostnameVerifier ();
final HostnameVerifier localhostAcceptedHostnameVerifier = new javax.net.ssl.HostnameVerifier () {
public boolean verify ( String hostname, javax.net.ssl.SSLSession sslSession ) {
if ( hostname.equals ( "localhost" ) ) {
return true;
}
return defaultHostnameVerifier.verify ( hostname, sslSession );
}
};
javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier ( localhostAcceptedHostnameVerifier );
}
#Test
public void testGet_ImageResponse() throws Exception {
//Arrange
String url = getUrl() + "/xyz?s_action=test&s_type=i";
//Act
ResponseEntity<byte[]> response = restTemplate.getForEntity(url, byte[].class);
//Assert
//Response Status
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
//Response has cookie
assertThat(response.getHeaders().containsKey("Set-Cookie")).isTrue();
//Extract cookie from header
List<String> cookies = response.getHeaders().get("Set-Cookie");
//Construct cookie from RAW Header Response
Cookie cookie = RawCookieParser.constructCookieFromHeaderResponse(response.getHeaders().get("Set-Cookie").toString());
//Cookies name matches
//Cookie value cannot be matched because value is being set from external JAR
assertEquals(cookie.getName(), appConfig.getName());
//Cookie domain matches
assertEquals(cookie.getDomain(), appConfig.getDomain());
}
public class RawCookieParser {
/*
* Construct a cookie object by parsing the HTTP Header response
*/
public static Cookie constructCookieFromHeaderResponse(String input) throws Exception {
String rawCookie = input.replace("[", "").replace("]", "");
String[] rawCookieParams = rawCookie.split(";");
String[] rawCookieNameAndValue = rawCookieParams[0].split("=");
if (rawCookieNameAndValue.length != 2) {
throw new Exception("Invalid cookie: missing name and value.");
}
String cookieName = rawCookieNameAndValue[0].trim();
String cookieValue = rawCookieNameAndValue[1].trim();
Cookie cookie = new Cookie(cookieName, cookieValue);
for (int i = 1; i < rawCookieParams.length; i++) {
String rawCookieParamNameAndValue[] = rawCookieParams[i].trim().split("=");
String paramName = rawCookieParamNameAndValue[0].trim();
if (rawCookieParamNameAndValue.length == 2) {
String paramValue = rawCookieParamNameAndValue[1].trim();
if (paramName.equalsIgnoreCase("secure")) {
cookie.setSecure(true);
} else if (paramName.equalsIgnoreCase("max-age")) {
int maxAge = Integer.parseInt(paramValue);
cookie.setMaxAge(maxAge);
} else if (paramName.equalsIgnoreCase("domain")) {
cookie.setDomain(paramValue);
} else if (paramName.equalsIgnoreCase("path")) {
cookie.setPath(paramValue);
}
}
}
return cookie;
}
}
I want do call a SOAP service with Spring WS WebServiceTemplate. I have used this very often and it always worked so far. But now I just get an soap envelope with empty body:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header/><SOAP-ENV:Body/></SOAP-ENV:Envelope>
I have created the request and response classes with the JAXB Maven Plugin. And the generated source code looks exactly like the services which are working.
Example:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(
name = "startRequest_RequestParameters",
propOrder = {"url"}
)
#XmlRootElement(
name = "startRequest"
)
public class StartRequest {
#XmlElement(
required = true
)
#XmlSchemaType(
name = "anyURI"
)
protected String url;
public StartRequest() {
}
public String getUrl() {
return this.url;
}
public void setUrl(String value) {
this.url= value;
}
}
I call the webservice template with marshallSendAndReceive
StartRequest request = new StartRequest();
request.setUrl(url);
StartResponse response = (StartResponse) webServiceTemplate.marshalSendAndReceive(endpointUrl, request);
I configure the WebServiceTemplate with java configuration:
public WebServiceTemplate startRequestWebServiceTemplate() throws Exception {
return createWebServiceTemplate(createMarshaller(), createSecurityInterceptor(username, password), createMessageSender(proxyHost, proxyPort));
}
private WebServiceTemplate createWebServiceTemplate(Jaxb2Marshaller marshaller, ClientInterceptor securityInterceptor, WebServiceMessageSender messageSender) {
WebServiceTemplate webServiceTemplate = new WebServiceTemplate();
webServiceTemplate.setMarshaller(marshaller);
webServiceTemplate.setUnmarshaller(marshaller);
webServiceTemplate.setMessageSender(messageSender);
if (securityInterceptor != null) {
webServiceTemplate.setInterceptors((ClientInterceptor[]) Arrays.asList(securityInterceptor, createLoggingInterceptor()).toArray());
} else {
webServiceTemplate.setInterceptors((ClientInterceptor[]) Arrays.asList(createLoggingInterceptor()).toArray());
}
webServiceTemplate.setCheckConnectionForFault(true);
webServiceTemplate.afterPropertiesSet();
return webServiceTemplate;
}
private Jaxb2Marshaller createMarshaller() throws Exception {
Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller();
jaxb2Marshaller.setClassesToBeBound(StartRequest.class, StartResponse.class);
jaxb2Marshaller.afterPropertiesSet();
return jaxb2Marshaller;
}
private ClientInterceptor createLoggingInterceptor() {
return new SoapLoggingInterceptor(systemName);
}
private Wss4jSecurityInterceptor createSecurityInterceptor(String username, String password) {
Wss4jSecurityInterceptor wss4jSecurityInterceptor = new Wss4jSecurityInterceptor();
wss4jSecurityInterceptor.setSecurementPasswordType("PasswordText");
wss4jSecurityInterceptor.setSecurementActions("UsernameToken");
wss4jSecurityInterceptor.setSecurementUsername(username);
wss4jSecurityInterceptor.setSecurementPassword(password);
wss4jSecurityInterceptor.setSkipValidationIfNoHeaderPresent(true);
wss4jSecurityInterceptor.setValidateRequest(false);
wss4jSecurityInterceptor.setValidateResponse(false);
return wss4jSecurityInterceptor;
}
private HttpComponentsMessageSender createMessageSender(String proxyHost, String proxyPort) {
HttpComponentsMessageSender httpComponentsMessageSender = new HttpComponentsMessageSender(createHttpClient(proxyHost, proxyPort));
httpComponentsMessageSender.setAcceptGzipEncoding(true);
return httpComponentsMessageSender;
}
private HttpClient createHttpClient(String proxyHost, String proxyPort) {
RequestConfig.Builder configBuilder = RequestConfig.custom()
.setConnectTimeout(DEFAULT_CONNECTION_TIMEOUT_MILLISECONDS)
.setSocketTimeout(DEFAULT_READ_TIMEOUT_MILLISECONDS)
.setConnectionRequestTimeout(CONNECTION_REQUEST_TIMEOUT);
addProxySettings(configBuilder, proxyHost, proxyPort);
HttpClientBuilder clientBuilder = HttpClients.custom().setDefaultRequestConfig(configBuilder.build());
addInterceptor(clientBuilder);
addConnectionManager(clientBuilder);
return clientBuilder.build();
}
private void addProxySettings(RequestConfig.Builder configBuilder, String proxyHost, String proxyPort) {
if (StringUtils.isNotBlank(proxyHost)) {
configBuilder.setProxy(new HttpHost(proxyHost, Integer.valueOf(proxyPort)));
}
}
private void addInterceptor(HttpClientBuilder clientBuilder) {
clientBuilder.addInterceptorFirst(new HttpComponentsMessageSender.RemoveSoapHeadersInterceptor());
}
private void addConnectionManager(HttpClientBuilder clientBuilder) {
if (maxConnections > DEFAULT_MAX_CONNECTIONS_PER_ROUTE) {
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
cm.setMaxTotal(maxConnections);
cm.setDefaultMaxPerRoute(maxConnections);
clientBuilder.setConnectionManager(cm);
}
}
This configuration worked fine for other soap implementations. But here I just get the soap envelope with the empty body.
Has anyone an idea what's wrong here?
I did something wrong when refactoring the LoggingInterceptor. When handling the request it took the response part from the MessageContext instead of the request part, which caused to overwrite the request with the response. So if you have such a problem check your interceptors if they handle response and request correctly
I am Spring MVC beginner and I want to call rest in #ResponseBody. My external node server doesn't react on that method. I don't got message about request in my server console. Without UserRest it works. I would be grateful for your help
#Controller
public class AjaxController {
#RequestMapping(value= "user", method=RequestMethod.GET)
public #ResponseBody String login (){
UserRest ur = new UserRest();
Response r = ur.getUserName(2);
Gson gs = new Gson();
String str = gs.toJson(r);
return str;
}
}
Response getUserName(int userID){
Response response = new Response();
StringBuilder total = new StringBuilder();
try {
URL url = new URL(Properties.SERVER_SECURE_URL + "users/" + userID);
urlConnection = (HttpsURLConnection) url.openConnection();
urlConnection.setDoOutput(false);
urlConnection.setDoInput(true);
urlConnection.setRequestMethod("GET");
urlConnection.setRequestProperty("Authorization","1Strajk");
response.setMessageCode(urlConnection.getResponseCode());
if(response.getMessageCode()==Response.MESSAGE_OK) {
InputStream in = urlConnection.getInputStream();
BufferedReader r = new BufferedReader(new InputStreamReader(in));
String line;
while ((line = r.readLine()) != null) {
total.append(line);
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(!total.toString().isEmpty()){
response.setObject(total.toString());
}
urlConnection.disconnect();
}
return response;
}
I resolve it. I forgot about SSL connection. Before calling rest I called that method:
public class SSLUtils {
public static void trustEveryone() {
try {
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, new X509TrustManager[]{new X509TrustManager(){
public void checkClientTrusted(X509Certificate[] chain,
String authType) throws CertificateException {}
public void checkServerTrusted(X509Certificate[] chain,
String authType) throws CertificateException {}
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}}}, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(
context.getSocketFactory());
} catch (Exception e) { // should never happen
e.printStackTrace();
}
}
}