Modify Path Param of an HttpServletRequest - java

I have a method which processes that URL:
http://IP:PORT/auth/myapp?Username=username
and accessible from remote. However I can not change external system which uses my app and it sends username within HTTP Header. I mean they access that URL:
http://IP:PORT/auth/myapp
I think that I can get related HTTP Header as follows:
Enumeration headerNames = request.getHeaderNames();
while(headerNames.hasMoreElements()) {
String headerName = (String)headerNames.nextElement();
if (headerName.equals("UNAME")) {
String username = request.getHeader(headerName);
}
}
I can not modify whole part of my app and I have to add that info as path parameter into existing request. I mean change that request to:
http://IP:PORT/auth/myapp?Username=username
How can I do that?
PS:
Can this piece of code solve the problem that I've described?
if (request.getParameter("Username") == null) {
Enumeration headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = (String) headerNames.nextElement();
if (headerName.equals("UNAME")) {
String username = request.getHeader(headerName);
response.sendRedirect(request.getRequestURI() + "&Username="+username);
}
}
}

If you are accessing 'username' query parameter using HttpServletRequest.getParameter('username') in other parts of your app, you can try using HttpServletRequestWrapper
class MyRequestWrapper extends HttpServletRequestWrapper{
public String getParameter(String name){
// if name equals username, call super.getHeader('username')
//else super.getParameter(name);
}
}
You can extend this class and override getParameter() method. In your implementation, you get the value from header if parameter name is username else call the super method.

It's round-a-bout, but if you also have the HttpServletResponse object, you can send a redirect request to the http://IP:PORT/auth/myapp?Username=username URL

I think there is two solutions:
Use a filter on your application mapped on each request. The filter will check header and add a parameter if the UNAME header is there
Use Apache Rewrite Module in front of your web application. Rewrite module will add the parameter to the url (check "Module Apache mod_rewrite")
UPDATE: As mohit said used a HttpRequestWrapper combined with a filter
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
//create the MyCustomWrapperRequest object to wrap the HttpServletRequest
MyCustomWrapperRequest request = new MyCustomWrapperRequest((HttpServletRequest)servletRequest);
//continue on in the filter chain
filterChain.doFilter(request, servletResponse);
}
And in MyCustomWrapperRequest override getParameter() function

Related

Adding Headers to Zuul when re-directing

