【问题标题】:JSF Creating a managed bean (application scope) for Security ManagementJSF 为安全管理创建托管 bean(应用程序范围)
【发布时间】:2012-04-16 06:24:16
【问题描述】:

我在创建自定义安全过滤器时遇到问题。 我打算创建一个过滤器,它将与服务器检查在 URL 中传递的 sessionId - 用于身份验证和授权。 我打算在代码中使用现有的 JSF 框架(例如缓存会话等)

为此,我首先定义了一个应用程序范围的 bean。 faces-config.xml 看起来像:


securityBeancom.ecom.scareer.security.SecurityBean应用程序
登录BD#{loginBD}
托管豆>

然后定义一个名为SecurityBean的类为:

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;
}

}

我有一个过滤器:

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;
}   
}

现在当我运行它时,我的服务器出现以下问题:

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

如何获取 loginBD 属性的句柄(在 faces-config 中定义为属性)?有没有办法手动(通过代码)注入这个属性?或任何其他方式来解决这个问题?

【问题讨论】:

    标签: jsf javabeans code-injection managed


    【解决方案1】:

    这不是在 servlet 过滤器中获取应用程序范围的托管 bean 的正确方法。您不需要为它手动创建一个完整的FacesContext。应用程序范围的托管 bean 存储为 servletcontext 属性,托管 bean 名称作为键。

    所以,在您的 doFilter() 方法中,只需执行以下操作:

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

    至于你的具体问题,你似乎需要这个 bean before JSF 第一次被初始化。在这种情况下,您确实需要在null 时自己创建它。

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

    与具体问题无关,我对这种设计方法打了很多问号。但由于具体的功能要求不清楚,我无法提出正确的方法。至少,您似乎正在重新发明容器管理的身份验证和/或HttpSession、会话范围的托管bean、EJB等已经提供的任何东西。这是不对的。这不必要地过于复杂。

    【讨论】:

    • 您好 Balus,感谢您的快速回复。当用户登录时,我们创建 sessionid 并将它们与其他用户信息一起存储在缓存中(以及数据库中)。当用户现在导航到页面时,所有链接都将在 URL 中包含 sessionId,将检查是否过期以及授权。我打算使用现有的会话缓存(由 loginBD 访问),它是一个 Spring 组件。我的问题是,当我将此 bean 设置为应用程序范围和 eager=true 时,我无法获得 loginBD 的句柄。我已按照您的建议更正了代码。
    • 除上述之外。我添加了行securityBean.setLoginBD(new LoginBD());根据您的评论。但是 loginBD 本身使用 Autowiring 和 Spring 注解来获取 Dao 值。所以他们稍后会在 loginBD 中给出空指针。无论如何延迟应用程序范围bean的初始化直到加载Spring组件?我使用 Glassfish 服务器。
    猜你喜欢
    • 2012-03-27
    • 2013-10-19
    • 2015-08-22
    • 2011-06-28
    • 2012-10-11
    • 2011-11-16
    • 2012-11-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多