【问题标题】:Basic Security in JSFJSF 中的基本安全性
【发布时间】:2010-12-01 00:47:02
【问题描述】:

我希望看到一个简单的登录应用程序,但不像 this 那样简单。

我想要了解 JSF 的工作原理,我已经开发了很多 ASP.NET,您在其中有代码,您可以在其中检查是否在登录时创建了会话。

JSF 中类似的解决方案会很棒。

这基本上是我想要实现的:

  • 登录页面
  • 如果正常
    • 创建会话并返回“成功”
  • 如果失败
    • 返回“失败”

(“成功”和失败映射到faces-config.xml)

在成功页面上,我希望确定用户已登录,因此如果您没有获得正确的会话,则应该无法导航到“success.jspx”。

【问题讨论】:

标签: jsf login


【解决方案1】:

除了能够使用组件rendered 等面向基于角色的安全性的属性之外,核心JSF 中没有固有的身份验证功能。

默认情况下,JSF 应用程序依赖与包含它的 Web 组件相同的容器管理的安全机制 (JEE5 tutorial)。像Seam 这样的第三方框架可以提供替代方案。

如果您想添加自己的应用程序安全性,servlet filter 是更简单的机制之一。

此过滤器保护restricted 目录下的资源,如web.xml 中所定义:

  <filter>
    <filter-name>AuthenticationFilter</filter-name>
    <filter-class>restricted.AuthenticationFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>AuthenticationFilter</filter-name>
    <url-pattern>/restricted/*</url-pattern>
  </filter-mapping>

过滤器类实现:

public class AuthenticationFilter implements Filter {
  private FilterConfig config;

  public void doFilter(ServletRequest req, ServletResponse resp,
      FilterChain chain) throws IOException, ServletException {
    if (((HttpServletRequest) req).getSession().getAttribute(
        AuthenticationBean.AUTH_KEY) == null) {
      ((HttpServletResponse) resp).sendRedirect("../restricted_login.faces");
    } else {
      chain.doFilter(req, resp);
    }
  }

  public void init(FilterConfig config) throws ServletException {
    this.config = config;
  }

  public void destroy() {
    config = null;
  }
}

faces-config.xml 中定义的登录 bean:

public class AuthenticationBean {
  public static final String AUTH_KEY = "app.user.name";

  private String name;
  public String getName() { return name; }
  public void setName(String name) { this.name = name; }

  public boolean isLoggedIn() {
    return FacesContext.getCurrentInstance().getExternalContext()
        .getSessionMap().get(AUTH_KEY) != null;
  }

  public String login() {
    FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put(
        AUTH_KEY, name);
    return "secret";
  }

  public String logout() {
    FacesContext.getCurrentInstance().getExternalContext().getSessionMap()
        .remove(AUTH_KEY);
    return null;
  }
}

restricted_login.jsp页面中的JSF登录表单:

  <f:view>
    <p><a href="restricted/secret.faces">try to go to secret
    page</a></p>
    <h:form>
    Username:
    <h:panelGroup rendered="#{not authenticationBean.loggedIn}">
        <h:inputText value="#{authenticationBean.name}" />
        <h:commandButton value="login"
          action="#{authenticationBean.login}" />
      </h:panelGroup>
      <h:commandButton value="logout"
        action="#{authenticationBean.logout}"
        rendered="#{authenticationBean.loggedIn}" />
    </h:form>
  </f:view>

(选择重定向 URL/机制是为了简洁,而不是任何形式的最佳实践;有关更多选项,请参阅 Servlet API。)

【讨论】:

  • 您能否澄清这与容器管理的安全性有何不同?以及在考虑了每种方法的优点/缺点之后,如何选择一个而不是另一个?
  • @Marcos - 这真的应该是它自己的问题,可能引用这个。
  • 其实我已经在这里发布过类似的东西:stackoverflow.com/questions/7872265/…
【解决方案2】:

如果您愿意尝试更高级的方法,那么我建议您研究一下 spring-security+JSF。它就像一个魅力。

您可以编写应用程序,就好像它没有受到安全保护一样,然后只需配置应该使用方面保护的区域。

春季安全:http://static.springsource.org/spring-security/site/

教程:http://ocpsoft.com/java/acegi-spring-security-jsf-login-page/

【讨论】:

    【解决方案3】:

    最好的方法是使用容器管理的安全性。

    Here is a tutorial 了解如何使用glassfishjsf 实现这一目标。

    【讨论】:

      【解决方案4】:

      如果您使用模板,我发现您并不需要过滤器。

      index.jsp

      <jsp:forward page="startup.faces"></jsp:forward>
      

      startup.xhtml (.faces),实际上并没有尝试显示屏幕,它在加载时调用 javascript startupSubmit() 并单击按钮。这会将流程直接发送到 StartupBean.java 中的 start() 方法。

      <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0     Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      .
      .
       <script type="text/javascript">
       function startupSubmit() {
        **document.getElementById('startupForm:startupBtn').click();**
       }
       </script>
       <h:body onload="startupSubmit()">
       <h:form id="startupForm">
       <p:commandButton id="startupBtn" value="" action="#{startupBean.start}" ajax="false" />  
       </h:form>
       </h:body>
      </html>
      

      StartupBean.java(不是下面的 template.xhtml 的一部分)。 StartupBean 中的 start() 方法将名为 authorized 的变量设置为 true(默认为 false),然后跳转到 first.xhtml。您可以使用任何您希望确定是否已将授权设置为真的标准...例如登录标准。

      package gov.irs.eservices.managementBeans;
      
      import javax.faces.bean.ManagedBean;
      import javax.faces.bean.SessionScoped;
      
      @ManagedBean(name="startupBean")
      @SessionScoped
      public class StartupBean {
      
      private boolean authorized;
      
      public StartupBean() {
      }
      
      public String start() {
          **setAuthorized(true);**
          return "first";
      }
      
      public boolean isAuthorized() {
          return authorized;
      }
      
      public void setAuthorized(boolean authorized) {
          this.authorized = authorized;
      }
      }
      

      模板.xhtml。在 template.xhtml 中,就在表单内部,放置一个 h: 或 p: panelGrid 并且仅在 startupBean.authorized 为 true 时才呈现它。用户访问模板中包含的页面的唯一方法是首先通过 StartupBean.java。

      <f:view>
      <div id="container">
      <h:form id="templateForm">
      **<p:panelGrid rendered="#{startupBean.authorized}">**
          <div id="header">
              <ui:include src="header.xhtml" />
          </div>
      
          <div id="wrapper">
              <div id="firstId">
                  <ui:insert name="first"></ui:insert>
              </div>
      .
      .  <!-- MORE PAGES -->
      .
      .
          </div>
      
          <div id="footer">
              <ui:include src="footer.xhtml" />
          </div>
      </p:panelGrid>
      </h:form>
      </div>      
      </f:view>
      

      所以,这就是我的解决方案。我已经对其进行了相当彻底的测试,它似乎工作正常。

      【讨论】:

      • @ManagedBean StartupBean 中的 name 属性是不必要的,因为它会默认获取此名称。
      猜你喜欢
      • 1970-01-01
      • 2017-11-24
      • 2014-01-20
      • 1970-01-01
      • 2014-10-29
      • 2012-06-12
      • 2015-04-10
      • 2018-08-05
      • 2018-01-04
      相关资源
      最近更新 更多