在JBOSS中实现用户安全认证

为什么要使用JBOSS为我们提供的安全体系?

常规的做法是,我们在servlet中取得用户登录信息(用户名和密码),去数据库验证通过后将些信息保存在session中,以便在此用户的整个会话期间能进行相应的权限控制。J2EE架构体系为我们提供了安全、事务和线程等诸多的便利,让我们能专注于业务逻辑的实现。为什么我们在开发J2EE应用时还要采用传统的安全体系呢?
JBossSX框架为我们提供了灵活的安全架构,基本上可以满足我们绝大部分的安全需要(请参考本站《关于用户权限的联想》一文)。

用JBossSX的实现用户认证

我们知道,servlet提供了四种安全认证方式:HTTP Basic Authentication, HTTP Digest Authentication, Form Based Authentication和HTTPS Client Authentication。其中又以表单认证(我们的传统认证方式都是基于表单的)和基本认证应用地最为广泛。下面我就以实例来说明如何在JBoss中实现这两种用户认证。

涉及到用户认证,就先要确定用户的信息如何保存?JBoss为我们提供了多种灵活的LoginModules,比如JaasServerLoginModule,它是在服务器端用两个文本格式的属性文件来记录用户信息的;LdapServerLoginModule用LDAP来保存用户信息。等等。

你肯定要说这样岂不是太不方便了!我如何将用户登录信息与应用中的其它部分联系起来,必竟我们的应用大多基于数据库系统的?不错,上面的方法有其局限性,我们不打算采用。下面我主要谈论的是另一种方式:DatabaseServerLoginModule,顾名思义,这是将用户登录信息保存在数据库中的一种认证模式。

我们要做的步骤如下:

第一步:修改jboss/conf/auth.conf文件,在文件后加上:


example2 {
org.jboss.security.auth.spi.DatabaseServerLoginModule required
dsJndiName="java:/mySQLDS"
principalsQuery=
"select PASSWORD from sads_userpri where USERID=?"
rolesQuery=
"select ROLEID,ROLEGROUP from sads_userpri where USERID=?"
;
};

这里的java:/mySQLDS是我在jboss.jcml文件中配置好的数据源(如何配置就不罗嗦了)。后面两句比较重要,principalsQuery是用来查询用户密码的SQL语句,rolesQuery用来查询用户角色和角色组,我把这些信息都存在我的sads_userpri表中,这里你可以修改以适应你现在的应用。

要注意的一点是RoleGroup这个字段必须有,如果你不打算设置角色组,就必需将其值设为字串"Roles"。
各个相关关字段的定义均为varchar(64)即可。

第二步:写一个jboss-web.xml文件,与web.xml文件放在一起,即WEB-INF/下。


<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
<security-domain>java:/jaas/example2</security-domain>
<ejb-ref>
<ejb-ref-name>ejb/HelloEJB</ejb-ref-name>
<jndi-name>Hello</jndi-name>
</ejb-ref>
</jboss-web>

注意<security-domain> 标签,就是指向我们在auth.conf文件中加上的example2认证方式。下面是EJB的引用标签,把ejb/HelloEJB这个引用名指向Hello这个JNDI名(它在jboss.xml中定义)。

再修改web.xml文件,加上:


<security-constraint>
<display-name>test</display-name>
<web-resource-collection>
<web-resource-name>Collection1</web-resource-name>
<url-pattern>/*</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
<http-method>PUT</http-method>
<http-method>DELETE</http-method>
<http-method>HEAD</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>Echo</role-name>
</auth-constraint>
<user-data-constraint>
<transport-guarantee>NONE</transport-guarantee>
</user-data-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>sharetop.com</realm-name>
</login-config>
<security-role>
<role-name>Echo</role-name>
</security-role>
<ejb-ref>
<ejb-ref-name>ejb/HelloEJB</ejb-ref-name>
<ejb-ref-type>Session</ejb-ref-type>
<home>securityhello.HelloHome</home>
<remote>securityhello.Hello</remote>
</ejb-ref>
</web-app>

好,改了这么多还只是让我们访问servlet时会弹出登录窗口,但是如何控制用户权限,我们还没涉及,这方面的知识我就不多说了,任何一本EJB开发的书上都会有涉及。我给出我的例子代码中的ejb-jar.xml文件:


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 1.1//EN" "http://java.sun.com/j2ee/dtds/ejb-jar_1_1.dtd">
<ejb-jar>
<enterprise-beans>
<session>
<ejb-name>Hello</ejb-name>
<home>securityhello.HelloHome</home>
<remote>securityhello.Hello</remote>
<ejb-class>securityhello.HelloBean</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
<security-role-ref>
<description />
<role-name>Echo</role-name>
<role-link>Echo</role-link>
</security-role-ref>
</session>
</enterprise-beans>
<assembly-descriptor>
<security-role>
<description />
<role-name>Echo</role-name>
</security-role>
<method-permission>
<role-name>Echo</role-name>
<method>
<description />
<ejb-name>Hello</ejb-name>
<method-name>*</method-name>
</method>
</method-permission>
<container-transaction>
<method>
<ejb-name>Hello</ejb-name>
<method-name>*</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
</assembly-descriptor>
</ejb-jar>

到这一步,全部的配置都做完了,你可能会有一个疑问:如何在程序中访问到登录的用户名呢?两个方面,在EJB中可以通过Context的getCallerPrincipal()方法取得,在servlet中的request.getUserPrincipal()也可以取得当前用户的Principal对象,关于Principal类,可以参考一下API中的说明。

上面给出的web.xml文件你可能已经看出是基于BASIC认证方式的,如何使用Form认证呢?同样,你可以这样修改:


<login-config>
<auth-method>FORM</auth-method>
<form-login-config>
<form-login-page>/Jsp1.jsp</form-login-page>
<form-error-page>/Jsp2.jsp</form-error-page>
</form-login-config>
</login-config>

在Jsp1.jsp文件中我们加入一个表单,注意action和各个元素的命名:

<form method="POST" action="j_security_check">
<input type="text" name="j_username">
<input type="password" name="j_password">
<input type="submit" name="login">
</form>

仅此而已,是不是很方便?大家可以用上述方法写一个最简单的HelloWorld来试一下。

这是转贴