2009年3月10日星期二

WLS_019:使用数据库表作为Authentication Provider (翻译+整理)

本文最后一次修改时间:2012-02-08
运行环境:WebLogic Server 12c + Oracle Database XE 10gR2

发现有很多人问如何为WebLogic Server配置基于数据库的认证方式,大概是还是因为数据库比LDAP好操作,好控制。
本来以为这个问题很简单,但查找了很多资料,居然没有详细的说明,包括WebLogic的官方文档也只是说可以配置,但没有具体说明如何配。
直到我找到了参考文献1的这篇文章。
以下内容基本来自这篇文章的翻译和整理。

重点步骤说明:
1. 创建JHS Schema
当然,你可以修改成其它名称的Schema。


2. 在JHS Schema下创建Table和数据
执行如下SQL语句:
CREATE TABLE JHS_ROLES
(
ID NUMBER(*, 0) NOT NULL,
ORG_KEY VARCHAR2(30) DEFAULT 'DEFAULT' NOT NULL,
SHORT_NAME VARCHAR2(10) NOT NULL,
NAME VARCHAR2(40) NOT NULL
);

CREATE TABLE JHS_USER_ROLE_GRANTS
(
ID NUMBER(*, 0) NOT NULL,
USR_ID NUMBER(*, 0) NOT NULL,
RLE_ID NUMBER(*, 0) NOT NULL
);

CREATE TABLE JHS_USERS
(
ID NUMBER(*, 0) NOT NULL,
EMAIL_ADDRESS VARCHAR2(240),
USERNAME VARCHAR2(240) NOT NULL,
ORG_KEY VARCHAR2(30) DEFAULT 'DEFAULT',
PASSWORD VARCHAR2(240),
DISPLAY_NAME VARCHAR2(240),
LOCALE VARCHAR2(10)
);

ALTER TABLE JHS_ROLES
ADD CONSTRAINT JHS_RLE_PK PRIMARY KEY
( ID ) ENABLE;

ALTER TABLE JHS_ROLES
ADD CONSTRAINT JHS_RLE_UK1 UNIQUE
( SHORT_NAME,ORG_KEY ) ENABLE;

ALTER TABLE JHS_USER_ROLE_GRANTS
ADD CONSTRAINT JHS_URG_PK PRIMARY KEY
( ID ) ENABLE;

ALTER TABLE JHS_USER_ROLE_GRANTS
ADD CONSTRAINT JHS_URG_UK1 UNIQUE
( RLE_ID, USR_ID ) ENABLE;

ALTER TABLE JHS_USERS
ADD CONSTRAINT JHS_USR_PK PRIMARY KEY
( ID ) ENABLE;

CREATE SEQUENCE JHS_SEQ INCREMENT BY 1 MAXVALUE 999999999999999999999999999 MINVALUE 1 CACHE 20 ;

-- Create two users SKING and AHUNOLD
insert into jhs_users (ID, EMAIL_ADDRESS, USERNAME, ORG_KEY, PASSWORD, DISPLAY_NAME)
select jhs_seq.nextval,'SKING','SKING','DEFAULT','SKING', 'Steven King'
from dual
where not exists (select '1' from jhs_users where username='SKING');

insert into jhs_users (ID, EMAIL_ADDRESS, USERNAME, ORG_KEY, PASSWORD, DISPLAY_NAME)
select jhs_seq.nextval,'AHUNOLD','AHUNOLD','DEFAULT','AHUNOLD', 'Alexander Hunold'
from dual
where not exists (select '1' from jhs_users where username='AHUNOLD');

-- set up two roles: Administrator and User
insert into jhs_roles(id, SHORT_NAME, name)
select jhs_seq.nextval, 'ADMIN','Administrator'
from dual
where not exists (select '1' from jhs_roles where short_name='ADMIN');

insert into jhs_roles(id, SHORT_NAME, name)
select jhs_seq.nextval, 'USER','User'
from dual
where not exists (select '1' from jhs_roles where short_name='USER');

-- Make Steven King Administrator
insert into jhs_user_role_grants (id,rle_id,usr_id)
select jhs_seq.nextval, rle.id, usr.id
from jhs_roles rle, jhs_users usr
where rle.short_name='ADMIN'
and usr.username='SKING'
and not exists (select '1' from jhs_user_role_grants urg2
          where urg2.usr_id = usr.id
          and   urg2.rle_id = rle.id);

-- Make Alexander Hunold User
insert into jhs_user_role_grants (id,rle_id,usr_id)
select jhs_seq.nextval, rle.id, usr.id
from jhs_roles rle, jhs_users usr
where rle.short_name='USER'
and usr.username='AHUNOLD'
and not exists (select '1' from jhs_user_role_grants urg2
          where urg2.usr_id = usr.id
          and   urg2.rle_id = rle.id);

