Showing posts with label J2EE. Show all posts
Showing posts with label J2EE. Show all posts

Sunday, July 19, 2015

Infinite loop in Filter after sendRedirect()

My intention is to redirect to another site whenever the User-Agent has detected the mobile keyword. I have a solution for this approach where I have the file name for mobile version start with m and reside at the location same as desktop version. I try not to branch out another path for mobile version to keep maintenance easy.

Unfortunately the Filter call was unintentionally looping on itself. This seems to me that for every new redirection Filter call, the mobile keyword validation will get invoked and the new redirection is sent again. And this process will go on endlessly. This is so not good, I was trapped!

This is what happened on my code:
 @Override
 public void doFilter(ServletRequest request, ServletResponse response,
   FilterChain chain) throws IOException, ServletException {

  HttpServletRequest httpServletRequest = (HttpServletRequest) request;
  HttpServletResponse httpServletResponse = (HttpServletResponse) response;

  if( httpServletRequest.getHeader("User-Agent").indexOf("Mobile") != -1 ) {
   
   httpServletResponse.sendRedirect(httpServletRequest.getContextPath() + "/m" + httpServletRequest.getRequestURI().substring(httpServletRequest.getRequestURI().lastIndexOf("/")+1, httpServletRequest.getRequestURI().length()));
  }
  else {  
   chain.doFilter(request, response);
  }
According to the advice from expert. I'm going to need additional validation to stop the process after I reach to that URI. Thus, what I need to do is to put additional validation as below:
 @Override
 public void doFilter(ServletRequest request, ServletResponse response,
   FilterChain chain) throws IOException, ServletException {

  HttpServletRequest httpServletRequest = (HttpServletRequest) request;
  HttpServletResponse httpServletResponse = (HttpServletResponse) response;

  if( httpServletRequest.getHeader("User-Agent").indexOf("Mobile") != -1 ) {

   // stop redirect if the site has reach
   if( httpServletRequest.getRequestURI().equalsIgnoreCase(httpServletRequest.getContextPath() + "/" + httpServletRequest.getRequestURI().substring(httpServletRequest.getRequestURI().lastIndexOf("/")+1, httpServletRequest.getRequestURI().length()))) {
    chain.doFilter(request, response);
   }
   else {
    httpServletResponse.sendRedirect(httpServletRequest.getContextPath() + "/m" + httpServletRequest.getRequestURI().substring(httpServletRequest.getRequestURI().lastIndexOf("/")+1, httpServletRequest.getRequestURI().length()));
   }
  }
  else {  
   chain.doFilter(request, response);
  }
Tested and worked as expected. I did also tried to forward the request as shown below:
 @Override
 public void doFilter(ServletRequest request, ServletResponse response,
   FilterChain chain) throws IOException, ServletException {

  HttpServletRequest httpServletRequest = (HttpServletRequest) request;
  HttpServletResponse httpServletResponse = (HttpServletResponse) response;

  if( httpServletRequest.getHeader("User-Agent").indexOf("Mobile") != -1 ) {
   
   request.getRequestDispatcher("m" + httpServletRequest.getRequestURI().substring(httpServletRequest.getRequestURI().lastIndexOf("/")+1, httpServletRequest.getRequestURI().length())).forward(request, response);
  }
  else {  
   chain.doFilter(request, response);
  }
I find this working as well but for subsequent call on doFilter, the httpServletRequest.getRequestURI() will always stuck on the old URI. I think this is due to the reason URL is forward on server site, but not reflected on client site.

Friday, July 10, 2015

Detecting web's mobile version with Filter

What a boring day, while I was dreaming on my desk, something has caught my attention. I realize that in most of the time, my web application was run on desktop platform, and never know what would happen if I run the it on mobile platform? I was thinking this could be a disaster for user. But how? Could I do the validation checking in controller bean? Just like this:
If mobile device then render mobile site otherwise render desktop site.
But in reality, what I got from the Internet majority are done using JavaScript, some use CSS to accomplished the task. But this doesn't help because mine technology used was JSF and J2EE stuff. Thus I would use Filter for my case. See my action on following content:
package org.huahsin;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebFilter(filterName="MyFilter", urlPatterns={"/pages/*", "*.xhtml"})
public class MyFilter implements Filter {

 @Override
 public void init(FilterConfig filterConfig) throws ServletException {
  // TODO Auto-generated method stub

 }

 @Override
 public void doFilter(ServletRequest request, ServletResponse response,
   FilterChain chain) throws IOException, ServletException {
  // TODO Auto-generated method stub
  
  HttpServletRequest httpServletRequest = (HttpServletRequest) request;
  HttpServletResponse httpServletResponse = (HttpServletResponse) response;
  
  // redirect to mobile version
  if( httpServletRequest.getHeader("User-Agent").indexOf("Mobile") != -1 ) {
    ...
    ...
  }
  else {  
   chain.doFilter(request, response);  // forward to original request
  }
 }

 @Override
 public void destroy() {
  // TODO Auto-generated method stub

 }

}
This code will easily know where does the request come from. I'm so curious to know what is actually hiding in User-Agent? Did a quick diagnose on it and found the following string will be retrieve when the site was render on desktop browser:
Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:38.0) Gecko/20100101 Firefox/38.0
And this is the string I got if I render the site on iPad:
Mozilla/5.0 (iPad; CPU OS 8_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12F69 Safari/600.1.4

Tuesday, June 30, 2015

Handling logout mechanism in J2EE way

Last time when I was working with Spring, the logout was handled in such a way:
  <http auto-config="true">
    ...

    <logout logout-success-url="/pages/login.xhtml?faces-redirect=true">
  </http>
But now I'm doing it in pure J2EE way, thus this is what I got:
 <h:form>
  <p:commandbutton action="#{myController.logout}" value="logout" />
 </h:form>
@ManagedBean
@RequestScoped
public class MyController {

 ...
 ...
 
 public void logout() throws IOException {
  ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
  ec.invalidateSession();
  ec.redirect(ec.getRequestContextPath() + "/login.xhtml");
 }
}
Notice the huge difference? With Spring, one statement rules the world.

How could I wire j_username and j_password in managed bean?

Two things required in a typical design of an authentication mechanism in J2EE 6; one is the security-constraint in Deployment Descriptor as shown below:
...

 <security-constraint>
  <web-resource-collection>
   <web-resource-name>Authentication</web-resource-name>
   <description>Please login</description>
   <url-pattern>/pages/*</url-pattern>
   <http-method>GET</http-method>
   <http-method>POST</http-method>
  </web-resource-collection>
  <auth-constraint>
   <role-name>Junior</role-name>
  </auth-constraint>
 </security-constraint>

 <login-config>
  <auth-method>FORM</auth-method>
  <form-login-config>
   <form-login-page>/login.xhtml</form-login-page>
   <form-error-page>/error.xhtml</form-error-page>
  </form-login-config>
 </login-config>
 
 <security-role>
  <role-name>Junior</role-name>
 </security-role>
Second would be the form in login page:
...

 <form method="post" action="j_security_check">
  <h:outputLabel value="Username: " />
  <p:inputText id="j_username" />
  <h:outputLabel value="Password: " />
     <p:password id="j_password" />
     <p:commandButton action="#{myController.link}" value="please login"/>
     
 </form>
As in the value shown in url-pattern, I have my protected page sit inside this directory. Whereas the login page doesn't need to be inside that directory, it would be best to put in the root of WebContent. The managed bean that wired the login page is as follow:
@ManagedBean
@RequestScoped
public class MyController {

 public void link() {
   FacesContext.getCurrentInstance().getExternalContext().redirect("landingpage.xhtml");
 }
}
But then I was wondering whether I'm doing the login page correctly as according to the J2EE 6 standard? Assuming I'm wrong, how could I move j_username, and j_password into a managed bean? After some research on this matter, there are two things need to be done in order to meet my objective. First, the form is no longer require to send post method to j_security_check:
...

 <h:form>
     <h:outputLabel value="Username: " />
     <p:inputText id="username" value="#{myController.username}" />
     <h:outputLabel value="Password: " />
     <p:password id="password" value="#{myController.password}" />
     <p:commandButton action="#{myController.link}" value="link"/>
 </h:form>
And the login process was handled in the managed bean as shown below:
@ManagedBean
@RequestScoped
public class MyController {
 private String username;
 private String password;

 public void link() {
  HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
  
  try {
   request.login(username, password);
   
   FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put("user", username);
   FacesContext.getCurrentInstance().getExternalContext().redirect("landingpage.xhtml");
  }
  catch( ServletException e ) {
   ...
  }
 }
}
See it? The j_username, and j_password has been replace by the username and password wired inside the managed bean now. And the form is no longer contain any j_ fields.

Saturday, January 18, 2014

How to configure Tomcat to support JEE6?

I have a servlet program create under Tomcat 6 environment, I found it interesting there was an error on login() and logout() as these methods are undefined.
protected void processRequest(HttpServletRequest request, HttpServletResponse response) {
    ...
    request.login(userName, password);
    ...
    ...
    request.logout();
}
As I check in the documentation in JEE6, the login() and logout() was there but not in JEE5. I did a check on the project facet in Eclipse IDE, the project was using Dynamic Web Module 2.5 and Java 1.6. Can I conclude that my tomcat is actually working with JEE5? I am so curious what else did I miss configure in order to support JEE6?

As I did a deep search, this can not be done. Why? Because Tomcat isn't an enterprise server. I found the clue from here. To proof me right, I run quick test on WAS Liberty Profile and Tomcat EE by configuring the target runtime in Eclipse IDE. Both of them compile without any error. Cheers!

By the way, I though I suppose to aware of this since I have been doing enterprise software for 3 years?

Sunday, June 2, 2013

JAAS is not for human being

I have spend quite a number of months on this JAAS topic already, but at last I am still failed to configure it on the web project. I have been trying so hard to get it work, and have read tons of resources on the configuration, and now I damn tired with this JAAS (on the web). During this experiment, I found out that JAAS is just a low level security framework to secure the resources beyond the web application level. It just too low until the level that I am require to configure it in the server. Frankly speaking, such a low level configuration isn't my favorite due to its maintenance effort.

Take for example, I'm required to put the following code in catalina.policy under <tomcat_dir>/conf:

export JAVA_OPTS=-Djava.security.auth.login.config==$CATALINA_HOME/conf/login.config

I found this is so not programmer friendly when come to development. Anyhow there is a workaround, put the following code inside the Eclipse's server launch configuration under the VM arguments:

-Djava.security.auth.login.config= "&It;tomcat_dir>\conf\login.config"

Launch the server will see my expected login page, type in the correct username and password, the browser will redirect me to HTTP Status 403 - Access to the requested resource has been denied.

What else do I miss configure? I think I will just forget about JAAS thing since Spring Security can achieve my objective easily.