SpringSecurity3的核心类有三种
1.URL过滤器或方法拦截器:用来拦截URL或者方法资源对其进行验证,其抽象基类为AbstractSecurityInterceptor
2.资源权限获取器:用来取得访问某个URL或者方法所需要的权限,接口为SecurityMetadataSource
3.访问决策器:用来决定用户是否拥有访问权限的关键类,其接口为AccessDecisionManager。
调用顺序为:AbstractSecurityInterceptor调用SecurityMetadataSource取得资源的所有可访问权限,然后再调用AccessDecisionManager来实现决策,确定用户是否有权限访问该资源。
SecurityMetadataSource包括MethodSecurityMetadataSource和FilterInvocationSecurityMetadataSource,分别对应方法和URL资源。
你也可以完全自定义自己的过滤器、资源权限获取器、访问决策器,下面给出完整的springsecurity3的配置文件:
- <?xml version="1.0" encoding="UTF-8"?>
- <beans:beans xmlns="http://www.springframework.org/schema/security"
- xmlns:beans="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
- http://www.springframework.org/schema/security
- http://www.springframework.org/schema/security/spring-security-3.0.xsd">
- <beans:bean id="loggerListener"class="org.springframework.security.authentication.event.LoggerListener" />
- <http auto-config="true" access-denied-page="/403.jsp">
- <intercept-url pattern="/css/**" filters="none" />
- <intercept-url pattern="/images/**" filters="none" />
- <intercept-url pattern="/js/**" filters="none" />
- <intercept-url pattern="/403.jsp" filters="none" />
- <intercept-url pattern="/" filters="none" />
- <intercept-url pattern="/login.jsp" filters="none" />
- <form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?error=true"default-target-url="/finance/index.do?listId=CONSUMPTION&page.rowCount=10" />
- <logout logout-success-url="/login.jsp"/>
- <!-- 防止同一用户多次登录,使第二次登录失败 -->
- <session-management>
- <concurrency-control max-sessions="1" error-if-maximum-exceeded="true" />
- </session-management>
- <!-- 增加一个filter,这点与Acegi是不一样的,不能修改默认的filter了,这个filter位于FILTER_SECURITY_INTERCEPTOR之前 -->
- <custom-filter before="FILTER_SECURITY_INTERCEPTOR" ref="urlSecurityFilter" />
- </http>
- <!-- 一个自定义的filter,必须包含authenticationManager,accessDecisionManager,securityMetadataSource三个属性,
- 我们的所有控制将在这三个类中实现,解释详见具体配置 -->
- <beans:bean id="urlSecurityFilter"class="com.maxjay.main.system.web.filter.UrlSecurityInterceptorFilter">
- <beans:property name="authenticationManager" ref="authenticationManager" />
- <beans:property name="accessDecisionManager" ref="securityAccessDecisionManager" />
- <beans:property name="securityMetadataSource" ref="urlSecurityMetadataSource" />
- </beans:bean>
- <!-- 认证管理器,实现用户认证的入口,主要实现UserDetailsService接口即可 -->
- <authentication-manager alias="authenticationManager">
- <authentication-provider user-service-ref="userService">
- <!-- 如果用户的密码采用加密的话,可以加点“盐”
- <password-encoder hash="md5" />
- -->
- </authentication-provider>
- </authentication-manager>
- </beans:beans>
<?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd"> <beans:bean id="loggerListener" class="org.springframework.security.authentication.event.LoggerListener" /> <http auto-config="true" access-denied-page="/403.jsp"> <intercept-url pattern="/css/**" filters="none" /> <intercept-url pattern="/images/**" filters="none" /> <intercept-url pattern="/js/**" filters="none" /> <intercept-url pattern="/403.jsp" filters="none" /> <intercept-url pattern="/" filters="none" /> <intercept-url pattern="/login.jsp" filters="none" /> <form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?error=true" default-target-url="/finance/index.do?listId=CONSUMPTION&page.rowCount=10" /> <logout logout-success-url="/login.jsp"/> <!-- 防止同一用户多次登录,使第二次登录失败 --> <session-management> <concurrency-control max-sessions="1" error-if-maximum-exceeded="true" /> </session-management> <!-- 增加一个filter,这点与Acegi是不一样的,不能修改默认的filter了,这个filter位于FILTER_SECURITY_INTERCEPTOR之前 --> <custom-filter before="FILTER_SECURITY_INTERCEPTOR" ref="urlSecurityFilter" /> </http> <!-- 一个自定义的filter,必须包含authenticationManager,accessDecisionManager,securityMetadataSource三个属性, 我们的所有控制将在这三个类中实现,解释详见具体配置 --> <beans:bean id="urlSecurityFilter" class="com.maxjay.main.system.web.filter.UrlSecurityInterceptorFilter"> <beans:property name="authenticationManager" ref="authenticationManager" /> <beans:property name="accessDecisionManager" ref="securityAccessDecisionManager" /> <beans:property name="securityMetadataSource" ref="urlSecurityMetadataSource" /> </beans:bean> <!-- 认证管理器,实现用户认证的入口,主要实现UserDetailsService接口即可 --> <authentication-manager alias="authenticationManager"> <authentication-provider user-service-ref="userService"> <!-- 如果用户的密码采用加密的话,可以加点“盐” <password-encoder hash="md5" /> --> </authentication-provider> </authentication-manager> </beans:beans>
其中userService实现了UserDetailsService用来与自己的用户表进行适配,UrlSecurityInterceptorFilter继承了AbstractSecurityInterceptor并实现了Filter接口,urlSecurityMetadataSource实现了FilterInvocationSecurityMetadataSource接口,securityAccessDecisionManager实现了AccessDecisionManager接口,它们都通过spring的注解声明为容器的一个对象,所以在配置文件中才能直接引用。
springsecurity3有提供了几个已经实现好的访问决策器,其抽象类为AbstractAccessDecisionManager,它使用投票机制(AccessDecisionVoter)来确定用户有没有权限访问某资源,其实现类有三个:
AffirmativeBased:只要有一个投票器通过即审核通过 ConsensusBased:只有当赞成票>反对票时 审核才会通过
UnanimousBased:只要有一个投票器反对,审核就不通过 你可以直接使用该抽象类的实现类,其配置如下:
- <beans:bean id="accessDecisionManager"class="org.springframework.security.access.vote.AffirmativeBased">
- <!-- false意味着当配置的投票器只投了弃权票时,不允许继续执行 -->
- <beans:property name="allowIfAllAbstainDecisions" value="false"/>
- <!-- 配置该决策器所需要的投票器 -->
- <beans:property name="decisionVoters">
- <beans:list>
- <beans:bean class="org.springframework.security.access.vote.RoleVoter"/>
- <beans:bean class="org.springframework.security.access.vote.AuthenticatedVoter"/>
- </beans:list>
- </beans:property>
- </beans:bean>
<beans:bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased"> <!-- false意味着当配置的投票器只投了弃权票时,不允许继续执行 --> <beans:property name="allowIfAllAbstainDecisions" value="false"/> <!-- 配置该决策器所需要的投票器 --> <beans:property name="decisionVoters"> <beans:list> <beans:bean class="org.springframework.security.access.vote.RoleVoter"/> <beans:bean class="org.springframework.security.access.vote.AuthenticatedVoter"/> </beans:list> </beans:property> </beans:bean>
Security框架可以精确控制页面的一个按钮、链接,它在页面上权限的控制实际上是通过它提供的标签来做到的
Security共有三类标签authorize authentication accesscontrollist ,第三个标签不在这里研究
前提:项目需要引用spring-security-taglibs-3.05,jstl1.2的jar包,页面加入:
<%@ taglib prefix=”sec” uri=”http://www.springframework.org/security/tags” %>
本文配置
一、authorize
对应的类: org.springframework.security.taglibs.authz.AuthorizeTag
attribute: access url method ifNotGranted ifAllGranted ifAnyGranted
使用方式:见SimpleDemo的index.jsp
<p>
<sec:authorize ifAllGranted="ROLE_ADMIN">#这里可以用逗号分隔,加入多个角色
你拥有管理员权限,你可以查看 该页面<a href="admin.jsp"> 管理员进入</a> </sec:authorize> </p> <p> <sec:authorize url='/profile.jsp'>你登陆成功了可以看到 <a href="profile.jsp"> 这个页面</a></sec:authorize>
</p>
页面标签的使用与权限配置相对应
<intercept-url pattern="/admin.jsp" access="hasRole('ROLE_ADMIN')" /> <intercept-url pattern="/profile.jsp" access="isAuthenticated()" /> <intercept-url pattern="/**" access="permitAll" />
对比可以看到只有ROLE_ADMIN角色的用户才能访问admin.jsp,通过认证的用户都可以访问profile.jsp
从标签源码可以知道,authorize标签判断顺序是: access->url->ifNotGranted->ifAllGranted->ifAnyGranted 但他们的关系是“与”: 即只要其中任何一个属性不满足则该标签中间的内容将不会显示给用户,举个例子:
<sec:authorize ifAllGranted=”ROLE_ADMIN,ROLE_MEMBER” ifNotGranted=”ROLE_SUPER”>满足才会显示给用户 </sec:authorize>
标签中间的内容只有在当前用户拥有ADMIN,MEMBER角色,但不拥有SUPER权限时才会显示
access属性是基于角色判断,url属性是基于访问路径判断,与security.xml配置对应
对于ifAllGranted ,ifNotGranted,ifAnyGranted属性的理解可以与集合api类比
Collection grantedAuths :当前用户拥有的权限 Collection requiredAuths : 当前要求的权限,即ifAllGranted ,ifNotGranted,ifAnyGranted 属性的值
满足ifAllGranted: 只需要grantedAuths.containsAll(requiredAuths);返回true即可 满足ifAnyGranted: 只需要grantedAuths.retainAll(requiredAuths);有内容即可(两集合有交集) 满足ifNotGranted:与Any相反,如果没有交集即可
二、authentication
对应的类: org.springframework.security.taglibs.authz.AuthenticationTag
attribute: property(required) var htmlEscape scope
使用方式:
<sec:authentication property=’name’ /> <sec:authentication property=’principal.username’ /> <sec:authentication property=’principal.enabled’ /> <sec:authentication property=’principal.accountNonLocked’ />
主要用来显示authentication属性,
var scope: 将property的值以var设置到scope域中
htmlEscape: 将特殊字符转义 > –> “>”
以最新版的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操心的,但很显然,子猴认为Spring Security在这点上做得并不好。http://www.blogjava.net/libin2722/articles/332006.html
最近在项目中用到了Spring Security 2
为了实现自己的一些逻辑,所以粗略的读了一次他的代码,和文档。
文档中介绍了Spring Security 的大体框架和原理。
但是对于如何扩展Spring Security,使它更适合自己的项目并没有详细的介绍,这点比较遗憾。
所以硬着头皮,大致过了一次他的代码。
以下是我扩展的需求:
1、用户登录是判断用户登录错误次数是否超过指定次数,如三次,若超过则锁定用户
2、登录成功更新用户的信息,如最后登录时间,登录IP等,并保存到数据库。
3、登录失败则增加用户登录失败的次数。
对于上面的这些需求,读了源代码之后,你会发现Spring Security的框架十分灵活。
代码也很清晰。
首先,用户登录时,经过了一系列的过滤器,他的过滤器使用Spring来管理的。
其中一个名为AuthenticationProcessFilter的过滤器,专门为网页验证提供。
跟了一下这个过滤器,发现他继承的抽象类AbstractProcessingFilter中有几个可以扩展的方法:
- protectedvoid onPreAuthentication(HttpServletRequest request,HttpServletResponse response) throws AuthenticationException, IOException {
- }
- protectedvoid onSuccessfulAuthentication(HttpServletRequest request,HttpServletResponse response,Authentication authResult) throws IOException {
- }
- protectedvoid onUnsuccessfulAuthentication(HttpServletRequest request,HttpServletResponse response,
- AuthenticationException failed) throws IOException {
- }
- protectedvoid onPreAuthentication(HttpServletRequest request,HttpServletResponse response) throws AuthenticationException, IOException {
- }
- protectedvoid onSuccessfulAuthentication(HttpServletRequest request,HttpServletResponse response,Authentication authResult) throws IOException {
- }
- protectedvoid onUnsuccessfulAuthentication(HttpServletRequest request,HttpServletResponse response,
- AuthenticationException failed) throws IOException {
- }
protected void onPreAuthentication(HttpServletRequest request,HttpServletResponse response) throws AuthenticationException, IOException { } protected void onSuccessfulAuthentication(HttpServletRequest request,HttpServletResponse response,Authentication authResult) throws IOException { } protected void onUnsuccessfulAuthentication(HttpServletRequest request,HttpServletResponse response, AuthenticationException failed) throws IOException { }
看方法名大家应该能想到他的用处,就是在验证前,验证成功后,验证失败后,提供扩展的逻辑操作。
所以我们可以通过继承AuthenticationProcessFilter并重写这几个方法来增加自己的逻辑。
重新实现了过滤器之后要重新设置Spring Security 2的过滤器链,而且原来的auto-config也不能使用。否则将无效
配置如下:
- <httpentry-point-ref="authenticationEntryPoint">
- <intercept-urlpattern="/js/*"filters="none"/>
- <intercept-urlpattern="/styles/*"filters="none"/>
- <intercept-urlpattern="/images/*"filters="none"/>
- <intercept-urlpattern="/login"filters="none"/>
- <intercept-urlpattern="/reg"filters="none"/>
- <intercept-urlpattern="/"filters="none"/>
- <intercept-urlpattern="/registerMember"filters="none"/>
- <intercept-urlpattern="/index.jsp"access="ROLE_USER"/>
- <intercept-urlpattern="/member/*"access="ROLE_USER"/>
- <!-- <form-login login-processing-url="/checkMember" login-page="/login" authentication-failure-url="/login?error=true" default-target-url="/" /> -->
- <anonymous/>
- <logoutlogout-url="/logout"logout-success-url="/"/>
- <remember-mekey="e37f4b31-0c45-11dd-bd0b-0800200c9a66"/>
- </http>
- <authentication-manageralias="authenticationManager"/>
- <beans:beanclass="com.mysport.security.filter.EnhanceAuthenticationProcessFilter">
- <custom-filterposition="AUTHENTICATION_PROCESSING_FILTER"/>
- <beans:propertyname="authenticationManager"ref="authenticationManager"/>
- <beans:propertyname="authenticationFailureUrl"value="/login?error=true"/>
- <beans:propertyname="defaultTargetUrl"value="/"/>
- <beans:propertyname="filterProcessesUrl"value="/checkMember"/>
- <beans:propertyname="memberService"ref="memberService"/>
- </beans:bean>
- <beans:beanid="authenticationEntryPoint"class="org.springframework.security.ui.
- webapp.AuthenticationProcessingFilterEntryPoint">
- <beans:propertyname="loginFormUrl"value="/login"/>
- </beans:bean>
- <<SPANclass=hilite1><SPANstyle="BACKGROUND-COLOR: #ffff00">http</SPAN></SPAN><SPANclass=hilite2><SPANstyle="BACKGROUND-COLOR: #55ff55">entry-point-ref</SPAN></SPAN>="authenticationEntryPoint">
- <intercept-urlpattern="/js/*"filters="none"/>
- <intercept-urlpattern="/styles/*"filters="none"/>
- <intercept-urlpattern="/images/*"filters="none"/>
- <intercept-urlpattern="/login"filters="none"/>
- <intercept-urlpattern="/reg"filters="none"/>
- <intercept-urlpattern="/"filters="none"/>
- <intercept-urlpattern="/registerMember"filters="none"/>
- <intercept-urlpattern="/index.jsp"access="ROLE_USER"/>
- <intercept-urlpattern="/member/*"access="ROLE_USER"/>
- <!-- <form-login login-processing-url="/checkMember" login-page="/login" authentication-failure-url="/login?error=true" default-target-url="/" /> -->
- <anonymous/>
- <logoutlogout-url="/logout"logout-success-url="/"/>
- <remember-mekey="e37f4b31-0c45-11dd-bd0b-0800200c9a66"/>
- </<SPANclass=hilite1><SPANstyle="BACKGROUND-COLOR: #ffff00">http</SPAN></SPAN>>
- <authentication-manageralias="authenticationManager"/>
- <beans:beanclass="com.mysport.security.filter.EnhanceAuthenticationProcessFilter">
- <custom-filterposition="AUTHENTICATION_PROCESSING_FILTER"/>
- <beans:propertyname="authenticationManager"ref="authenticationManager"/>
- <beans:propertyname="authenticationFailureUrl"value="/login?error=true"/>
- <beans:propertyname="defaultTargetUrl"value="/"/>
- <beans:propertyname="filterProcessesUrl"value="/checkMember"/>
- <beans:propertyname="memberService"ref="memberService"/>
- </beans:bean>
- <beans:beanid="authenticationEntryPoint"class="org.springframework.security.ui.
- webapp.AuthenticationProcessingFilterEntryPoint">
- <beans:propertyname="loginFormUrl"value="/login"/>
- </beans:bean>
<http entry-point-ref="authenticationEntryPoint"> <intercept-url pattern="/js/*" filters="none" /> <intercept-url pattern="/styles/*" filters="none" /> <intercept-url pattern="/images/*" filters="none" /> <intercept-url pattern="/login" filters="none" /> <intercept-url pattern="/reg" filters="none" /> <intercept-url pattern="/" filters="none" /> <intercept-url pattern="/registerMember" filters="none" /> <intercept-url pattern="/index.jsp" access="ROLE_USER" /> <intercept-url pattern="/member/*" access="ROLE_USER" /> <!-- <form-login login-processing-url="/checkMember" login-page="/login" authentication-failure-url="/login?error=true" default-target-url="/" /> --> <anonymous /> <logout logout-url="/logout" logout-success-url="/" /> <remember-me key="e37f4b31-0c45-11dd-bd0b-0800200c9a66" /> </http> <authentication-manager alias="authenticationManager"/> <beans:bean class="com.mysport.security.filter.EnhanceAuthenticationProcessFilter"> <custom-filter position="AUTHENTICATION_PROCESSING_FILTER" /> <beans:property name="authenticationManager" ref="authenticationManager" /> <beans:property name="authenticationFailureUrl" value="/login?error=true" /> <beans:property name="defaultTargetUrl" value="/" /> <beans:property name="filterProcessesUrl" value="/checkMember" /> <beans:property name="memberService" ref="memberService" /> </beans:bean> <beans:bean id="authenticationEntryPoint" class="org.springframework.security.ui. webapp.AuthenticationProcessingFilterEntryPoint"> <beans:property name="loginFormUrl" value="/login" /> </beans:bean>
如果使用Remember Me则要以类似的方式实现RememberMeProcessingFilter
1、要去掉http中的
<remember-me key="yourkey" />
2、实现rememberMeService
- <beans:beanid="rememberMeServices"class="org.springframework.security.ui.rememberme.TokenBasedRememberMeServices">
- <beans:propertyname="key"value="yourkey"/>
- <beans:propertyname="userDetailsService"ref="securityManager"/>
- </beans:bean>
- <beans:beanid="rememberMeServices"class="org.springframework.security.ui.rememberme.TokenBasedRememberMeServices">
- <beans:propertyname="key"value="yourkey"/>
- <beans:propertyname="userDetailsService"ref="securityManager"/>
- </beans:bean>
<beans:bean id="rememberMeServices" class="org.springframework.security.ui.rememberme.TokenBasedRememberMeServices" > <beans:property name="key" value="yourkey" /> <beans:property name="userDetailsService" ref="securityManager" /> </beans:bean>
3、实现自己的RememberMeProcessingFilter
- <beans:beanclass="com.mysport.security.filter.EnhanceRememberMeProcessingFilter">
- <custom-filterposition="REMEMBER_ME_FILTER"/>
- <beans:propertyname="authenticationManager"ref="authenticationManager"/>
- <beans:propertyname="rememberMeServices"ref="rememberMeServices"/>
- <beans:propertyname="memberService"ref="memberService"/>
- </beans:bean>
- <beans:beanclass="com.mysport.security.filter.EnhanceRememberMeProcessingFilter">
- <custom-filterposition="REMEMBER_ME_FILTER"/>
- <beans:propertyname="authenticationManager"ref="authenticationManager"/>
- <beans:propertyname="rememberMeServices"ref="rememberMeServices"/>
- <beans:propertyname="memberService"ref="memberService"/>
- </beans:bean>
<beans:bean class="com.mysport.security.filter.EnhanceRememberMeProcessingFilter"> <custom-filter position="REMEMBER_ME_FILTER" /> <beans:property name="authenticationManager" ref="authenticationManager" /> <beans:property name="rememberMeServices" ref="rememberMeServices" /> <beans:property name="memberService" ref="memberService" /> </beans:bean>
4、最重要的,重新将rememberMeAuthenticationProvider添加到providers中,主要是设置对应的key,否则cookie在解密时无法正确解出内容
- <beans:beanid="rememberMeAuthenticationProvider"class="org.springframework.security.providers.rememberme.RememberMeAuthenticationProvider">
- <custom-authentication-provider/>
- <beans:propertyname="key"value="yourkey"/>
- </beans:bean>
- <beans:beanid="rememberMeAuthenticationProvider"class="org.springframework.security.providers.rememberme.RememberMeAuthenticationProvider">
- <custom-authentication-provider/>
- <beans:propertyname="key"value="yourkey"/>
spring Security 3.1的配置(二)
四.将用户角色及权限放入数据库(mysql 5)。
首先先配置数据源,可以是任何的库。Jar包就用下载的ss3.1里的包就可以。以下以mysql为例。
1.定义三个表:用u_user,权限表u_authority,用户权限表u_role。
CREATE DATABASE initlife
CHARACTER SET utf8
COLLATE 'utf8_general_ci';
CREATE TABLE `u_user` (
`us_name` VARCHAR(50) NOT NULL,
`us_password` VARCHAR(50) NOT NULL,
`us_enabled` TINYINT(1) NULL DEFAULT NULL,
PRIMARY KEY (`us_name`),
UNIQUE INDEX `u_name` (`us_name`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB;
CREATE TABLE `u_authority` (
`au_authority` VARCHAR(50) NOT NULL,
`au_name` VARCHAR(50) NULL DEFAULT NULL,
PRIMARY KEY (`au_authority`),
UNIQUE INDEX `au_authority` (`au_authority`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB;
CREATE TABLE `u_role` (
`ro_usname` VARCHAR(50) NOT NULL,
`ro_auauthority` VARCHAR(50) NULL DEFAULT NULL,
INDEX `FK_u_role_u_user` (`ro_usname`),
INDEX `FK_u_role_u_authority` (`ro_auauthority`),
CONSTRAINT `FK_u_role_u_authority` FOREIGN KEY (`ro_auauthority`) REFERENCES `u_authority` (`au_authority`) ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT `FK_u_role_u_user` FOREIGN KEY (`ro_usname`) REFERENCES `u_user` (`us_name`) ON UPDATE CASCADE ON DELETE CASCADE
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB;
2.添加数据
INSERT INTO `u_authority` (`au_authority`, `au_name`) VALUES
('ROLE_ADMIN', '管理员'),
('ROLE_GUESS', '访客'),
('ROLE_USER', '普通用户');
INSERT INTO `u_role` (`ro_usname`, `ro_auauthority`) VALUES
('user', 'ROLE_USER'),
('user', 'ROLE_GUESS'),
('admin', 'ROLE_ADMIN'),
('admin', 'ROLE_USER'),
('guest', 'ROLE_GUESS');
INSERT INTO `u_user` (`us_name`, `us_password`, `us_enabled`) VALUES
('admin', '21232f297a57a5a743894a0e4a801fc3', 1),
('guest', '084e0343a0486ff05530df6c705c8bb4', 0),
('user', 'ee11cbb19052e40b07aac0ca060c23ee', 1);
3.修改
applicationContext-security.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<!-- 开启测试模式 -->
<!-- 自动配置模式,拦截所有请求进行匹配,有ROLE_USER才可以通过 -->
<http auto-config="true">
<!-- 指定不拦截登录页,* 表示可以带参数。从3.10开始,不再支持filters="none"配置 -->
<intercept-url pattern="/login.jsp*" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<intercept-url pattern="/**" access="ROLE_USER" />
<!-- 指定登录页面及登录失败跳转页 -->
<form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?login_error=1"/>
</http>
<!-- 认证管理器 -->
<authentication-manager>
<authentication-provider>
<password-encoder hash="md5"/>
<jdbc-user-service data-source-ref="dataSource"
users-by-username-query="select u.us_name username ,u.us_password password ,u.us_enabled enabled from u_user u where u.us_name = ? and u.us_enabled = 1"
authorities-by-username-query="select r.ro_usname,r.ro_auauthority from u_role r where r.ro_usname = ?" />
</authentication-provider>
</authentication-manager>
<!-- 指定中文资源 -->
<beans:bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<beans:property name="basename" value="classpath:org/springframework/security/messages_zh_CN"/>
</beans:bean>
</beans:beans>
4.我的web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<!-- 指定spring的配置文件,默认从web根目录寻找配置文件,我们可以通过spring提供的classpath:前缀指定从类路径下寻找 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:applicationContext-*.xml</param-value>
</context-param>
<!-- 对Spring容器进行实例化 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 加载spring Security 过滤器 -->
<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>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
5.我的另外配置文件
applicationContext-common.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="org.gjt.mm.mysql.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/sst?useUnicode=true&characterEncoding=UTF-8"/>
<property name="user" value="root"/>
<property name="password" value="123456"/>
<!--初始化时获取的连接数,取值应在minPoolSize与maxPoolSize之间。Default: 3 -->
<property name="initialPoolSize" value="1"/>
<!--连接池中保留的最小连接数。-->
<property name="minPoolSize" value="1"/>
<!--连接池中保留的最大连接数。Default: 15 -->
<property name="maxPoolSize" value="300"/>
<!--最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 -->
<property name="maxIdleTime" value="60"/>
<!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3 -->
<property name="acquireIncrement" value="5"/>
<!--每60秒检查所有连接池中的空闲连接。Default: 0 -->
<property name="idleConnectionTestPeriod" value="60"/>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
hibernate.hbm2ddl.auto=update
hibernate.show_sql=false
hibernate.format_sql=false
</value>
</property>
</bean>
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
</beans>
6.可以发布测试了。
五.开启页面ss EL表达式
1.修改plicationContext-security.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<!-- 开启测试模式 -->
<!-- 自动配置模式,拦截所有请求进行匹配,有ROLE_USER才可以通过 -->
<http auto-config="true" use-expressions="true" access-denied-page="/403.jsp" >
<!-- 指定不拦截登录页,* 表示可以带参数。从3.10开始,不再支持filters="none"配置 -->
<intercept-url pattern="/login.jsp*" access="permitAll" />
<intercept-url pattern="/user.jsp" access="hasRole('ROLE_USER')"/>
<intercept-url pattern="/admin.jsp" access="hasRole('ROLE_ADMIN')"/>
<intercept-url pattern="/**" access="hasRole('ROLE_USER')"/>
<!-- 指定登录页面及登录失败跳转页 -->
<form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?login_error=1"/>
</http>
<!-- 认证管理器 -->
<authentication-manager>
<authentication-provider>
<password-encoder hash="md5"/>
<jdbc-user-service data-source-ref="dataSource"
users-by-username-query="select u.us_name username ,u.us_password password ,u.us_enabled enabled from u_user u where u.us_name = ? and u.us_enabled = 1"
authorities-by-username-query="select r.ro_usname,r.ro_auauthority from u_role r where r.ro_usname = ?" />
</authentication-provider>
</authentication-manager>
<!-- 指定中文资源 -->
<beans:bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<beans:property name="basename" value="classpath:org/springframework/security/messages_zh_CN"/>
</beans:bean>
</beans:beans>
注:use-expressions="true" 不能与access="ROLE_****"或access="IS_AUTHENTICATED_ANONYMOUSLY"等一起应用,而需要改成如上的格式。
2.index.jsp 红色部分为标签库及表达式
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title><sec:authentication property="name" />的首页!</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
这是首页,欢迎<sec:authentication property="name" /> ! <br />
<p>
<sec:authorize access="hasRole('ROLE_ADMIN')">
You are a administrator! You can therefore see the <a href="./admin.jsp">管理员页</a>.<br/><br/>
</sec:authorize>
</p>
<p>
<sec:authorize access="hasRole('ROLE_USER')">
You are a user! You can therefore see the <a href="./user.jsp">用户页</a>.<br/><br/>
</sec:authorize>
</p>
<p><a href="./">Home</a></p>
<p><a href="./j_spring_security_logout">Logout</a></p>
<sec:authorize ifAllGranted="ROLE_USER" >
<a href="user.jsp">进入user.jsp页面</a><br>
</sec:authorize>
ifAllGranted:只有当前用户拥有所有指定的权限时,才能显示标签体的内容 (相当于“与” 的关系)<br><br>
<sec:authorize ifAnyGranted="ROLE_ADMIN" >
<a href="user.jsp">进入user.jsp页面</a><br>
</sec:authorize>
ifAnyGranted:当前用户拥有指定的权限中的一个的时候,就能显示标签内部内容(相当于“或”的关系)<br><br>
<sec:authorize ifNotGranted="ROLE_ADMIN" >
<a href="user.jsp">进入user.jsp页面</a><br>
</sec:authorize>
ifNotGranted:没有指定的权限的时候,显示标签体的内容 (相当于“非”的关系)<br><br><br>
</body>
</html>
3.可以发布测试了。当不同权限的用户登录时,就有不同的效果。
这是首页,欢迎admin !
You are a administrator! You can therefore see the 管理员页.
You are a user! You can therefore see the 用户页.
进入user.jsp页面ifAllGranted:只有当前用户拥有所有指定的权限时,才能显示标签体的内容 (相当于“与” 的关系)进入user.jsp页面ifAnyGranted:当前用户拥有指定的权限中的一个的时候,就能显示标签内部内容(相当于“或”的关系)ifNotGranted:没有指定的权限的时候,显示标签体的内容 (相当于“非”的关系)
~~~~~~~~~~~~~~~~~~~~我是分隔线~~~~~~~~~~~~~~~~~~~~~~~~~
这是首页,欢迎user !
You are a user! You can therefore see the 用户页.
进入user.jsp页面ifAllGranted:只有当前用户拥有所有指定的权限时,才能显示标签体的内容 (相当于“与” 的关系)ifAnyGranted:当前用户拥有指定的权限中的一个的时候,就能显示标签内部内容(相当于“或”的关系)进入user.jsp页面ifNotGranted:没有指定的权限的时候,显示标签体的内容 (相当于“非”的关系)
六.防同账号多人登陆
1.web.xml增加一个监听
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<!-- 指定spring的配置文件,默认从web根目录寻找配置文件,我们可以通过spring提供的classpath:前缀指定从类路径下寻找 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:applicationContext-*.xml</param-value>
</context-param>
<!-- 对Spring容器进行实例化 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<!-- 这个监听器会在session 创建和销毁的时候通知Spring Security。 -->
<listener-class>
org.springframework.security.web.session.HttpSessionEventPublisher
</listener-class>
</listener>
<!-- 加载spring Security 过滤器 -->
<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>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
2. applicationContext-security.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<!-- 开启测试模式 -->
<!-- 自动配置模式,拦截所有请求进行匹配,有ROLE_USER才可以通过 -->
<http auto-config="true" use-expressions="true" access-denied-page="/403.jsp" >
<!-- 指定不拦截登录页,* 表示可以带参数。从3.10开始,不再支持filters="none"配置 -->
<intercept-url pattern="/login.jsp*" access="permitAll" />
<intercept-url pattern="/timeout.jsp*" access="permitAll" />
<intercept-url pattern="/user.jsp" access="hasRole('ROLE_USER')"/>
<intercept-url pattern="/admin.jsp" access="hasRole('ROLE_ADMIN')"/>
<intercept-url pattern="/**" access="hasRole('ROLE_USER')"/>
<!-- 指定登录页面及登录失败跳转页 -->
<form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?login_error=1"/>
<!-- 指定成功退出系统后跳转页 -->
<logout logout-success-url="/login.jsp" />
<!-- 单用户登录,后登录者无法登录 -->
<session-management >
<concurrency-control max-sessions="1" error-if-maximum-exceeded="true" />
</session-management>
<!--session失效,超时页面。 -->
<session-management invalid-session-url="/timeout.htm" />
</http>
<!-- 认证管理器 -->
<authentication-manager>
<authentication-provider>
<password-encoder hash="md5"/>
<jdbc-user-service data-source-ref="dataSource"
users-by-username-query="select u.us_name username ,u.us_password password ,u.us_enabled enabled from u_user u where u.us_name = ? and u.us_enabled = 1"
authorities-by-username-query="select r.ro_usname,r.ro_auauthority from u_role r where r.ro_usname = ?" />
</authentication-provider>
</authentication-manager>
<!-- 指定中文资源 -->
<beans:bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<beans:property name="basename" value="classpath:org/springframework/security/messages_zh_CN"/>
</beans:bean>
</beans:beans>
3.发布,测成。。。。。