0
votes

I am having a problem creating custom securuity filter. I intend to create a Filter which would check with the server the sessionId which is passed in the URL - for authentication & authorization. I intend to use the exiting JSF framework already in the code (like caching for sessions, etc)

For it I first define an bean which is application scoped. The faces-config.xml looks like:

<managed-bean eager="true">
<managed-bean-name>securityBean</managed-bean-name> <managed-bean-class>com.ecom.scareer.security.SecurityBean</managed-bean-class> <managed-bean-scope>application </managed-bean-scope>
<managed-property> <property-name>loginBD</property-name> <value>#{loginBD}</value> </managed-property>
</managed-bean>

and then define a class called SecurityBean as:

public class SecurityBean implements Serializable {


// attributes
private LoginBD loginBD;    

public void setLoginBD(LoginBD loginBD) {
this.loginBD = loginBD;
}


public UserSession getUserSessionById(String sessionId) throws InvalidSessionException{             

// get the session id
UserSession us = loginBD.getSession(sessionId);

return us;
}

}

and I have a filter as :

public class SecurityFilter implements Filter{

private ServletContext servletContext;

// --------------- overridden method for Filter interface --------------- 

public void init(FilterConfig filterConfig) throws ServletException {
    servletContext = filterConfig.getServletContext(); 
}

public void doFilter(ServletRequest request, ServletResponse response,
        FilterChain chain) throws IOException, ServletException {

    Map<String, String[]> paramMap = request.getParameterMap();

    FacesContext facesContext = getFacesContext(request, response);

    // get the application bean for security         
    SecurityBean sb = (SecurityBean)getApplicationBean("securityBean", facesContext);

    // values needed from the parameter
    String[] vals = paramMap.get("accountId");
    String accId = (vals != null && vals.length > 0)? vals[0] : null;
    System.out.println("Account id .................. = " +  accId);

    String[] sessionIds = paramMap.get("sessionId");
    String sessionId = (sessionIds != null && sessionIds.length > 0)? sessionIds[0] : null;

    System.out.println("The session ...................... = " + sessionId);

    try {

        UserSession us = sb.getUserSessionById(sessionId);
    } catch (InvalidSessionException e) {
        System.out.println("Invalid session exception ");
        e.printStackTrace();
    }

    chain.doFilter(request, response);              
}

public void destroy() {
    // do nothing       
}


// --------------- for fetching the FacesContext


// You need an inner class to be able to call FacesContext.setCurrentInstance
// since it's a protected method
private abstract static class InnerFacesContext extends FacesContext {
    protected static void setFacesContextAsCurrentInstance(FacesContext facesContext) {
        FacesContext.setCurrentInstance(facesContext);
    }
}   


private FacesContext getFacesContext(ServletRequest request, ServletResponse response) {
    // Try to get it first  
    FacesContext facesContext = FacesContext.getCurrentInstance();
    if (facesContext != null) return facesContext;

    FacesContextFactory contextFactory = (FacesContextFactory)FactoryFinder.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
    LifecycleFactory lifecycleFactory = (LifecycleFactory)FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);    
    Lifecycle lifecycle = lifecycleFactory.getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);

    // Either set a private member servletContext = filterConfig.getServletContext(); 
    // in you filter init() method or set it here like this:
    // ServletContext servletContext = ((HttpServletRequest)request).getSession().getServletContext();
    // Note that the above line would fail if you are using any other protocol than http

    // Doesn't set this instance as the current instance of FacesContext.getCurrentInstance 
    facesContext = contextFactory.getFacesContext(servletContext, request, response, lifecycle);

    // Set using our inner class
    InnerFacesContext.setFacesContextAsCurrentInstance(facesContext);

    // set a new viewRoot, otherwise context.getViewRoot returns null
    UIViewRoot view = facesContext.getApplication().getViewHandler().createView(facesContext, "yourOwnID");
    facesContext.setViewRoot(view);

    return facesContext;
}   


/**
 * 
 * The method fetches the application bean for the given bean name.
 * The object returned should be cast correctly to the bean
 * 
 * @param beanName - the name of bean as described in JSF config
 * @return - The bean as object. It should be cast to correct bean object before using
 */     
private Object getApplicationBean(String beanName, FacesContext fContext) {     
    Object bean = fContext.getExternalContext().getApplicationMap().get(beanName);      
    return bean;
}   
}

Now when I run this I get the following problems in my server:

SEVERE: Critical error during deployment: 
 com.sun.faces.mgbean.ManagedBeanCreationException: Unable to set property loginBD for managed bean securityBean
