I am working with a microservice architecture where a Zuul gateway contacts a Eureka server to discover published microservices. I want my zuul gateway to accept path in a particular format.
It received a URL call with a member id sent as a token. Because we want to avoid sending sensitive information in URLs, these expirable tokens would be parsed by Zuul, translated to a Social Security Number, for example, and the ssn would be sent in a header.
For example, a bank acct GET:
http://zuulgateway/member/11/account/
would map to
http://microservice/account
X-MEMBER-SSN: 1112223333
My plan is to set up a "pre" Zuul filter to parse out the member token "11" and use it to get the SSN, then add it to the header.
But I'm not sure how I configure this route or if it is even possible.
zuul.routes.account.path: /member/*/**
does not achieve it. Is there some other mechanism I can use?
There are a few forum posts on the Zuul github regarding this issue but none of the solutions were working for me. I couldn't figure out why and finally realized: the hot deployment into Zuul from my Spring Tool Suite was restarting the application, but not deploying the latest changes (perhaps this is an endorsement for JRebel in the end!). Once I realized this I started experimenting with stop/deploy/start coding until I got a solution working with ZuulFilter.
The solution involves a "pre" filter which rewrites the contextURL to remove the /member/\d+/
portion of the URL so that the context can be directly mapped to the
Here is some example code:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
@Component
public class SpecialFilter extends ZuulFilter {
private static final String REQUEST_URI_KEY = "requestURI";
private static final Pattern URL_PATTERN = Pattern.compile("^\\/?(\\d+)\\/(.+)$");
@Override
public boolean shouldFilter() {
return isMember(RequestContext.getCurrentContext());
}
private boolean isMember(RequestContext currentContext) {
String path = (String) currentContext.get(REQUEST_URI_KEY);
Matcher m = URL_PATTERN.matcher(path);
return m.matches();
}
@Override
public Object run() {
RequestContext context = RequestContext.getCurrentContext();
String originalRequestPath = (String) context.get(REQUEST_URI_KEY);
Matcher m = URL_PATTERN.matcher(originalRequestPath);
System.out.println("Parsing original "+originalRequestPath + " against " + URL_PATTERN.toString());
if(!m.matches()) {
System.err.println("Invalid URL");
return null;
}
String ssn = translateSSN(m.group(1));
String requestPath = m.group(2);
String modifiedRequestPath = "/" + requestPath;
context.put(REQUEST_URI_KEY, modifiedRequestPath);
//Add this header to the request
context.addZuulRequestHeader("X-SSN", ssn);
return null;
}
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
// Should proceed this filter
return FilterConstants.PRE_DECORATION_FILTER_ORDER + 1;
}
}