Glassfish 3安全性 - 使用JDBC Realm进行基于表单的身份验证

时间:2011-10-30 12:43:47

标签: java security jsf java-ee glassfish

4 个答案:

答案 0 :(得分:18)

您的配置中缺少一些内容:

  • 密码以明文形式存储在数据库中。这很可能是不正确的。 Glassfish 3.1默认使用SHA-256算法,因此JDBC Realm将无法对用户进行身份验证,因为数据库中的存储值与Realm创建的摘要不匹配。您需要在领域配置中指定显式摘要算法,或者依赖于默认值。此外,您需要确保应用程序在创建新用户或修改其密码时创建摘要。如果要以明文形式存储密码,则必须为摘要算法指定“无”值。
  • 仅指定摘要算法是不够的。您需要指定存储摘要的编码(因为,摘要只是一个字节序列,并且可能不会存储为纯ASCII序列)。 Glassfish支持Hex和Base64编码,默认使用Hex编码。在存储密码摘要之前,在领域中配置Your app should therefore apply the same encoding。请注意,当您指定摘要算法“none”以明文形式存储密码时,您无需对存储的密码进行编码(同样,您也无需指定编码);至少这是我从阅读Glassfish资料中观察到的。
  • 此外,用户组映射目前似乎是1:1。 You might want to use a separate join table允许组和用户之间的1:N映射。
  • 您还需要确保启用“默认主体到角色映射”选项。如果没有此选项,则需要手动map the roles in web.xml to users and groups in your realm

关于使用form而不是h:form的主题,基本原因是JSF运行时不允许您指定action标记的h:form属性。在对响应进行编码时,此值由JSF运行时设置,因此,当您使用j_security_check标记时,您将无法指定h:form的值。 The documentation states this quite explicitly

  

“action”属性的值必须是传递的结果   查看当前视图的标识符到getActionURL()方法   此应用程序的ViewHandler,然后将该String传递给   encodeActionURL()上的ExternalContext方法。

<强>更新

根据发布的堆栈跟踪,我可以推断出Realm使用的JNDI数据源(在JNDI字段中指定)在Glassfish域中不可用。 JDBC Realm的先决条件之一是在Glassfish域配置中注册JNDI数据源,其连接池用于连接到底层数据库。

以下是我的Glassfish域配置文件(domain.xml)的片段,其中JNDI数据源(jdbc / galleriaDS)使用连接池(GalleriaPool),最终由JDBC Realm(GalleriaRealm)使用:

<domain log-root="${com.sun.aas.instanceRoot}/logs" application-root="${com.sun.aas.instanceRoot}/applications" version="12">
  <resources>
    ...
    <jdbc-connection-pool validation-table-name="SYSIBM.SYSDUMMY1" driver-classname="" datasource-classname="org.apache.derby.jdbc.ClientDataSource40" res-type="javax.sql.DataSource" description="" name="GalleriaPool" is-connection-validation-required="true" fail-all-connections="true" ping="true">
      <property name="User" value="APP"></property>
      <property name="DatabaseName" value="GALLERIA"></property>
      <property name="RetrieveMessageText" value="true"></property>
      <property name="CreateDatabase" value="true"></property>
      <property name="Password" value="APP"></property>
      <property name="ServerName" value="localhost"></property>
      <property name="Ssl" value="off"></property>
      <property name="SecurityMechanism" value="4"></property>
      <property name="TraceFileAppend" value="false"></property>
      <property name="TraceLevel" value="-1"></property>
      <property name="PortNumber" value="1527"></property>
      <property name="LoginTimeout" value="0"></property>
    </jdbc-connection-pool>
    <jdbc-resource pool-name="GalleriaPool" description="" jndi-name="jdbc/galleriaDS"></jdbc-resource>
  </resources>
  <servers>
    <server name="server" config-ref="server-config">
    ...
      <resource-ref ref="jdbc/galleriaDS"></resource-ref>
    </server>
  </servers>
  ...
  <configs>
    <config name="server-config">
    ...
      <security-service>
        <auth-realm name="GalleriaRealm" classname="com.sun.enterprise.security.auth.realm.jdbc.JDBCRealm">
          <property name="jaas-context" value="jdbcRealm"></property>
          <property name="encoding" value="Hex"></property>
          <property name="password-column" value="PASSWORD"></property>
          <property name="datasource-jndi" value="jdbc/galleriaDS"></property>
          <property name="group-table" value="USERS_GROUPS"></property>
          <property name="charset" value="UTF-8"></property>
          <property name="user-table" value="USERS"></property>
          <property name="group-name-column" value="GROUPID"></property>
          <property name="digest-algorithm" value="SHA-512"></property>
          <property name="user-name-column" value="USERID"></property>
        </auth-realm>
        ...
      </security-service>
    </config>
    ...
  </configs>
  ...
