新的项目没有开始于是抽空做一个通用一些的
Security,
后来又考虑到
CAS
和
SSL
的认证问题俺还没有弄懂,就选择直接使用
Spring
的子项目
acegi
acegi
是基于
Spring
的的一个安全框架,支持
HTTP
基本(
basic
)验证、
HTTP Request Session
验证、安全通道、
ACL
等等,功能强大。配置比较简单,但是还是要写一下:
1.
下载
Spring Acegi
的
jar
文件和它的源代码,在它的
binary
包中有一个
contacts.war
,这个是
acegi
的示例,把它放在
tomcat
的
webapps
下直接运行即可,这个是
acegi
很好的参考。
2.
将
acegi
的
Http Servlet Filter
配置在
web.xml
中。
<!-- Acegi Security-->
<
filter
>
<
filter-name
>
Acegi Filter Chain Proxy
</
filter-name
>
<
filter-class
>
org.acegisecurity.util.FilterToBeanProxy
</
filter-class
>
<
init-param
>
<
param-name
>
targetClass
</
param-name
>
<
param-value
>
org.acegisecurity.util.FilterChainProxy
</
param-value
>
</
init-param
>
</
filter
>
<
filter-mapping
>
<
filter-name
>
Acegi Filter Chain Proxy
</
filter-name
>
<
url-pattern
>
/*
</
url-pattern
>
</
filter-mapping
>
3.
将
contract
示例下的
applicationContext-acegi-security.xml
放在
ClassPath
下或
WEB-INF
下,并且在
web.xml
中指出该文件的位置:
<
context-param
>
<
param-name
>
contextConfigLocation
</
param-name
>
<
param-value
>
classpath*:/net/chinasam/common/applicationContext-*.xml
/WEB-INF/ applicationContext-acegi-security.xml
</
param-value
>
</
context-param
>
4.
在
applicationContext-acegi-security.xml
中找到下面的
bean
定义
<
bean
id
=
"jdbcDaoImpl"
class
=
"org.acegisecurity.userdetails.jdbc.JdbcDaoImpl"
>
<
property
name
=
"dataSource"
>
<
ref
bean
=
"dataSource"
/>
</
property
>
</
bean
>
可以看到
dataSource
属性必须引用一个
DataSource
,
修改这个
bean
,
是之使用一个
Spring
管理的
DataSource
实例。
JdbcDaoImpl
是查询数据库的实现类,这个类使用下面的
SQL
进行查询:
"SELECT username,password,enabled FROM users WHERE username = ?";
"SELECT username,authority FROM authorities WHERE username = ?";
前者查询用户,后者查询角色,你可以根据实际项目的情况进行修改:设置
JdbcDaoImpl
的
authoritiesByUsernameQuery
和
usersByUsernameQuery
属性。
这个我没有设置过,因为我的数据库结构和这个一样。
Acegi
没有包含用户管理,关于
User
的
CRUD
你必须自己完成,然后通过这两个属性告诉
acegi
如何查询用户和角色。
5.
密码编码,经常需要给密码进行编码,常用的算法包括
MD5
,
SHA
等,
applectionContext –acegi-Security.xml
中的配置为:
<
bean
id
=
"passwordEncoder"
class
=
"org.acegisecurity.providers.encoding.Md5PasswordEncoder"
/>
注意,如果使用上述配置,数据库中的
password
字段内容必须是实际内容的
MD5
摘要。
6.
ApplicationContext-acegi-securtiy.xml
缺省的使用
HTTP Request
验证,也就是通过普通的
HTML
标单提交用户名和口令,所以你必须编写自己的
login
页面,以下是一个例子,注意黑体字部分:
<
form
name
=
"login_form"
action
=
"
<
c:url
value
=
'j_acegi_security_check'
/>
"
method
=
"POST"
>
<
table
width
=
"241"
border
=
"0"
cellpadding
=
"0"
cellspacing
=
"0"
align
=
"center"
>
<
tr
>
<
td
><
img
src
=
"
<
fmt:message
key
=
"login.title.img"
/>
"
width
=
"241"
height
=
"26"
></
td
>
</
tr
>
<
tr
>
<
td
>
<
table
width
=
"241"
align
=
"center"
cellpadding
=
"0"
cellspacing
=
"0"
>
<
tr
>
<
td
width
=
"1"
bgcolor
=
"#D2DBE8"
></
td
>
<
td
align
=
"left"
>
<
fmt:message
key
=
"username"
/></
td
>
<
td
align
=
"left"
>
<
input
type
=
"text"
class
=
"text"
name
=
"j_username"
>
</
td
>
<
td
rowspan
=
"4"
>
<
img
src
=
"
<
fmt:message
key
=
"login.button.img"
/>
"
onclick
=
"javascript:login_form.submit()"
style
=
"cursor:hand"
>
</
td
>
<
td
width
=
"1"
bgcolor
=
"#D2DBE8"
></
td
>
</
tr
>
<
tr
>
<
td
width
=
"1"
bgcolor
=
"#D2DBE8"
></
td
>
<
td
align
=
"left"
>
<
fmt:message
key
=
"password"
/></
td
>
<
td
align
=
"left"
>
<
input
type
=
"password"
class
=
"text"
name
=
"j_password"
>
</
td
>
<
td
width
=
"1"
bgcolor
=
"#D2DBE8"
></
td
>
<
tr
>
<
td
width
=
"1"
bgcolor
=
"#D2DBE8"
></
td
>
<
td
align
=
"left"
colspan
=
"3"
>
<
fmt:message
key
=
"rememberme"
/>
<
input
type
=
"checkbox"
name
=
"_acegi_security_remember_me"
>
</
td
>
<
td
width
=
"1"
bgcolor
=
"#D2DBE8"
></
td
>
</
tr
>
</
table
>
</
td
></
tr
>
<
c:if
test
=
"
${! empty param.login_error}">
<
tr
><
td
><
br
>
<
fmt:message
key
=
"login.failed"
/>
<%=
((AuthenticationException) session.getAttribute(AbstractProcessingFilter.ACEGI_SECURITY_LAST_EXCEPTION_KEY)).getMessage()
%>
</
td
>
</
tr
>
</
c:if
>
</
table
>
</
form
>
最后,你需要配置
URL
权限,关于
POJO
的方法权限俺还没有弄懂,而且,如果不提供远程访问的情况下,一般来说也不需要。在
ApplicationContext-acegi-securtiy.xml
找到
bean
:
filterInvocationInterceptor
这个是基于
Http Request
验证的权限
terceptor
,
注意设置
bjectDefinitionSource
属性,下面是例子,
URL
的格式是参考
ANT
的格式,也可以根据正则表达式的写法:
<
property
name
=
"objectDefinitionSource"
>
<
value
>
<![CDATA[
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/index.faces=ROLE_SUPERVISOR,ROLE_ANONYMOUS,ROLE_USER
/login.jsp*=ROLE_SUPERVISOR,ROLE_ANONYMOUS,ROLE_USER
/images/*.*=ROLE_SUPERVISOR,ROLE_ANONYMOUS,ROLE_USER
/common/*.*=ROLE_SUPERVISOR,ROLE_ANONYMOUS,ROLE_USER
/styles/*.*=ROLE_SUPERVISOR,ROLE_ANONYMOUS,ROLE_USER
/**=ROLE_USER
]]>
</
value
>
各个权限入口的顺序十分重要,注意必须把特殊的
URL
权限写在一般的
URL
权限之前。
7. Acegi
1.0
是基于
JDK1.5
的,虽然你可以在
1
.4
下使用,但是我还是把我的项目改为
JDK1
.5,
没成想还出现了一些问题。另外
JSTL
的也不同了,如果你想使用
EL
,则必须这样引用
core
:
<%@
taglib
uri
=
"http://java.sun.com/jstl/core_rt"
prefix
=
"c"
%>
在
JDK1.4
下使用
String.replaceFirst
和
replaceAll
方法没有问题,但是在
JDK1.5
下却报
IllegalArgumentException
,
getMessage
指出
Illegal group arguments
,但是单独写测试类运行却没有任何问题,我只好些了自己的
replace
算法,可是总觉得应该使用
JDK
提供的,希望达人指教。
8
.关于如何使用
ACL
、
SSL
等验证,俺需要进一步研究。