How do I forward to another URL from a Jetty handler? - java

Somewhere in our chain of servlet filters there is a filter which forwards the request to the sign-in page when a 401 error is sent, as a usability tweak.
I'm trying to convert this to a Jetty handler because someone wants all web applications to be authenticated by the same logic instead of every webapp having to implement their own authentication.
(The main reason we're using a filter approach in the first place is that nobody was able to get Jetty's container-level authentication to work at all - we have the ability to choose Windows auth or built-in auth and want to be able to switch between these at runtime and were never able to figure out how to make that work with Jetty.)
Out in the Jetty handler, there is some logic like this:
private void handleErrorBetter(HttpServletRequest servletRequest,
HttpServletResponse servletResponse)
throws ServletException, IOException {
if (isPageRequest(servletRequest)) {
ServletContext rootContext = servletRequest.getServletContext().getContext("/");
RequestDispatcher dispatcher = rootContext.getRequestDispatcher("/sign_in");
dispatcher.forward(servletRequest, servletResponse);
} else {
// ...
}
}
servletRequest.getServletContext() appears to correctly return the context for /. Interestingly it appears to do this even if I make a request for a different webapp, but according to the Javadoc I have to use getContext("/") to be sure that I get the root context, so I'm doing that.
Getting the dispatcher succeeds too.
Then I call forward() and this always returns a 404 response to the client.
If I go to /sign_in directly from a web browser, the form loads.
There are only two contexts on the server: the root context /, and a /sample/ context which I'm using to test the second webapp. So I know that /sign_in will be in the root context, but why does forward() give a 404 when forwarding to it?

It turned out to be a bug.
https://bugs.eclipse.org/bugs/show_bug.cgi?id=386359

Related

capturing relevant user requests using filter/interceptors

I am capturing URLs requested for my website. I need to see what all pages were requested from my website.
To achieve this I created a basic filter, and started logging page requests from there.
Now, this filter catches all the requests specific to a page.
For e.g. abc.com/page1, abc.com/resources/myjs.js, etc.
My problem is that for each page request, subsequent resources(js,css) are requested too. I want to capture only the relevant requests.
Right now, I check for patterns like /resources to ignore such requests, but I am looking for a more clean approach.
Also, will interceptors be more useful here?
I have seen filter patterns as well. But those are not useful, since I would have to create patterns for my filter.
If you want to capture the urls accessed from your website, you can configure spring boot to generate access logs in following way until you don't want more advance information:
server.tomcat.accesslog.directory=/logs/ #absolute directory path for log files\
server.tomcat.accesslog.enabled=false #Enable access log.
server.tomcat.accesslog.pattern=any_pattern # log string format pattern for access logs.
To perform any operation based on any request pattern, you can go ahead with filters.
I'm using filters for such requirements in following way:
public class CustomFilter extends OncePerRequestFilter{
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String url = request.getRequestURI();
if (url.startsWith("/resources/")) {
//your business logic
}
filterChain.doFilter(request,response);
}
}

Spring Security - api gateway pattern - bug?

