Spring官方|址Q?a >http://www.springsource.org/
spring-security下蝲地址Q?/p>
http://static.springsource.org/spring-security/site/downloads.html
我以其自带的一个简单例子来介绍一下吧Q因个例子配|不对的话极易报错(q个例子是个打包的War文gQ但与文档中的又不一P所以极易出错)Q我会对出现的错误给予出错原因及解决Ҏ?/p>
首先通过spring-security地址下蝲到最新版的spring-security-3.0.2.RELEASE.zipQ然后解压开来,在解压开的目录dist中,你会看到如下一些文Ӟ
看到spring-security-samples-tutorial-3.0.2.RELEASE.war了吗Q我׃q个ZQ把q个包拷? CWeb服务器(如TomcatQ的webapps目录下,启动服务器后Q会生成一个spring-security-samples- tutorial-3.0.2.RELEASE目Q可以把名字改短一Ҏ便访问,比如我这里改名ؓQspring-securityQ这样通过http://127.0.0.1/spring-security可以直接讉K了?/p>
q入目录WEB-INFQ可以看到如上的一些文Ӟ其中QapplicationContext-security.xml是权限控刉|文Ӟ所 有的权限控制都是在其中配|的Qbank-servlet.xml是系l的上下文配|文Ӟ可以在其中配|访问\径映(cM于Struts-1.x中的 struts-config.xml或struts-2.x中的struts.xml文gQ,也可以在其中q行一些装配等工作Q属于springU的Q与 安全配置没多大关pR?/p>
具体的文件内Ҏ׃展示了,因ؓ可以自己打开看,我这里只说要注意的一些地方:
1、在web.xml中的配置
服务启动时加载applicationContext-security.xml文gQ?/p>
<context-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/applicationContext-security.xml </param-value> </context-param>
要过滤链接的形式
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
你一定注意到了,q里的filter-class与Spring2.0的不同之处,如果你了解Spring2.0的话Q的,在Spring2.0中ؓQ?/p>
<filter-name>acegiFilterChain</filter-name>
<filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class>
q就?.0?.0改变很大的一个地方,2.0用的为AcegiQ后来Acegi嵌入CSpring中,成ؓ了Spring SecurityQ所以包的\径也都改变了Q?/p>
2、在applicationContext-security.xml中的配置
<http use-expressions="true"> <intercept-url pattern="/secure/extreme/**" access="hasRole('ROLE_SUPERVISOR')"/> <intercept-url pattern="/secure/**" access="isAuthenticated()" /> <!-- Disable web URI authorization, as we're using <global-method-security> and have @Secured the services layer instead <intercept-url pattern="/listAccounts.html" access="isRememberMe()" /> <intercept-url pattern="/post.html" access="hasRole('ROLE_TELLER')" /> --> <intercept-url pattern="/**" access="permitAll" /> <form-login /> <logout /> <remember-me /> <!-- Uncomment to enable X509 client authentication support <x509 /> --> <!-- Uncomment to limit the number of sessions a user can have --> <session-management invalid-session-url="/timeout.jsp"> <concurrency-control max-sessions="1" error-if-maximum-exceeded="true" /> </session-management> </http>
上面q段是初用者比较容易出错的地方Q这其实也是写这文章的主要原因之一Q注意到W一行的黑体字:
<http use-expressions=”true?gt;
表示q里的配|可以用一U表辑ּQ这U表辑ּ是cM于isAuthenticated()q样的写法,在后面会看到与这U写法不一样但同样可以辑ֈ相同效果的写法?/p>
intercept-url表示要拦截的url形式Q比?/p>
<intercept-url pattern=?secure/**?access=”isAuthenticated()?/>
表示根目录下的secure目录需要经q验证后才能讉K的?/p>
<form-login />是Spring Security自动Z生成的一个简陋的d面Q即使你没有创徏Md面Q当然你也可以修改,但不你修改,因ؓ你可以不使用默认的,可以采用 如下方式Q?lt;form-login login-page=? login.html?>自定义一个登录页面?/p>
其他的说明可以参考一个翻译的中文文档Q?/p>
http://www.family168.com/tutorial/springsecurity3/html/ns-config.html
3、容易出错的地方
在上面的译文档Q也是翻译自官方文档Q或英文官方文档中,l出的与上面例子功能怼的说明大概是q样的:
<http auto-config='true'>
<intercept-url pattern="/**" access="ROLE_USER" />
</http>
注意C上面例子不同的地方了吗?
q是注意W一行,q回不是<http use-expressions=”true?gt;而是<http auto-config=’true?gt;了,而下面的配置
<intercept-url pattern=?**?access=”ROLE_USER?/strong> />也与上面的写法不同,事实上如果是<http use-expressions=”true?gt;的话Q这?strong>access=”ROLE_USER?/strong>是错的Q正的写法应该ؓQhasRole(‘ROLE_USER??/p>
q不得不说这是Spring Security的文档与例子不搭配的一个低U错误,因ؓ当一个用者在打开例子又看到文档说明时Q他往往不知道这两者有何区别,如同我刚用的时候一P我在使用<http use-expressions=”true?gt;的同Ӟ?strong>access=”ROLE_USER?/strong>q种写法也写了进来,l果可想而知Q报了错Q报错信息诸如: 是?strong>use-expressionsorg.apache.jasper.JasperException: java.lang.IllegalArgumentException: Failed to evaluate
expression 'ROLE_SUPERVISOR'
org.springframework.expression.spel.SpelEvaluationException: EL1008E:(pos 0): Field or property 'ROLE_SUPERVISOR' cannot be found on object of type 'org.springframework.security.web.access. expression.WebSecurityExpressionRoot'
q个单的配置例子大概写q么多吧Q最后说说我对Spring Security的看法,我个得Spring Security的功能的是很强大,考虑得也非常全面Q几乎什么都x使用者做完,但正是它的这点,我觉得倒是它的~点Q在我了解它q|的q程中,? 个h觉得是非常复杂繁琐的Q而且它现在的文档支持也ƈ不好Q正如上面所看到的一P文档与例子中的写法都不一致。事实上Q用者ƈ不希望它什么都做到Q比 如它做的那个~省d面Q那个有意义吗?使用者比如我Q其实就是希望一个很单的权限判断Q比如我打开某一个链接,然后你告诉我讉K者有无权限访问,l? 我返回一个类似true或false的结果就够了Q至于其他的事情Q我是如何的处理后箋q程q不用Spring Security操心的,但很昄Q子猴认为Spring Security在这点上做得q不好?
h意:?span style="font-family: Arial;">2.0.0之前Q?span style="font-family: Arial;">Spring SecurityUCؓAcegi
Security。老的Acegi Security提供了一?span style="font-family: Arial;">ACL模块Q放?code>org.[acegisecurity/springsecurity].acl包下。这个老的现在已经被抛弃,q可能会在将来的
Spring Security
发行版中L。本章讨论新?/code>
ACL
模块Q官Ҏ荐在
Spring Security 2.0.0或更高版本中使用Q它被置?code>org.springframework.security.acls包下?/code>
复杂的应用常怼有需要定义访问权限——不只是?span style="font-family: Arial;">webh和方法调用水q的控制。而是安全册需要由?span style="font-family: Arial;">who (Authentication
), where (MethodInvocation
) and what (SomeDomainObject
)”构成。也是_授权册q要考虑Ҏ调用中的实际域对象?/p>
惌你正在ؓ宠物诊所设计一个应用。你的基?span style="font-family: Arial;">Spring的应用主要有两组用户Q宠物诊所的职员和客户。职员可以访问所有数据,但客户只能看到自q客户记录。ؓ了ɘq个例子更加有趣一点,你的客户可以让其它客L他们的客戯录,例如他们"puppy preschool"的顾问或本地"Pony Club"的总裁。以Spring Security为基Q你有几个方式可以选择Q?/p>
1.
~写一个自q业务Ҏ强制安全控制。你可以?span style="font-family: Arial;">Customer域对象中获取一个有讉K权限用户的集合。可以用SecurityContextHolder.getContext().getAuthentication()
来访?/code>
Authentication
对象?/code>
2.
~写一?/code>
AccessDecisionVoter
Q通过
Authentication
中储存的
GrantedAuthority[]
来控制安全检查。这意味着你的
AuthenticationManager
要把代表用户对域对象讉K权限?/code>
GrantedAuthority
[]填充?code>Authentication对象中去?/code>
3.
~写一?code>AccessDecisionVoter来强制安全控Ӟ直接打开目标
Customer
域对象。这意味着你的
voter
需要访?/code>
DAO
来获?/code>
Customer
对象。它接着会访?/code>
Customer
对象集合来进行安全决议?/code>
q些Ҏ都是合情合理的。但是,W一个方法把你的授权查和业务代码耦合C起了。带来的主要问题包括提高了单元测试的隑ֺQ以?code>Customer授权逻辑很难在其它地斚w用。从
Authentication
中获?code>GrantedAuthority[]是好的,但是面对数量很大?/code>
Customer
s时也是不行的。如果用戯问的Customer
s数量可能辑ֈ5,000个(在这个例子里不大可能Q但是想象如果是一个很大的Pony Club里的兽医Q)Q构?code>Authentication需要耗费的内存和旉可能不是你想要的。最后一U方法,~写代码从外部直接打开
Customer
Q可能是q?/code>
3
个方法里最好的一个。它成功分离了关注点Q也没有滥用内存?/code>
CPU
Q但q是效率很低Q?/code>
AccessDecisionVoter
和最l的业务Ҏ都要自己来调?/code>
DAO
Ҏ来获?/code>
Customer
对象。一ơ业务方法调用要讉K两次
DAO
很明显也不是你想要的。另外,上面提到的每一U方法你都要自己从头来编写自q讉K控制列表Q?/code>
ACL
Q持久化和业务逻辑?/code>
q运圎ͼq有另外一个选择Q下面我们来讨论一下这个选择?/p>
Spring Security?span style="font-family: Arial;">ACL服务发布?code>spring-security-acl-xxx.jar。要使用
Spring Security的域对象安全Q你要把q个包加入到你的classpath中?/p>
Spring Security的域对象安全以访问控制列表(ACLQؓ核心概念。你pȝ里的每个域对象都有自qACLQ这?span style="font-family: Arial;">ACL记录了谁能或不能讉K该域对象?span style="font-family: Arial;">Spring SecurityZ的应用带?span style="font-family: Arial;">3个主要的ACL相关能力?/p>
· 提供一条可以高效地Z所有的域对象获?span style="font-family: Arial;">ACL条目Q以及修?span style="font-family: Arial;">ACLQ的途径Q?/p>
· 提供一条在Ҏ被调用前保证l定用户有访问你的域对象许可的途径Q?/p>
· 提供一条在Ҏ被调用后保证l定用户有访问你的域对象许可的途径Q?/p>
正如W一Ҏ_Spring Security?span style="font-family: Arial;">ACL模块的主要能力之一是提供一个高性能获取ACL的途径。这?span style="font-family: Arial;">ACL repository的能力是非常重要的,因ؓ你系l中的每一个域对象可能有若q个讉K控制条目Q每?span style="font-family: Arial;">ACL又可能是从其?span style="font-family: Arial;">ACLl承而来——树型结构(q很常用Q?span style="font-family: Arial;">Spring Security提供了很Ҏ使用的支持)?span style="font-family: Arial;">Spring Security?span style="font-family: Arial;">ACL模块已经很小心的考虑了设计以满高性能?span style="font-family: Arial;">ACL索,它利用了可插接的~存模块Q死锁最化的数据库更新Q独立于ORM框架Q我们用直?span style="font-family: Arial;">JDBCQ,适当的包装,以及透明的数据库更新?/p>
数据库的设计是以ACL模块的操作ؓ中心的,让我们来看看该模块的实现里缺省用的4个主要数据表。下面列出在一个典型的Spring Security ACL部v中的数据表,以数据量大小排序Q行数最多的在最后面Q?/p>
·
ACL_SID使我们能够在pȝ中唯一定义Mprincipal?span style="font-family: Arial;">authorityQ?span style="font-family: Arial;">SID”表C?span style="font-family: Arial;">Security IDentity”)。主键是IDQ?span style="font-family: Arial;">SID的文字表C,以及一个表CSID?span style="font-family: Arial;">principal名还?code>GrantedAuthority的标识。每?/code>
principal
?/code>
GrantedAuthority
都有一个对应的记录。当在接受许可的上下文中使用的时候,
SID
通常被称为“接收者(
recipientQ”?/code>
· ACL_CLASS使我们能够在pȝ中唯一定义M域对象类。主键是IDQ然后是CLASSQ?span style="font-family: Arial;">javacdQ。每个我们想要ؓ其存?span style="font-family: Arial;">ACL许可的类有一个记录?/p>
· ACL_OBJECT_IDENTITY存储pȝ里每个域对象实例的信息。其中的列包?span style="font-family: Arial;">IDQ一个到?span style="font-family: Arial;">ACL_CLASS 的外?span style="font-family: Arial;">OBJECT_ID_CLASSQ一个我们知道是ؓ哪个ACL_CLASScd例提供信息的唯一标识OBJECT_ID_IDENTITYQ父对象Q一个到ACL_SID的外?span style="font-family: Arial;">OWNER_SID表示该域对象的所有者;ENTRIES_INHERITING表示是否允许ACL条目?span style="font-family: Arial;">ACL父条目ѝ?/p>
· 最后,ACL_ENTRY存储为每?span style="font-family: Arial;">recipient分配的权限。它的列包括一个到?span style="font-family: Arial;">ACL_OBJECT_IDENTITY的外键,recipient(例如一个到ACL_SID的外?span style="font-family: Arial;">)Q是否需要审计,以及一个表C可是授予q是拒绝的“整C掩码”(许可可能包括多个斚wQ如view, edit, deleteQ。每?span style="font-family: Arial;">recipientҎ个域对象的许可都有一个记?/p>
上一D中提到Q?span style="font-family: Arial;">ACLpȝ使用了“整C掩码”。不要担心,你不需要知道这些数字指向的具体位移来?span style="font-family: Arial;">ACLpȝQ只需要知道我们有32个开兛_以打开或关闭就可以了(一?span style="font-family: Arial;">int数?span style="font-family: Arial;">4?span style="font-family: Arial;">byteQ共32?span style="font-family: Arial;">bitsQ。每一个位代表一?span style="font-family: Arial;">permissionQ缺省地Q这?span style="font-family: Arial;">permission是:读(bit 0Q,写(bit 1Q,创徏Q?span style="font-family: Arial;">bit 2Q,删除Q?span style="font-family: Arial;">bit 3Q和理Q?span style="font-family: Arial;">bit 4Q。如果你要用其?span style="font-family: Arial;">permissionQ可以很Ҏ的实现自qpermission实例Q?span style="font-family: Arial;">ACL框架的其余部分不需要具备你定义的扩展的知识Q即可完成操作?/p>
明白我们采用q种“整C掩码”对你的pȝ中域对象的数量是完全没有影响q一Ҏ很重要的。我们的permission只能使用32?span style="font-family: Arial;">bitQ但是你可以有数以十亿计的域对象Q这也意味着会有C十亿计的记录?span style="font-family: Arial;">ACL_OBJECT_IDENTITY表中Q?span style="font-family: Arial;">ACL_ENTRY表的记录则更多)。我们特别说明这一Ҏ因ؓ发现有些用户错误的以为我们要为每个潜在的域对象用一?span style="font-family: Arial;">bitQ这是不对的?/p>
现在我们已经提供了一?span style="font-family: Arial;">ACLpȝ的基本概览,以及在表l构上看h的样子,现在让我们来看看关键接口。关键接口列表如下:
·
AclQ每一个域对象有且仅有一?span style="font-family: Arial;">ACL的对象,它内部拥?code>AccessControlEntryQƈ知道Acl的所有者?span style="font-family: Arial;">Acl不直接指向域对象Q而是指向一?code>ObjectIdentity
·
·
·
·
·
·
h意我们提供的AclService和相关的数据库类都是使用ANSI SQL的。因此可以工作在所有主数据库服务器上。在~写本文的时候,已经?span style="font-family: Arial;">Hypersonic SQL, PostgreSQL, Microsoft SQL Server ?span style="font-family: Arial;"> Oracle上测试过?/p>
Spring Security发行包中有两个例子可以用来演C?span style="font-family: Arial;">ACL模块。其一?span style="font-family: Arial;">Contacts例子Q另外一个是Document Management
System (DMS)。我们徏议你仔细看看q些例子?/p>
要开始?span style="font-family: Arial;">Spring Security?span style="font-family: Arial;">ACL能力Q你需要把你的ACL保存在某个地斏V这要通过Spring实例化一?code>DataSource 创徏所需数据表ƈ实例?code>JdbcMutableAclService 下面的代码展C如何创Z?span style="font-family: Arial;">AclQ或修改一个已存在?span style="font-family: Arial;">AclQ?/p>
在上面的例子中,我们获取?span style="font-family: Arial;">ACL是和一个标识(identiferQؓ44?span style="font-family: Arial;">"Foo"域对象联pd一L。我们增加一?span style="font-family: Arial;">ACE以便一个叫"Samantha"?span style="font-family: Arial;">principal?span style="font-family: Arial;">"administer"该对象。除?span style="font-family: Arial;">insertAceҎQ这D代码不需要再多加说明。它的第一个参数是新的ACE要插入到Acl的什么位|。上面的例子中,我们把它攑ֈ最后。最后一个参数是一个表CACE授权或拒l的boolean倹{大部分时候应该ؓ授权Q?span style="font-family: Arial;">trueQ,但是如果为拒l(falseQ,?span style="font-family: Arial;">permission会被有效的关闭?/p>
Spring Security没有?span style="font-family: Arial;">DAO?span style="font-family: Arial;">repository中ؓ自动创徏、更新或删除ACL提供M特别集成。你要ؓ域对象编写像上面q样的代码。在你的业务层中使用AOP来自动集?span style="font-family: Arial;">ACL信息和你业务层操作是值得考虑的。我们发现这是一U很有效的方式?/p>
一旦你已经使用了上面的技术来在数据库中存储一?/span>
ACL
信息Q下一步就是实际如何用这?/span>
ACL
信息作ؓ你的授权册逻辑的一部分。在q里你有好几个选择。你可以分别在方法调用前和调用后~写自己?/span>
?/code>
Acl
是存储在
ACL_OBJECT_IDENTITY表中的?/p>
AccessControlEntry
Q一?/code>
Acl
包含多个
AccessControlEntry
Q在ACL框架中,我们常常把它UCؓACE。每?span style="font-family: Arial;">ACE都指向一l?span style="font-family: Arial;">PermissionQ?span style="font-family: Arial;">Sid?span style="font-family: Arial;">Acl?span style="font-family: Arial;">ACE可以?span style="font-family: Arial;">granting?span style="font-family: Arial;">non-granting的,同时包含有审计的相关讄?span style="font-family: Arial;">ACE保存?span style="font-family: Arial;">ACL_ENTRY表中?/p>
Permission
Q?/code>
Permission
表示一个不可变的位掩码QƈZ掩码机制和信息输出提供方便的功能。上面提到的
5
个基本的
Permission
(bits 0 ?span style="font-family: Arial;"> 4)包含?/code>
BasePermission
cM?/code>
Sid
Q?/code>
ACL
模块需要参照到
principal
?/code>
GrantedAuthority[]
?/code>
Sid
接口提供实际安全对象Q如
principal, role, group
{等Q和
Acl
中实际内容的间接联系Q简U“安全n份”。普通的实现包括?/code>
PrincipalSid
Q在
Authentication
中表CZ?/code>
principal
Q和
GrantedAuthoritySid
。安全n份信息放?/code>
ACL_SID表中?/p>
ObjectIdentity
Q?/code>
ACL
模块中,内部?/code>
ObjectIdentity
来表C每个域对象。缺省的实现?/code>
ObjectIdentityImpl
?/code>
AclService
Q用于获?/code>
ObjectIdentity
适用?/code>
Acl
。在包含的实玎ͼ
JdbcAclService
Q中Q获取操作委z
LookupStrategy
?/code>
LookupStrategy
?/code>
ACL
信息提供高度优化的策略,使用批获取(
BasicLookupStrategy
Q,也支持用户实C提供更好的性能Q例如层ơ化查询和类似的以性能Z心的?/code>
ANSI SQL
能力?/code>
MutableAclService
Q允怿?/code>
Acl
以便q行持久化,如果不是Zq个可以不必使用q个接口?/code>
24.3. 入门
。然后该数据源被注入?/code>
JdbcMutableAclService
?/code>
BasicLookupStrategy
。后者提供了高性能?/code>
ACL
获取能力Q前者提供了可改变的能力。请参考发行包中Q何一个例子来了解如何q行配置。你q需要往上一节中提到的四个表中填充数据(参?/code>
ACL
例子看相?/code>
SQL
语句Q?/code>
后,你还要确认你的领域模型支持与
Spring Security ACL包互操作。很可能ObjectIdentityImpl
已经提供了够的支持Q它提供了多U可用的方式。大部分的域对象会包含一?/code>
public Serializable
getId()
Ҏ。如果返回类型是
long
Q或者和
long
兼容Q例?/code>
int
Q,你就不需要烦
ObjectIdentity
的问题了?/code>
ACL
模块的很多部分都依赖?/code>
long identifier
。如果没有?/code>
long
Q或
int
Q?/code>
byte
{)Q你有机会要重新实现几个cR我们不打算?/code>Spring Security ACL模块?code>支持?/code>
long identifier
Q因?/code>
long
和所有的数据?/code>
sequence
兼容Q也是最为常用的
identifier
数据cdQ同时有_的长度支持通常的应用场景?/code>
// Prepare the information we'd like in our access control entry (ACE)
ObjectIdentity oi = new ObjectIdentityImpl(Foo.class, new Long(44));
Sid sid = new PrincipalSid("Samantha");
Permission p = BasePermission.ADMINISTRATION;
// Create or update the relevant ACL
MutableAcl acl = null;
try {
acl = (MutableAcl) aclService.readAclById(oi);
} catch (NotFoundException nfe) {
acl = aclService.createAcl(oi);
}
// Now grant some permissions via an access control entry (ACE)
acl.insertAce(acl.getEntries().length, p, sid, true);
aclService.updateAcl(acl);
AccessDecisionVoter
?/span>
AfterInvocationProvider
。这些类应该使用
AclService
来获取相?/span>
ACL
Q然后调?/span>
Acl.isGranted(Permission[]
permission, Sid[] sids, boolean administrativeMode)
来决定是授权q是拒绝。你也可以用我们的
AclEntryVoter
Q?/span>
AclEntryAfterInvocationProvider
?/span>
AclEntryAfterInvocationCollectionFilteringProvider
cR所有这些类都提供了Z声明的方式,q行时根据这些设定信息来q行评估Q把你从~写代码中解攑և来。请参考例子应用学习如何用这些类?/span>
]]>