I am trying to use Zuul to redirect calls to a downstream system somewhere else.
In the re-direct, I need to add in a Header with necessary data for the api receiving the redirection to process. I can't seem to get the downstream system to detect this data. Attached is my code.
I am using Zuul from Edgware.SR3, Spring Boot 1.5.12
Zuul Filter
#Component
public class RouteFilter extends ZuulFilter{
#Override
public Object run() {
//Testing to add header
context.getRequest().getParameterMap().put("api", new String[]{"api"});
context.getResponse().setHeader("api", api);
context.addZuulResponseHeader("api", "api");
context.addZuulRequestHeader("api", "api");
context.setSendZuulResponse(false);
context.put(FORWARD_TO_KEY, redirect_urls.get(key));
context.setResponseStatusCode(HttpStatus.SC_TEMPORARY_REDIRECT);
context.getResponse().sendRedirect(redirect_urls.get(key));
return null;
}
}
Redirected Service Code
#RequestMapping(value = "/forward")
public ResponseEntity<String> forwardToMe(#RequestHeader(required = true, name = "api")String api){
return new ResponseEntity<String>("Hi",HttpStatus.OK);
}
Error Received in Postman
{
"timestamp": 1524737817729,
"status": 400,
"error": "Bad Request",
"exception": "org.springframework.web.bind.ServletRequestBindingException",
"message": "Missing request header 'api' for method parameter of type String",
"path": "/forward" }
I guess you use a Route Filter, maybe you can try with a Pre Filter.
Adding a custom header can be done with something like this : context.addZuulRequestHeader("Authorization", "Basic " + credentials);.
For the redirection part, you can check this thread
A little late my response but works fine
As referred in the official documentation Cookies and Sensitive Headers
The sensitiveHeaders are a blacklist, and the default is not empty. Consequently, to make Zuul send all headers (except the ignored ones), you must explicitly set it to the empty list. Doing so is necessary if you want to pass cookie or authorization headers to your back end. The following example shows how to use sensitiveHeaders:
zuul:
routes:
entry:
path: /users/**
strip-prefix: false
service-id: users-service
sensitive-headers:
- Cookie,Set-Cookie
This implemented example can also help you
In case if anyone still facing this issue,
In Zuul Proxy add the header to RequestContext as below:
RequestContext ctx = RequestContext.getCurrentContext();
ctx.addZuulRequestHeader("param", "value");
And then in the respective microservices write a custom filter and extract the value as below
#Component
public class MyFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
String headerParam = request.getHeaders("param").nextElement();
logger.info("headerParam: "+headerParam);
filterChain.doFilter(request, response);
}
}
I update my comment here just in case if anyone is still facing this problem.
I found this problem recently and resolved by adding the following configuration in my application.yml
application.yml
...
zuul:
sensitive-headers:
- Cookie,Set-Cookie
...
Reference Link below:
https://cloud.spring.io/spring-cloud-static/Dalston.SR5/multi/multi__router_and_filter_zuul.html
RequestContext ctx = RequestContext.getCurrentContext();
String auth = "useeerrr" + ":" + "passsss";
ctx.addZuulRequestHeader("Authorization", "Basic " +
Base64Variants.MIME_NO_LINEFEEDS.encode(auth.getBytes(StandardCharsets.US_ASCII)));
ctx.addZuulRequestHeader("X-USERNAME-HEADER","xxx");
Map<String, List<String>> newParameterMap = new HashMap<>();
Map<String, String[]> parameterMap = ctx.getRequest().getParameterMap();
for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
String key = entry.getKey();
String[] values = entry.getValue();
newParameterMap.put(key, Arrays.asList(values));
}
String authenticatedKey = "authenticated";
String authenticatedValue = "true";
newParameterMap.put(authenticatedKey,Arrays.asList(authenticatedValue));
ctx.setRequestQueryParams(newParameterMap);
HttpServletRequest request = ctx.getRequest();
logger.info(String.format("%s request to %s", request.getMethod(), request.getRequestURL().toString()));
return null;

Spring 3 - How to throw an exception when additional request parameters are passed

I have a controller mapping, where I pass 2 request params instead of 1. But when done like that Spring is not throwing any exception rather it is ignoring the additional request params.
For eg:
#RequestMapping(value="/test", method = RequestMethod.GET)
public ModelAndView eGiftActivation(#RequestParam("value") String value)
When I hit my app using /test.do?value=abcd it is working fine. But when I pass additional params like /test.do?value=abcd&extra=unwanted also it's working fine.
In this case I want Spring to restrict the second URL where additional params are passed.
How can I achieve this?
You could check it manually, like this:
#RequestMapping("/test")
public ModelAndView eGiftActivation(HttpServletRequest request) {
Map<String, String[]> params = request.getParameterMap();
if (params.size() != 1 || !params.containsKey("value")) {
throw new RuntimeException("Extra parameters are present"); // or do redirect
}
...
}
I don't think it's possible (For Spring to prevent the request to flow to any controller's method). The reason being that:
Your controller handles request based on the URI path like, /app/hello/{name} rather than the request parameters
Request parameters are there to give extra set of meta-info for the request rather than endpoint specification of request.
But, if you wanted to restrict the URI path as such, you can use regex and you can avoid. I'm afraid it's not feasible and even the requirement for that never arose.
Programmatical Way:
Having said that, you can take HttpServletRequest for parameters and loop through the parameters to check for extra ones:
#RequestMapping(value="/test", method = RequestMethod.GET)
public Object eGiftActivation(#RequestParam("value") String value, HttpServletRequest request){
//check the request.getParameterMap() and throw custom exception if you need and handle using Exception handler or throw invalid request
return new ResponseEntity<String>(HttpStatus.SC_BAD_REQUEST);
}
I prefer handling these kind of validations (if required, what ever may be the reason) inside the Filter generically so that the requests will not even reach the Controller methods.
Please find the required code to handle inside the Filter as below (logic is almost similar to Slava).
#Component
public class InvalidParamsRequestFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
Map<String, String[]> params = request.getParameterMap();
if (request.getRequestURI().contains("/test") && (params.size() != 1 || !params.containsKey("value"))) {
//Here, Send back the Error Response OR Redirect to Error Page
} else {
filterChain.doFilter(request, response);
}
}
}

ServletFilter - changing URL does not work

I have a GWT application which URL is .../Organizer.html. I would like to access it also from url .../organizer. So I decided to replace the original HttpServletRequest with the wrapper with overriden getRequestURL() and getRequestURI() methods in a servlet filter.
Filter code:
String[] urlSplit = req.getRequestURL().toString().split("/");
String urlEnd = urlSplit[urlSplit.length -1];
if (urlEnd.equals(ORGANIZER_URL_ALTERNATIVE)){
String newUrl = req.getRequestURL().toString().
replace(ORGANIZER_URL_ALTERNATIVE, ORGANIZER_URL);
String newUri = req.getRequestURI().toString().
replace(ORGANIZER_URL_ALTERNATIVE, ORGANIZER_URL);
request = new ChangeUrlRequest(req, newUrl, newUri);
}
//Just to check if it works
req = (HttpServletRequest) request;
System.out.println(req.getRequestURL());
System.out.println(req.getRequestURI());
chain.doFilter(request, response);
Request wrapper:
class ChangeUrlRequest extends HttpServletRequestWrapper {
private StringBuffer newUrlBuffer;
private String newUri;
public ChangeUrlRequest(HttpServletRequest request,String newUrl, String newUri) {
super(request);
newUrlBuffer = new StringBuffer(newUrl);
this.newUri = newUri;
}
#Override
public StringBuffer getRequestURL() {
return newUrlBuffer;
}
#Override
public String getRequestURI() {
return newUri;
}
}
This works as expected in way that I can use .../organizer and is replaced by .../Organizer.html
The problem I have now is that even if the URL/URI is the same whether I access .../organizer or .../Organizer.html from browser, only the .../Organizer.html works and for .../organizer the Tomcat complains it cannot find the page: HTTP Status 404 - /my-page/Organizer.html
The output of System.out from the code above:
with Organizer.html (works):
URL: http://localhost:8080/my-page/Organizer.html
URI: /my-page/Organizer.html
with organizer (gives above mentiond 404):
URL: http://localhost:8080/my-page/Organizer.html
URI: /my-page/Organizer.html
Do I need to override or change also something else?
If the goal is to be able to access the app by either URL, maybe there is a simpler solution. You could write a servlet accessible at /organizer and forward the request to the HTML page, e.g.:
#WebServlet(urlPatterns="/organizer")
public class OrganizerServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) {
RequestDispatcher dispatcher = getServletContext().getRequestDispatcher("/Organizer.html");
dispatcher.forward(request,response);
}
}
Note that you may need to modify the paths for /organizer and /Organizer.html if they are not at the root of your application.

How to add a cookie using doTag method in custom tag?

I have developed a custom tag library in Java which I use in my web application.
I am not sure why but my doTag() is not setting up cookie at all. I have cleared my cache and restarted my computer as well. Here is the code:
public class UserVersionOfSite extends EvenSimplerTagSupport {
private static final Log logger = LogFactory.getLog(UserVersionOfSite.class);
private StringWriter sw = new StringWriter();
#Override
public void doTag() throws IOException, JspException {
getJspBody().invoke(sw); //get the tag body and put it in StringWriter object
//get request object to get cookie value
PageContext ctx = (PageContext)getJspContext();
HttpServletRequest httpServletRequest = (HttpServletRequest) ctx.getRequest();
HttpServletResponse httpServletResponse = (HttpServletResponse) ctx.getResponse();
if(httpServletRequest.getParameterMap().containsKey("show_full_site")) {
logger.debug("show_full_site ");
if(!checkIfCookieExists(httpServletRequest)){
Cookie cookie = new Cookie("SHOW_FULL_SITE",httpServletRequest.getParameter("show_full_site"));
cookie.setMaxAge(86400);
httpServletResponse.addCookie(cookie);
//write the tag output
if(!httpServletRequest.getParameter("show_full_site").equalsIgnoreCase("true")){
//write the response
getJspContext().getOut().println(sw.toString());
}
}else{
String cookieValueString = getCookieValue(httpServletRequest.getCookies(),"SHOW_FULL_SITE","false");
if(!cookieValueString.equalsIgnoreCase("true")){
//write the response
getJspContext().getOut().println(sw.toString());
}
}
}
}
#Override
public String getResult() throws IOException {
return "User version of site";
}
public String getCookieValue(Cookie[] cookies,
String cookieName,
String defaultValue) {
for(int i=0; i<cookies.length; i++) {
Cookie cookie = cookies[i];
if (cookieName.equals(cookie.getName()))
return(cookie.getValue());
}
return(defaultValue);
}
public boolean checkIfCookieExists(HttpServletRequest httpServletRequest){
logger.debug("inside checkIfCookieExists()");
boolean cookiePresent = Arrays.asList(httpServletRequest.getCookies()).contains( "SHOW_FULL_SITE" );
return cookiePresent;
}
}
Even I tried adding the code without using if else statements but still no success. Is there any thing critical I am missing?
Any ideas guys??!!! I have checked the browser's setting as well, but there is nothing there which is blocking a creation of cookie!
I realise the horse has probably bolted by the time I'm posting this but, for the benefit of others stumbling across it, I think the problem may be related to the feature of RequestDispatcher highlighted in this question: unable to add a cookie included in JSP via jsp:include
your following line inside checkIfCookieExists() method is wrong:
Arrays.asList(httpServletRequest.getCookies()).contains( "SHOW_FULL_SITE" );
HttpServletRequest.getCookies() returns Cookie[]. You are wrapping it inside a List and checking for a string "SHOW_FULL_SITE" inside this.
Coming back to your question- how do you know cookie is not being set in the HTTP headers? Try using browser plugins like firebug to see the HTTP response headers coming from server. Also set the path of cookie before adding it to response e.g.
Cookie cookie = new Cookie("SHOW_FULL_SITE",httpServletRequest.getParameter("show_full_site"));
cookie.setMaxAge(86400);
cookie.setPath("/");

Converting GET method url's to logical permanent links

I have a J2EE application with a web service which goes like
http://servername/service?task=getFile&id=25
How can I convert these type of urls to
http://servername/service/getFile/25
http://servername/service/getFile/26
etc?
Please provide your suggestions.
You can use the UrlRewriteFilter in order to achieve this. You will just have to write the rules for rewriting, similar to mod_rewrite. For example:
<rule>
<from>^/products/([0-9]+)$</from>
<to>/products/index.jsp?product_id=$1</to>
</rule>
To the point, you thus want to forward the friendly URL to an unfriendly URL (so that you don't need to change existing request parameter collecting logic of the servlet) and to redirect the unfriendly URL to an friendly URL (so that the friendly URL get reflected in the browser address bar of the client).
The best place for this is a Filter. To access the HttpServletRequest, just downcast ServletRequest to HttpServletRequest. You can get the query string by getQueryString() and you can get the pathinfo by getRequestURI(). Here's a kickoff example:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException
{
HttpServletRequest httpreq = (HttpServletRequest) request;
String query = httpreq.getQueryString();
if (query != null) {
// Unfriendly URL invoked. Convert params to pathinfo and redirect.
StringBuffer newURL = httpreq.getRequestURL();
for (String param : query.split("&")) {
newURL.append('/').append(param.substring(param.indexOf('=') + 1));
}
((HttpServletResponse) response).sendRedirect(newURL.toString());
} else {
// Friendly URL invoked. Convert pathinfo to params and forward.
String[] parts = httpreq.getRequestURI().replace(httpreq.getContextPath(), "").split("/");
String newURL = String.format("%s?task=%s&id=%s", parts[1], parts[2], parts[3]);
httpreq.getRequestDispatcher(newURL).forward(request, response);
}
}
You can of course also grab the aforementioned UrlRewriteFilter.

Categories

Resources