We've created an application that mimics the Spring Security - API Gate Pattern tutorial (https://spring.io/guides/tutorials/spring-security-and-angular-js/#_the_api_gateway_pattern_angular_js_and_spring_security_part_iv). The only variation is that we're using a MySQL database rather than Redis.
Using localhost:8080 as the root, we have localhost:8080/login (login page), localhost:8080/ui (jQuery client), and localhost:8080/api (restful web services, business logic, etc.)
We're finding session handling and forwarding to the various entities works as expected. Meaning the session gets created as expected, forwarding is happening as expected, etc. There is one exception. If I log in, then log out, then go directly to localhost:8080/ui it'll forward me to the login page. You login, and it forwards you back to the localhost:8080/ui, but will display "ACCESS DENIED"!
After tracking the sessions in the database and client I've found that there are two sessions that exist in the database. One with permissions and one without. The client retains the one without!
Has anyone else run into this problem? Is there a way to circumvent this?
Here's a list steps I went through, the database session tracking, and client verification.
session_id principal_name Client
------------------------------------------------------------
1) go to localhost:8080
9229045c-27e0-410a-8711-45c56576d647 - X
2) login
2275db1c-fca4-4a2f-be73-e440599499d6 root X
3) logout
cc917e68-b1c0-46a4-bbe3-6705ccf7a5fa - X
4) go to localhost:8080/ui --> forwards to localhost:8080/login
cc917e68-b1c0-46a4-bbe3-6705ccf7a5fa - X
5) login -> forwards to localhost:8080/ui -> Access Denied
90d7931d-b265-42e2-a225-286bcf7d159c - X
d2fae0ac-9cf9-4287-8e38-51f64b0ab28d root
Alright, after many hours we found a solution to what seemed to be inconsistent behavior. Meaning sometimes you'd log in and it'd retain the proper session and you could go the the localhost:8080/ui page and not get the Whitelabel Error page... sometimes you'd still get it.
On the Gateway server...
1) Added RequestMethod.POST
#Controller
public class HomeController {
#RequestMapping(method = { RequestMethod.GET, RequestMethod.POST }, path = "/")
public String home() {
return "redirect:" + RequestMappings.UI;
}
}
2) Changed configure file, specifically
a) added .successForwardUrl(“/”)
b) added .loginProcessingUrl(“/login”)
c) added .logoutSuccessUrl("/login?logout")
#Override
protected void configure(HttpSecurity http) throws Exception {
http.headers()
.frameOptions().sameOrigin()
.and().formLogin()
.loginPage(RequestMappings.LOGIN)
.failureHandler(failureHandler())
.successForwardUrl("/")
.permitAll()
.loginProcessingUrl("/login")
.and().logout()
.logoutSuccessUrl("/login?logout")
.and().authorizeRequests()
.antMatchers("/login").permitAll()
.antMatchers(RequestMappings.CHANGE_PASSWORD).permitAll()
.anyRequest().authenticated()
.and().csrf()
.csrfTokenRepository(csrfTokenRepository())
.and().addFilterAfter(csrfHeaderFilter(), SessionManagementFilter.class);
}
Now, there still is a way to get the whitepage error. If, before ever logging in, you go directly to localhost:8080/ui…. It’ll forward you to the localhost:8080/login page. You log in. You’ll be at localhost:8080/ui/ looking at everything as expected. If you remove the last forward slash then you’ll get the whitepage error. Then from there things can get mucked up in the cache. But if you go back to the root, you can login as normal and everything will work as normal.
I think what is going on is that the pre-login localhost:8080/ui call is being cached and because the index.html page was never loaded once you log back in and go back you pass the authorization check, but it tries to load… well, nothing, then throws an error. At least that’s my best guess.
Anyways, cheers! Thanks for the help, which started us off on the right track!
Most likely, when you go directly back to your UI application, it is still using the old session ID and when you log in, it is redirecting you to the UI again with the old session ID.
It could also be that it is still an old issue on Tomcat, check this thread to see how to properly clear your cookies: https://straypixels.net/clearing-cookies-in-spring-tomcat/
Bascially, extend the SimpleUrlLogoutSuccessHandler like this:
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
//This search may or may not actually be necessary, you might be able to just
// create a new cookie with the right name
for(Cookie cookie : request.getCookies()) {
if(cookie.getName() == "JSESSIONID") {
//Clear one cookie
cookie.setName("");
cookie.setMaxAge(0);
cookie.setPath(request.getContextPath());
response.addCookie(cookie);
//Clear the other cookie
Cookie cookieWithSlash = cookie.clone();
cookieWithSlash.setPath(request.getContextPath() + "/");
response.addCookie(cookieWithSlash);
}
}
//This is actually a filter; continue along the chain
super.onLogoutSuccess(request, response, authentication);
}

Cloud Endpoints: Access Paramters in Servlet Filter