at com.sun.faces.mgbean.ManagedBeanBuilder$BakedBeanProperty.set(ManagedBeanBuilder.java:615)
at com.sun.faces.mgbean.ManagedBeanBuilder.buildBean(ManagedBeanBuilder.java:133)
at com.sun.faces.mgbean.BeanBuilder.build(BeanBuilder.java:104)
at com.sun.faces.mgbean.BeanManager.createAndPush(BeanManager.java:409)
at com.sun.faces.mgbean.BeanManager.create(BeanManager.java:269)
at com.sun.faces.mgbean.BeanManager.create(BeanManager.java:256)
at com.sun.faces.config.ConfigureListener.contextInitialized(ConfigureListener.java:255)
at org.apache.catalina.core.StandardContext.contextListenerStart(StandardContext.java:4690)
at com.sun.enterprise.web.WebModule.contextListenerStart(WebModule.java:534)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:5305)
at com.sun.enterprise.web.WebModule.start(WebModule.java:500)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:917)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:901)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:755)
at com.sun.enterprise.web.WebContainer.loadWebModule(WebContainer.java:1980)
at com.sun.enterprise.web.WebContainer.loadWebModule(WebContainer.java:1630)
at com.sun.enterprise.web.WebApplication.start(WebApplication.java:100)
at org.glassfish.internal.data.EngineRef.start(EngineRef.java:130)
at org.glassfish.internal.data.ModuleInfo.start(ModuleInfo.java:269)
at org.glassfish.internal.data.ApplicationInfo.start(ApplicationInfo.java:286)
at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:461)
at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:240)
at org.glassfish.deployment.admin.DeployCommand.execute(DeployCommand.java:370)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$1.execute(CommandRunnerImpl.java:355)
at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:370)
at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:1067)
at com.sun.enterprise.v3.admin.CommandRunnerImpl.access$1200(CommandRunnerImpl.java:96)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1247)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1235)
at com.sun.enterprise.v3.admin.AdminAdapter.doCommand(AdminAdapter.java:465)
at com.sun.enterprise.v3.admin.AdminAdapter.service(AdminAdapter.java:222)
at com.sun.grizzly.tcp.http11.GrizzlyAdapter.service(GrizzlyAdapter.java:168)
at com.sun.enterprise.v3.server.HK2Dispatcher.dispath(HK2Dispatcher.java:117)
at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:234)
at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:822)
at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:719)
at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:1013)
at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:225)
at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:137)
at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:104)
at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:90)
at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:79)
at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:54)
at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:59)
at com.sun.grizzly.ContextTask.run(ContextTask.java:71)
at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:532)
at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:513)
at java.lang.Thread.run(Thread.java:619)
 Caused by: java.lang.IllegalStateException: No WebApplicationContext found: no ContextLoaderListener registered?
at org.springframework.web.jsf.FacesContextUtils.getRequiredWebApplicationContext(FacesContextUtils.java:83)
at org.springframework.web.jsf.el.SpringBeanFacesELResolver.getWebApplicationContext(SpringBeanFacesELResolver.java:91)
at org.springframework.web.jsf.el.SpringBeanFacesELResolver.getBeanFactory(SpringBeanFacesELResolver.java:79)
at org.springframework.beans.factory.access.el.SpringBeanELResolver.getValue(SpringBeanELResolver.java:50)
at com.sun.faces.el.DemuxCompositeELResolver._getValue(DemuxCompositeELResolver.java:176)
at com.sun.faces.el.DemuxCompositeELResolver.getValue(DemuxCompositeELResolver.java:203)
at com.sun.el.parser.AstIdentifier.getValue(AstIdentifier.java:99)
at com.sun.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:219)
at com.sun.faces.mgbean.BeanBuilder$Expression.evaluate(BeanBuilder.java:591)
at com.sun.faces.mgbean.ManagedBeanBuilder$BakedBeanProperty.set(ManagedBeanBuilder.java:606)
... 47 more

How can I get a handle to the loginBD property (defined in faces-config as property)? Is there a way to manually (through code) inject this propery? or any other way to get a handle to this?

1

1 Answers

2
votes

This is not the right way to get an application scoped managed bean in a servlet filter. You do not need to manually create a whole FacesContext for it. Application scoped managed beans are stored as servletcontext attributes with the managed bean name as key.

So, in your doFilter() method, just do:

ServletContext context = request.getServletContext();
SecurityBean securityBean = (SecurityBean) context.getAttribute("securityBean");
// ...

As to your concrete problem, you seem to need this bean before JSF ever get initialized for the first time. In that case, you really need to create it yourself when it's null.

if (securityBean == null) {
    securityBean = new SecurityBean();
    securityBean.setLoginBD(new LoginBD()); // Or get it from application scope?
    context.setAttribute("securityBean", securityBean);
}

// ...

Unrelated to the concrete problem, I put a lot of question marks around this design approach. But as the concrete functional requirement is unclear, I am not able to propose the right approach. At least, you seem to be reinventing whatever is already been provided by container managed authentication and/or HttpSession, session scoped managed beans, EJBs, etc. This is not right. This is unnecessarily overcomplicated.