</domain>

更新#2 - 获取JDBC Realm针对Derby执行的SQL语句

看起来SQL查询的结构与您准备的数据库模型不匹配。您可以通过derby.language.logStatementText系统属性查看JDBC领域针对Derby实例执行的SQL语句。可以将此属性设置为true作为derby.properties文件中的静态值,它将在重新启动Derby实例时生效。 derby.properties文件需要包含以下条目:

derby.language.logStatementText=true

并且此文件必须放在Derby实例的当前工作目录中。当前工作目录通常是包含Derby数据库的目录,可以在Derby启动期间使用derby.system.home JVM参数显式指定:

-Dderby.system.home=C:\derby

Derby执行的所有SQL语句现在都将记录在derby.log文件中。

根据提供的信息,我的印象是有一个名为GROUPS的单独表格,用于存储组信息,这与联接表格不同 - USER_GROUP 。在这种情况下,您的领域必须配置为将组表格设为USER_GROUP而不是GROUP;您可以通过查看JDBC领域发出的SQL查询来确认这一点。

为了阐明上述观点,JDBC领域配置中的Group字段不用于指定存储组信息的表。相反,它用于指定存储组 - 用户映射的表。在1:1映射中,Group表可以存储此信息,但在1:M或通常在M:M场景中,您将拥有一个包含映射的单独表。 JDBC领域发出的SQL查询使用映射表而不是实际的组表(如果它们不同)来确定用户所属的组。

答案 1 :(得分:3)

除了Vineet的回答之外,我还注意到在GlassFish的Security选项卡中未选中选项安全管理器,应该启用该选项以便在您的Realm中使用Security。

如果您使用JDBC JNDI名称,则不需要为JDBC领域提供用户名/密码

答案 2 :(得分:2)

我已经在Sailfin(基于Glassfish 2)上做过这个。

首先,我没有看到任何sun-web.xml文件将web.xml文件中定义的角色映射到定义数据库的组。

其次,根据本教程:http://codepimpsdotorg.blogspot.com/2007/12/glassfish-jdbc-realm-authentication.html,您必须指定一个摘要算法(“无”不是一个选项,不确定它是否在Glassfish 3中保持不变)。

第三,表和列需要按特定顺序排列,我们使用以下结构:

@Entity
@Table(name = "AuthenticationUser")
public class UserEntity implements Serializable, Identifiable
{   
    private static final long serialVersionUID = -1213555368237839900L;

    @Id
    @Column(name = "name", length = 20)
    private String itsName;

    @Column(name = "password", length = 1024)
    private String itsPassword;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "groupName", referencedColumnName = "name")
    private GroupEntity itsGroupEntity;
...

@Entity
@Table(name = "AuthenticationGroup")
public class GroupEntity implements Serializable, Identifiable
{
    private static final long serialVersionUID = -1213554368237839900L;

    private static final String USER_GROUP_COLUMN = "itsGroupEntity";

    @Id
    @Column(name = "name", length = 20)
    private String itsName;

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY,
            mappedBy=USER_GROUP_COLUMN)
    private List<UserEntity> itsUsers;
...

定义领域:

user-table=AuthenticationUser
user-name-column=name
password-column=password
group-table=AuthenticationUser
group-name-column=groupName

重要提示:用户名和组名属于同一个表!因此表AuthenticationGroup仅供我们内部使用,但Glassfish不使用。

答案 3 :(得分:2)

我遇到了同样的问题。

我通过将密码重命名为(User_password)和表中的userName(User_name)字段(除了用户名和密码之外的其他任何内容)来解决此问题,不知何故使用“userName”和“password”会导致执行时发生冲突使用Realms进行身份验证。

还要使用摘要算法:= none,以防您将密码存储为纯文本。

在“安全”菜单中, 启用默认主体到角色映射。

希望这有帮助,