I'm trying to build an api with Google Cloud Endpoints.
As Cloud Endpoints does not provide authentication beside Googles own OAuth I try to build my own. Therefore I want to access the parameters provided for the API (for example #Named("token") token) inside a servlet filter.
Unfortunately I cannot find any of the provided information inside the httpRequest. Is that normal? Is there a possibility to access the parameters?
I would appreciate if someone could help me!
UPDATE:
With the infos from jirungaray I tried to build an authentication using headers but ran into the same problem. Used a REST-Client to send some headers as I could not figure out how to do this with the API Explorer. Inside my filter I try to access the token from the headers:
#Override
public void doFilter(
ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String authToken = httpRequest.getHeader(Constants.AUTH_TOKEN);
...
chain.doFilter(request, response);
}
The reason why I try to do something like this, is that I'm using Guice for Dependency Injection and want my token to be injected inside another object.
With Guice I have the following Provider using the token to inject a FacebookClient (using the token) per request.
#Provides
public FacebookClient getFacebookClientProvider(#Named("fbToken") Provider<String> fbToken) {
return new DefaultFacebookClient(fbToken.get(), Version.VERSION_2_2);
}
As described in the Guice wiki (SevletModule) this uses a sevlet filter to get the information from the request.
Is there any solution to achieve this kind of DI with Cloud Endpoints?
Philip,
Yes, it does makes sense you are getting an empty request. Your endpoint calls are first handled by Google (they receive the API calls) and then those are processed and sent to a handler in your app. As this is all done in the background it's very easy to miss that your endpoints aren't actually getting the same request you sent, they get a completely different request sent from Google's infrastructure.
Even though your approach should work including tokens info in url makes them easier to sniff, even if you use SSL or encrypt your params the token is there in plain sight.
For what you are trying to achieve I recommend you include the token as a header in your request and retrieve that header by accessing the HTTPRequest directly on the endpoint, this is injected automatically if you include an HTTPServletRequest param in you endpoint method.
eg.
public APIResponse doSomething(SomeComplexRquestModel request,
HttpServletRequest rawRequest) {
}
If you still feel you should go with your original approach just comment and I'll help you debug the issue.

Redirecting HTML page only once

I have Spring app that uses JSP. And I need to redirect one page, but only once. It's due the spring security - once the user comes to this page, I need to call redirection to log him out, but to stay on the same page. Is there any way, how to do this only via java, html or js? Or maybe I've asked wrong - there must be some way, how to do this, but I'm not very into frontend technologies, so I'd be glad, if anybody could post some code to show me, how to make this work.. Thanks :)
So the usecase: user types mywebsite.com/login -> I need to call redirect to log him out in the case he's already logged -> the same site shall appear but now without redirection.
PS: I can't redirect the user to another site - it must be the same site with same source code.
If I understand your use case, I would rather log out a user as a part of the login action (i.e. factor out the logging out process, and call it from both the logout action, as well as from the login action).
If that is not feasible (and I can't of a reason why it wouldn't be), I would focus on "redirect if he's logged in" part rather than "redirect only once" part - the former is very easy to check without almost any modification to your code, assuming you're using some kind of sessions to track the logged-in status.
Disclaimer: I know next to nothing about Sprint, these are all very general comments that should work on any classic webapp.
You can use filter .
#Override
public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) throws IOException, ServletException {
HttpServletRequest test1= (HttpServletRequest) arg0;
test1.getRequestURL()); it gives http://localhost:8081/applicationName/menu/index.action
test1.getRequestURI()); it gives applicationName/menu/index.action
String pathname = test1.getServletPath()); it gives //menu/index.action
if(pathname.equals("//menu/index.action")){
arg2.doFilter(arg0, arg1); // call to urs servlet or frameowrk managed controller method
// in resposne
HttpServletResponse httpResp = (HttpServletResponse) arg1;
RequestDispatcher rd = arg0.getRequestDispatcher("another.jsp"); redirect to another page at response time
rd.forward(arg0, arg1);
}
donot forget to put <dispatcher>FORWARD</dispatcher> in filter mapping in web.xml
If you do not want to use filter you can redirect to another jsp from urs controller method.

Forward request from a filter

I need to forward my request (to a jsp but I don't think it's matter) from an http.Filter
if the URI of the original request pass some validation that my filter runs.
I found this page that faced similar task
Still I need to figure the following:
How can I get ServletContext in doFilter() method (in order to call forward API) getServletContext() is not recignized
Do I have to call chain.doFilter() before the forward, after the forward or not at all?
In addition do I have to call chain.doFilter() if my validation passed or only if it fails (because in this case I won't continue to forward my page)?
This question actually continue this thread,
to be more obvious, the code could be something like:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (request instanceof HttpServletRequest) {
HttpServletRequest httpServletRequest = ((HttpServletRequest)request);
String requestURI = httpServletRequest.getRequestURI();
String contextPath = httpServletRequest.getContextPath();
if (<this is my implementation of the validation of this filter>){
getServletContext().getRequestDispatcher(
"MySpecific.jsp").forward(request,response);
}
}
chain.doFilter(request,response);
}
How can I get ServletContext in doFilter() method?
httpServletRequest.getSession().getServletContext();
Do I have to call chain.doFilter() before the forward, after the forward or not at all? In addition do I have to call chain.doFilter() if my validation passed or only if it fails (because in this case I won't continue to forward my page)?
I would say that if you forwarded the request, you should not call chain.doFilter() - the forwarded request will get filtered according to its own filter configuration. If your validation failed though, it depends on what the semantics of your web app are - if the original page is some sort of general error/login/welcome screen, you may want to continue to that when the validation failed. It is hard to say without knowing more of the context.
To get the ServletContext, you've got 2 options:
Store off the FilterConfig during the initialization and call FilterConfig.getServletContext()
call HttpServletRequest.getSession().getServletContext()
I don't think you necessarily need the ServletContext to get the RequestDispatcher as you could just call HttpServletRequest.getRequestDispatcher().
In relation to FilterChain.doFilter() call, if you're forwarding, I would think you wouldn't make the call, as once you forward, I assume you don't want any of the standard behavior to take place.
If you don't forward (you don't fall into your if block), then I'd call the FilterChain.doFilter() method, however that assumes there is a target on the other end to be invoked.

Categories

Resources