commit;


3. 创建jhsDS DataSource






4. 创建Database Authenticator Provider
切换到Provider->Authentication Tab:

新增一个Authenticator,注意,Type要选择 SQLAuthenticator。

设置Control Flag为SUFFICIENT。

注意,DataSource Name指向的是JHS Data Source,而不是JNDI Name:jhsDS;Password使用Plain风格,最后别忘了点击Save按钮。

接着,还要指明如何通过SQL语句获取User、Group、Role。
为了简单,这里直接修改[domain_name]\config\config.xml文件,当然,也可以通过WLS Console修改。
修改前的DatabaseAuthenticator配置如下:
  <sec:authentication-provider xsi:type="wls:sql-authenticatorType">
        <sec:name>DatabaseAuthenticator</sec:name>
        <sec:control-flag>SUFFICIENT</sec:control-flag>
        <wls:data-source-name>jhsDS</wls:data-source-name>
        <wls:plaintext-passwords-enabled>true</wls:plaintext-passwords-enabled>
        <wls:password-style>PLAINTEXT</wls:password-style>
</sec:authentication-provider>

修改后的DatabaseAuthenticator配置如下:
  <sec:authentication-provider xsi:type="wls:sql-authenticatorType">
        <sec:name>DatabaseAuthenticator</sec:name>
        <sec:control-flag>SUFFICIENT</sec:control-flag>
        <wls:data-source-name>JHS Data Source</wls:data-source-name>
        <wls:plaintext-passwords-enabled>true</wls:plaintext-passwords-enabled>
        <wls:sql-get-users-password>SELECT password FROM jhs_users WHERE username = ?</wls:sql-get-users-password>
        <wls:sql-user-exists>SELECT username FROM jhs_users WHERE username = ?</wls:sql-user-exists>
        <wls:sql-list-member-groups>SELECT short_name FROM jhs_user_role_grants g ,jhs_roles r,jhs_users u WHERE g.usr_id = u.id and g.rle_id = r.id and u.username = ?</wls:sql-list-member-groups>
        <wls:sql-list-users>SELECT username FROM jhs_users WHERE username LIKE ?</wls:sql-list-users>
        <wls:sql-get-user-description>SELECT display_name FROM jhs_users WHERE username = ?</wls:sql-get-user-description>
        <wls:sql-list-groups>SELECT short_name FROM jhs_roles WHERE short_name LIKE ?</wls:sql-list-groups>
        <wls:sql-group-exists>SELECT short_name FROM jhs_roles WHERE short_name = ?</wls:sql-group-exists>
        <wls:sql-is-member>SELECT u.username FROM jhs_user_role_grants g ,jhs_users u WHERE u.id = g.usr_id and rle_id = ( select id from jhs_roles where short_name = ? ) AND usr_id = ( select id from jhs_users where username = ? )</wls:sql-is-member>
        <wls:sql-get-group-description>SELECT name FROM jhs_roles WHERE short_name = ?</wls:sql-get-group-description>
        <wls:password-style>PLAINTEXT</wls:password-style>
        <wls:sql-create-user>INSERT INTO jhs_users ( id,username , password , display_name) VALUES (jhs_seq.nextval, ? , ? , ? )</wls:sql-create-user>
        <wls:sql-remove-user>DELETE FROM jhs_users WHERE username = ?</wls:sql-remove-user>
        <wls:sql-remove-group-memberships>DELETE FROM jhs_user_role_grants WHERE rle_id = ( select id from jhs_roles where short_name = ? ) or usr_id = ( select id from jhs_users where username = ? )</wls:sql-remove-group-memberships>
        <wls:sql-set-user-description>UPDATE jhs_users SET display_name  = ? WHERE username = ?</wls:sql-set-user-description>
        <wls:sql-set-user-password>UPDATE jhs_users SET password = ? WHERE username = ?</wls:sql-set-user-password>
        <wls:sql-create-group>insert into jhs_roles(id, short_name, name) values (jhs_seq.nextval, ?, ?)</wls:sql-create-group>
        <wls:sql-set-group-description>UPDATE jhs_roles SET name = ? WHERE short_name =  ?</wls:sql-set-group-description>
        <wls:sql-add-member-to-group>INSERT INTO jhs_user_role_grants (id,rle_id,usr_id) VALUES( jhs_seq.nextval , ( select id from jhs_roles where short_name = ?),(select id from jhs_users where username = ?))</wls:sql-add-member-to-group>
        <wls:sql-remove-member-from-group>DELETE FROM jhs_user_role_grants WHERE rle_id = ( select id from jhs_roles where short_name = ? ) AND usr_id = ( select id from jhs_users where username = ? )</wls:sql-remove-member-from-group>
        <wls:sql-remove-group>DELETE FROM jhs_roles WHERE short_name = ?</wls:sql-remove-group>
        <wls:sql-remove-group-member>DELETE FROM jhs_user_role_grants WHERE rle_id = ( select id from jhs_roles where short_name = ? )</wls:sql-remove-group-member>
        <wls:sql-list-group-members>SELECT username FROM jhs_user_role_grants g ,jhs_roles r,jhs_users u WHERE g.usr_id = u.id and g.rle_id = r.id and r.short_name = ? and u.username like ?</wls:sql-list-group-members>
      </sec:authentication-provider>


