I'm working on a project that uses the Spring Portlet-MVC framework and Velocity on a Liferay Portal server. For a few pages we have the requirement to serve them on a secure connection. Being fairly new to Portlets I came up with the solution of linking to an Action-Method and redirecting from there.
#ActionMapping(params = "command=secureRedirect")
public void actionSecureRedirect(ActionRequest request, ActionResponse response) {
HttpServletRequest servletRequest = PortalUtil.getHttpServletRequest(request);
String absoluteUrl = servletRequest.getRequestURL().toString();
String[] urlComponents = StringUtils.split(absoluteUrl, '/');
StringBuffer redirectUrl = new StringBuffer("https://");
redirectUrl.append(urlComponents[1]);
redirectUrl.append("<specificPath>");
response.sendRedirect(redirectUrl.toString());
}
My solution works but to me it doesn't seem really nice. I was wondering if somebody could think of another, more transparent way to do this (using Interceptors and Annotations on RenderMappings maybe?).
Any suggestions will be greatly appreciated!
By some pages, do you mean Liferay pages or are you just concerned about the url that gets generated when a user clicks on some link from the portlet.
If you want to make some link of the portlet secure, then when use liferay-portlet flavour or renderURL or actionURL. It has an attribute called secure, which if set to true will make your url starting with https
If you are looking for some liferay page(for example /web/guest/mypage) to be secure then its kind of a hack and I wouldnt really suggest this to anyone, but if you have got no other option, you can create a service pre hook and check for url patterns you are concerned about and redirect to an https version of that url.
write this code in controller
protected PortletURL getRedirectURL(ActionRequest actionRequest) {
ThemeDisplay themeDisplay = (ThemeDisplay) actionRequest.getAttribute(WebKeys.THEME_DISPLAY);
String portletName = (String) actionRequest.getAttribute(WebKeys.PORTLET_ID);
PortletURL redirectURL = PortletURLFactoryUtil.create(PortalUtil.getHttpServletRequest(actionRequest), portletName, themeDisplay.getLayout().getPlid(),
PortletRequest.RENDER_PHASE);
return redirectURL;
}
#ActionMapping(params="something")
public void save(ActionRequest actionRequest, Other parameters){
/.....Your code
.....//
redirectURL = getRedirectURL(actionRequest);
actionResponse.sendRedirect(redirectURL.toString());
}
Related
I'm a bit new to microservices and Spring. I have Spring Cloud microservices (ports: 8xxx-8xxx) with a Zuul gateway running on port 9000. There's a method inside a controller on a UI service which should do a login and then return to a index.html page:
#RequestMapping(value="/do-login", method = RequestMethod.POST)
public RedirectView doLogin (#ModelAttribute("authEntity") final AuthEntity authEntity, final Model model) {
model.addAttribute(VERSION, applicationVersion);
model.addAttribute("authEntity", new AuthEntity());
authenticatedStatus = true;
model.addAttribute(AUTHENTICATED, authenticatedStatus);
return new RedirectView("index");
}
The problem is that when above method completes it returns an url of the microservice itself localhost:8888/index but not localhost:9000/services/ui/.
If I use a simpler method:
#RequestMapping(value="/do-login", method = RequestMethod.POST)
public String doLogin (#ModelAttribute("authEntity") final AuthEntity authEntity, final Model model) {
model.addAttribute(VERSION, applicationVersion);
model.addAttribute("authEntity", new AuthEntity());
authenticatedStatus = true;
model.addAttribute(AUTHENTICATED, authenticatedStatus);
return "index";
}
This returns correctly an url of gateway localhost:9000/services/ui/do-login but with a /do-login which I do not need.
Maybe I can get rid of /do-login/ part of url? Or maybe there is a solution for the incorrect redirect?
Thanks in advance!
If you use relative path like in return "index"; the result of the POST request sent to http://localhost:9000/services/ui/do-login will include URLs to http://localhost:9000/... unless coded otherwise in the jsp / freemarker / thymeleaf file.
If you want to get rid of the do-login, you would need to implement what's called a Redirect After Post (or redirect after form submit) approach so that a page refresh doesn't resubmit the form. If you take this approach, which seem what you were doing when using: return new RedirectView("index");, I can think of a couple ways of fixing the URL and set it to the proxy host.
1) http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/view/RedirectView.html, there are a couple of constructors that takes a host parameter, you would need to inject the proxy host in the controller class and most-likely in every controller class that implements Redirect After Post.
2) http://tuckey.org/urlrewrite/, include UrlRewriteFilter and configure rules to rewrite from webapp host to proxy host when webapp http status code response is 302. With this approach it would only be once rule and no need to inject proxy host to controller classes or change the return new RedirectView("index");`
3) Maybe this rewriting is implemented in Zuul and you don't need include and configure UrlRewriteFilter as suggested in 2).
As a side note, I have configured Nginx's proxy_pass to a Java webapps (where I implemented Redirect After Post) in the past and I don't recall having this issue. Will have to take a look at both UrlRewriteFilter and Nginx config files to expand on this.
I found that this (thanks to answer in here: Spring redirect url issue when behind Zuul proxy) seems to work as required (but is considered a 'workaround'):
#RequestMapping(value="/do-login", method = RequestMethod.POST)
public void doLogin (#ModelAttribute("authEntity") final AuthEntity authEntity,
final Model model,
HttpServletResponse servletResponse) throws IOException {
...
String rUrl = ServletUriComponentsBuilder.fromCurrentContextPath().path("/").build().toUriString();
servletResponse.sendRedirect(rUrl);
}
I need to redirect the url according to the username in the url.
Let say, url is of the form of
get http://192.168.2.0/user1/app/node-id/info
then,
it should redirect this url to different server like,
get http://192.168.2.1/app/node-id/info
The client should be unaware of the fact, it is being redirected.
He/She should see the same url, but the response should come from the redirected url in Json or html format.
Which we can perform via request forwarding.
Every where I am able to see the solution servlet way.
What is the best way to do it spring way?
Java servlets have redirect and forward methods. Difference between them are explained on here and you can use them as below; redirect changes the url but forward processes the request internal.
If you want to redirect users to a remote url check this Q&A and maybe to redirect without changing browser's address, the easy way is reading it with url and writing it to response on servlet
private void redirect(ResponsePage aDestinationPage, HttpServletResponse aResponse) throws IOException {
String urlWithSessionID = aResponse.encodeRedirectURL(aDestinationPage.toString());
aResponse.sendRedirect( urlWithSessionID );
}
private void forward(ResponsePage aResponsePage, HttpServletRequest aRequest, HttpServletResponse aResponse) throws ServletException, IOException {
RequestDispatcher dispatcher = aRequest.getRequestDispatcher(aResponsePage.toString());
dispatcher.forward(aRequest, aResponse);
}
In addition for usernames on url addresses, you can use filters and redirect them as your wish. For using spring with filter please check here and here
I want to use a normal spring mvc controler and request mapping using path variables.
I do not want to forward or redirect, just change the string that user sees.
#RequestMapping(value = "/Foo/{id}/*", method = RequestMethod.GET)
public ModelAndView getFoo(#PathVariable final String friendlyUrl) {
//how can I rewite the url that user sees ?
}
(the same behaviour as when you change the title of an existing question on stackoverflow)
If you watch the traffic in wireshark, firebug or something you see, that stackoverflow sends a HTTP 301 Moved Permanently to the final URL.
You could do the same.
For this you need the HttpServletResponse, you can add it to the method signature to get it injected.
Set the permanent redirect:
String rightUrl = urlCompleter.complete(friendlyUrl);
response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
response.setHeader("Location", rightUrl);
Where you need to implement urlCompleter on your own, eg. look in the database table of entries and locate the right url component.
I'd like to find the absolute URL of the webapp in Spring, from the Controller. I'm aware of JSTL c:url, but I need this info from inside the Controller.
#Controller
public class AuthorizeController {
#Autowired
private Authorizer auth;
#RequestMapping("/auth")
public String sendToAuthorization() {
String baseUrl = "http://localhost:8080/tasks/";
return "redirect:" + auth.getAuthorizationUrl(baseUrl);
}
}
As you can see the baseUrl is hardcoded, and I could provide it to the Authorizer class via Spring configuration, but I am sure that it's possible to get this information from Spring within the Controller. I tried to google "spring mvc url" and could not find a way to solve this problem.
I think that getting absolute url is only possible while processing the request as your server may have many IP addresses and domain names.
#RequestMapping("/auth")
public String sendToAuthorization(HttpServletRequest request) {
String baseUrl = String.format("%s://%s:%d/tasks/",request.getScheme(), request.getServerName(), request.getServerPort());
return "redirect:" + auth.getAuthorizationUrl(baseUrl);
}
As for the servlet, it may also have several mappings in web.xml.
similar question
P.S. Anyway, url parsing in runtime does not look like a good idea to me.
Very late to this answer, but a variant to Boris's answer, if you don't want to push servlet objects into method signatures, is to use RequestContextHolder from a utility class/method. This would also give the ability to abstract fallback logic (e.g., pulling from a property file). Cheesy example:
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if(null != requestAttributes && requestAttributes instanceof ServletRequestAttributes) {
HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest();
// build URL from request
}
else {
// fallback logic if request won't work...
}
This presumes you have org.springframework.web.context.request.RequestContextListener registered as a listener in web.xml
I just want a Tapestry page to redirect to a static page like this :
http://www.myWebSite.com/home/myPage.tml
-> http://www.myWebSite.com/static/myStaticPage.html
I try to do this by returning a new URL, but i need to know the web site address for that (http://www.myWebSite.com/). So, i would like to know how to do this without knowing the web site address ?
Thank you.
You can inject (using #Inject) the HttpServletRequest directly in your page directly, without using RequestGlobals, and use its getServerName() method to get the server name. Not tested:
#Inject
private HttpServletRequest request;
Object onActivate() {
return new java.net.URL("http://" + request.getServerName() " + "/myStaticPage.html");
}
Found : using the RequestGlobals service
String baseUrl = requestGlobals.getHTTPServletRequest().getRequestURL().toString().replaceFirst(requestGlobals.getHTTPServletRequest().getRequestURI(), "");
Just use it to build your URL string put it in a URL instance.