权限控制:spring 3.0 security配置例子
我几年前自己写过一个后台权限管理的东西,也是基于用户-角色-权限这样的结构,说实话代码结构写得不好,因为是硬编码的形式,虽然功能其实用起来还是挺好用的,用户、角色及权限的管理均可在后台轻松完成,但现在如果再用硬编码的形式写这样的系统就说不过去了。目前关于权限管理的java开源系统比较多,有的比较有名,有的不出名,比如像Spring Security就很出名,在权限管理方面做得也非常全面,子猴这篇文章也是对Spring Security做个简短介绍。
我以最新版的spring-security-3.0.2作为介绍对象,如果你了解Spring 2.0 Security的话,那就比较抱歉了,因为spring-security-3.0.2与2.0相比改动很大,在2.0中的一些配置拿到3.0几乎是行不通的,如果你还没有接触过spring,那么以下是其地址:
Spring官方网址:http://www.springsource.org/
spring-security下载地址:
http://static.springsource.org/spring-security/site/downloads.html
我以其自带的一个简单例子来介绍一下吧,因为这个例子配置不对的话极易报错(这个例子是个打包的War文件,但与文档中的又不一样,所以极易出错),我会对出现的错误给予出错原因及解决方法。
首先通过spring-security地址下载到最新版的spring-security-3.0.2.RELEASE.zip,然后解压开来,在解压开的目录dist中,你会看到如下一些文件:
看到spring-security-samples-tutorial-3.0.2.RELEASE.war了吗?我就以这个为例,把这个包拷贝到你Web服务器(如Tomcat)的webapps目录下,启动服务器后,会生成一个spring-security-samples-tutorial-3.0.2.RELEASE项目,可以把名字改短一点方便访问,比如我这里改名为:spring-security,这样通过http://127.0.0.1/spring-security可以直接访问了。
进入目录WEB-INF,可以看到如上的一些文件,其中,applicationContext-security.xml是权限控制配置文件,所有的权限控制都是在其中配置的,bank-servlet.xml是系统的上下文配置文件,可以在其中配置访问路径映射(类似于Struts-1.x中的struts-config.xml或struts-2.x中的struts.xml文件),也可以在其中进行一些装配等工作,属于spring级的,与安全配置没多大关系。
具体的文件内容我就不展示了,因为可以自己打开看,我这里只说要注意的一些地方:
1、在web.xml中的配置
服务启动时加载applicationContext-security.xml文件:
<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>
你一定注意到了,这里的filter-class与Spring2.0的不同之处,如果你了解Spring2.0的话,的确,在Spring2.0中为:
<filter-name>acegiFilterChain</filter-name>
<filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class>
这就是3.0与2.0改变很大的一个地方,2.0用的为Acegi,后来Acegi嵌入到了Spring中,成为了Spring Security,所以包的路径也都改变了!
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>
上面这段是初用者比较容易出错的地方,这其实也是写这篇文章的主要原因之一,注意到第一行的黑体字:
<http use-expressions=”true”>
表示这里的配置可以使用一种表达式,这种表达式就是类似于isAuthenticated()这样的写法,在后面会看到与这种写法不一样但同样可以达到相同效果的写法。
intercept-url表示要拦截的url形式,比如
<intercept-url pattern=”/secure/**” access=”isAuthenticated()” />
表示根目录下的secure目录需要经过验证后才能访问的。
<form-login />是Spring Security自动为你生成的一个简陋的登录页面,即使你没有创建任何登录页面,当然你也可以修改,但不建议你修改,因为你可以不使用默认的,可以采用如下方式:<form-login login-page=’/ login.html’/>自定义一个登录页面。
其他的说明可以参考一个翻译的中文文档:
http://www.family168.com/tutorial/springsecurity3/html/ns-config.html
3、容易出错的地方
在上面的翻译文档(也是翻译自官方文档)或英文官方文档中,给出的与上面例子功能相似的说明大概是这样的:
? <http auto-config='true'>
??? <intercept-url pattern="/**" access="ROLE_USER" />
? </http>
注意到与上面例子不同的地方了吗?
还是注意第一行,这回不是<http use-expressions=”true”>而是<http auto-config=’true’>了,而下面的配置
<intercept-url pattern=”/**” access=”ROLE_USER” />也与上面的写法不同,事实上如果是<http use-expressions=”true”>的话,这句access=”ROLE_USER”就是错的,正确的写法就应该为:hasRole(‘ROLE_USER’)。
这不得不说这是Spring Security的文档与例子不搭配的一个低级错误,因为当一个使用者在打开例子又看到文档说明时,他往往不知道这两者有何区别,就如同我刚使用的时候一样,我在使用<http use-expressions=”true”>的同时,将access=”ROLE_USER”这种写法也写了进来,结果可想而知,报了错!报错信息诸如:
org.apache.jasper.JasperException: java.lang.IllegalArgumentException: Failed to evaluate
expression ‘ROLE_SUPERVISOR’
就是说use-expressions这种表示法是不认识access=”ROLE_USER”这种写法的,另外,当使用了<http use-expressions=”true”>时,一定要在配置中至少有一个符合use-expressions的表示法,否则就会报类似如下错误:
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’
这个简单的配置例子大概就写这么多吧,最后说说我对Spring Security的看法,我个人觉得Spring Security的功能的确是很强大,考虑得也非常全面,几乎什么都想替使用者做完,但正是它的这点,我觉得倒是它的缺点,在我了解它并配置的过程中,我个人觉得是非常复杂繁琐的,而且它现在的文档支持也并不好,正如上面所看到的一样,文档与例子中的写法都不一致。事实上,使用者并不希望它什么都做到,比如它做的那个缺省登录页面,那个有意义吗?使用者比如我,其实就是希望一个很简单的权限判断,比如我打开某一个链接,然后你告诉我访问者有无权限访问,给我返回一个类似true或false的结果就够了!至于其他的事情,我是如何的处理后续过程并不用Spring Security操心的