5. 重启WebLogic,应该能看到Database中的用户和组。
用户:AHUNOLD和SKING

组:ADMIN和USER

把新增的Database Authentication provider Reorder到最上面。
因为默认的Default Authentication provider 的Control Flag 是Required,因此会首先从这里验证用户,验证不通过则宣告认证失败。
为了让Database Authenticator能够首先验证用户,需要把Database Authentication provider放到最上面。


6. 测试
在WLS Console中,可以直接为Database Authentication provider 创建用户和组。
创建的用户和组将会写到JHS Schema指定的数据库表中。
(1)创建用户GuoLiJie。

(2)创建组managers。

(3)把GuoLiJie放到组managers中。
发布timeoff.war,访问http://localhost:7001/timeoff/managers/officeclosing.html。
输入用户GuoLiJie/welcome1,应该能够验证通过看到下个页面。

7. 加密用户口令
以上的设置用户的口令是以明文保存在数据库表JHS_USERS中的,这当然不够安全。

在Database Authentication provider中的Specific配置Tab中,有如下属性可以设置:
(1)Plaintext Passwords Enabled:true/false。
true:WLS将从数据库表中读取passwords,并直接和用户输入的密码进行比较,即认为数据库表中保存的是明文。
这是我们现在使用的方式。
false:WLS认为数据库表中保存的是密文,将会把用户输入的密码加密后在进行比较。
我们将在后面测试该选项。
(2)Password Style Retained:true/false。
当WLS写入口令到数据库表时,会根据口令的加密算法生成密文,同时在前面加上一个算法前缀:如{SHA-1}W6PH5MM5PZ8GGIULBPGZG37MJ9G= 。
true:当用户设置(新增或修改)口令时,继续使用原密文使用的加密算法(根据前缀来判断使用的是哪种加密算法)。
false:当用户设置(新增或修改)口令时,使用Password Algorithm设置的算法重新计算密文。
(3)Password Algorithm:默认SHA-1。还可以是MD2,MD5。
更多值请参考http://java.sun.com/javase/6/docs/technotes/guides/security/StandardNames.html#algspec。
(4)Password Style:可以为PLAINTEXT,HASHED,SALTEDHASHED。
PLAINTEXT:明文。
HASHED:密文,但同一密码生成的密文相同,容易被破解。
SALTEDHASHED:加盐Hash,即使是同一密码生成的密文也不相同,不容易被破解。

8. 测试加密后的用户口令
(1)把数据库中的口令以密文方式保存。
为此设置如下:
Plaintext Passwords Enabled=false
Password Style Retained=false
Password Algorithm:SHA-1
Password Style:HASHED

增加用户Test、Test2,并修改GuoLiJie的口令,这里口令都设置为welcome1。
注意,口令都被加密了,但是同样的口令加密后的密文都是一样的:{SHA-1}41vs5sXm4OhspR0EQOkigqnWrIo=。

(2)设置Password Style:SALTEDHASHED,其余的设置与(1)相同。
修改用户Test、Test2、GuoLiJie的口令,所有口令还是设置为welcome1。
再次查看数据库表中口令值,发现虽然口令都是一样的,但是加密后的密文完全不一样。

重新打开一个浏览器,访问http://localhost:7001/timeoff/managers/officeclosing.html。
输入用户GuoLiJie/welcome1,应该能够验证通过看到下个页面,这说明口令加密后工作正常。

可以看出,不论选择哪种加密算法,WLS都会帮你做认证,无需开发人员做额外的加解密工作,这个真是方便啊。

参考文献:
1. http://biemond.blogspot.com/2008/12/using-database-tables-as-authentication.html
2. http://chrismuir.sys-con.com/node/1130973/mobile
3. http://java.sun.com/javase/6/docs/technotes/guides/security/StandardNames.html#algspec

没有评论: