http://it.kswchina.com/java/zd/410273_2.html
spring security的jar你需要这几个:
spring-security-acl-2.0.4.jar
spring-security-core-2.0.4.jar
spring-security-core-tiger-2.0.4.jar
spring-security-taglibs-2.0.4.jar
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:security="http://www.springframework.org/schema/security"
- 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-2.0.2.xsd
- http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.2.xsd">
- ...
- </beans>
1.Spring Security已经成为Spring Framework下最成熟的安全系统,它为我们提供了强大而灵活的企业级安全服务,如:
Ø 认证授权机制
Ø Web资源访问控制
Ø 业务方法调用访问控制
Ø 领域对象访问控制Access Control List(ACL)
Ø 单点登录(Central Authentication Service)
Ø X509认证
Ø 信道安全(Channel Security)管理等功能
当保护Web资源时,Spring Security使用Servlet 过滤器来拦截Http请求进行身份验证并强制安全性,以确保WEB资源被安全的访问。如下图是Spring Security的主要组件图(摘自《Spring in Action》):
2.保护Web资源
3.开始web:
(1) web.xml:
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>
org.springframework.security.util.FilterToBeanProxy
</filter-class>
<init-param>
<param-name>targetClass</param-name>
<param-value>
org.springframework.security.util.FilterChainProxy
</param-value>
</init-param>
</filter>
<!-- /j_spring_security_check是Spring Security默认的进行表单验证的过滤地址,你也可以修改为别的名称,但是需要和
applicationContext-security.xml中相对应 -->
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/j_spring_security_check</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
(2)配置applicationContext-security.xml :
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON 定义URL在匹配之前必须先转为小写,PATTERN_TYPE_APACHE_ANT 定义了使用Apache ant的匹配模式,/**定义的将等号后面的过滤器应用在那些URL上,这里使用全部URL过滤,每个过滤器之间都适用逗号分隔,它们按照一定的顺序排列。
提示:
特别需要注意的是,即使你配置了系统提供的所有过滤器,这个过滤器链会很长,但是千万不要使用换行,否则它们不会正常工作,
容器甚至不能正常启动。
<bean id="filterChainProxy"
class="org.springframework.security.util.FilterChainProxy">
<property name="filterInvocationDefinitionSource">
<value><![CDATA[
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/**=httpSessionContextIntegrationFilter,logoutFilter,
authenticationProcessingFilter,securityContextHolderAwareRequestFilter,
rememberMeProcessingFilter,anonymousProcessingFilter,exceptionTranslationFilter,
filterSecurityInterceptor
]]></value>
</property>
</bean>
各个过滤器的执行顺序如以上配置。
<bean id="httpSessionContextIntegrationFilter"
class="org.springframework.security.context.HttpSessionContextIntegrationFilter"/>
下面的代码是从安全上下文的获取Authentication对象的方法:
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
logoutFilter(退出过滤器) ,退出登录操作:
<bean id="logoutFilter"
class="org.springframework.security.ui.logout.LogoutFilter">
<constructor-arg value="/index.jsp"/>
<constructor-arg>
<list>
<!-- 实现了LogoutHandler接口(logout方法) -->
<ref bean="rememberMeServices"/>
<bean class="org.springframework.security.ui.logout.SecurityContextLogoutHandler"/>
</list>
</constructor-arg>
</bean>
LogoutFilter的构造函数需要两个参数,第一个是退出系统后系统跳转到的URL,第二个是一个LogoutHandler类型的数组,这个数组里的对象都实现了LogoutHandler接口,并实现了它的logout方法,用户在发送退出请求后,会一次执行LogoutHandler数组的对象并调用它们的 logout方法进行一些后续的清理操作,主要是从SecurityContextHolder对象中清楚所有用户的认证信息(Authentication对象),将用户的会话对象设为无效,这些都时由SecurityContextLogoutHandler来完成。 LogoutFilter还会清除Cookie记录,它由另外一个Bean来完成(RememberMeServices)。
<bean id="rememberMeServices"
class="org.springframework.security.ui.rememberme.TokenBasedRememberMeServices"
p:key="springsecurity"
p:userDetailsService-ref="userDetailsService"/>
TokenBasedRememberMeServices继承自系统的AbstractRememberMeServices抽象类(实现了 RememberMeServices和 LogoutHandler两个接口), RememberMeServices接口的loginSuccess方法负责在用户成功登录之后将用户的认证信息存入Cookie中,这个类在后续的过滤器执行过程中也会被用到。
另一个userDetailsService属性也是指向了我们配置的Bean, 它负责从数据库中读取用户的信息.
过滤器链的下个配置的过滤器是authenticationProcessingFilter(认证过程过滤器),我们使用它来处理表单认证,当接受到与filterProcessesUrl所定义相同的请求时它开始工作:
<bean id="authenticationProcessingFilter"
class="org.springframework.security.ui.webapp.AuthenticationProcessingFilter"
p:authenticationManager-ref="authenticationManager"
p:authenticationFailureUrl="/login.jsp?login_error=1"
p:defaultTargetUrl="/default.jsp"
p:filterProcessesUrl="/j_spring_security_check"
p:rememberMeServices-ref="rememberMeServices"/>
下面列出了认证过程过滤器配置中各个属性的功能:
1.authenticationManager 认证管理器
2.authenticationFailureUrl 定义登录失败时转向的页面
3.defaultTargetUrl 定义登录成功时转向的页面
4.filterProcessesUrl 定义登录请求的地址(在web.xml中配置过)
5.rememberMeServices 在验证成功后添加cookie信息
这里也用到了rememberMeServices,如果用户认证成功,将调用RememberMeServices的loginSuccess方法将用户认证信息写入Cookie中,这里也可以看到使用IoC的好处。
决定用户是否有权限访问受保护资源的第一步就是要确定用户的身份,最常用的方式就是用户提供一个用户名和密码以确认用户的身份是否合法,这一步就是由认证过程过滤器调用authenticationManager(认证管理器)来完成的。 org.springframework.security.AuthenticationManager接口定义了一个authenticate方法,它使用Authentication作为入口参数(只包含用户名和密码),并在验证成功后返回一个完整的Authentication对象(包含用户的权限信息GrantedAuthority数组对象),authenticationProcessingFilter(认证过程过滤器)会将这个完整的 Authentication对象存入SecurityContext中,如果认证失败会抛出一个AuthenticationException并跳转到authenticationFailureUrl 定义的URL.认证管理其配置如下:
<bean id="authenticationManager"
class="org.springframework.security.providers.ProviderManager"
p:sessionController-ref="concurrentSessionController">
<property name="providers">
<list>
<ref bean="daoAuthenticationProvider"/>
<bean
class="org.springframework.security.providers.anonymous.AnonymousAuthenticationProvider"
p:key="springsecurity"/>
<bean
class="org.springframework.security.providers.rememberme.RememberMeAuthenticationProvider"
p:key="springsecurity"/>
</list>
</property>
</bean>
providers属性定义了提供者管理器的集合,ProviderManager(提供者管理器)逐一遍历这个认证提供者的集合并调用提供者的 authenticate方法,如果一个提供者认证失败会尝试另外一个提供者直到某一个认证提供者能够成功的验证该用户的身份,以保证获取不同来源的身份认证.
提 供 者 | 作 用 |
DaoAuthenticationProvider | 从数据库中读取用户信息验证身份 |
AnonymousAuthenticationProvider | 匿名用户身份认证 |
RememberMeAuthenticationProvider | 已存cookie中的用户信息身份认证 |
AuthByAdapterProvider | 使用容器的适配器验证身份 |
CasAuthenticationProvider | 根据Yale中心认证服务验证身份, 用于实现单点登陆 |
JaasAuthenticationProvider | 从JASS登陆配置中获取用户信息验证身份 |
RemoteAuthenticationProvider | 根据远程服务验证用户身份 |
RunAsImplAuthenticationProvider | 对身份已被管理器替换的用户进行验证 |
X509AuthenticationProvider | 从X509认证中获取用户信息验证身份 |
TestingAuthenticationProvider | 单元测试时使用 |
作为程序员,我们可以根据需要选择不同的AuthenticationProvider(认证提供者)来对自己的系统提供认证服务。
这里我们着重介绍DaoAuthenticationProvider,它从数据库中读取用户信息验证身份,配置如下:
class ="org.springframework.security.providers.dao.DaoAuthenticationProvider"
p:passwordEncoder-ref ="passwordEncoder"
p:userDetailsService-ref ="userDetailsService" />
< bean id ="passwordEncoder"
class ="org.springframework.security.providers.encoding.Md5PasswordEncoder" />
String getUsername():获取用户名;
String getPassword():获取密码;
boolean isAccountNonExpired():用户帐号是否过期;
boolean isAccountNonLocked():用户帐号是否锁定;
boolean isCredentialsNonExpired():用户的凭证是否过期;
boolean isEnabled():用户是否处于激活状态。
当以上任何一个判断用户状态的方法都返回false时,用户凭证就被视为无效。UserDetails接口还定义了获取用户权限信息的 getAuthorities()方法,该方法返回一个GrantedAuthority[]数组对象,GrantedAuthority是用户权限信息对象,这个对象中定义了一个获取用户权限描述信息的getAuthority()方法。
这个封装了用户详细信息的UserDetails该从哪儿获取呢?这就是我们接下来要介绍的UserDetailsService接口,这个接口中只定义了唯一的UserDetails loadUserByUsername(String username)方法,它通过用户名来获取整个UserDetails对象。
Spring Security要做的就是将这个未认证的Authentication对象和UserDetails进行匹配,成功后将UserDetails中的用户权限信息拷贝到Authentication中组成一个完整的Authentication对象,共其它组件共享。
这样,我们就可以在系统中获取用户的相关信息了,需要使用到Authentication对象定义的Object getPrincipal()方法,这个方法返回一个Object类型的对象,通常可以将它转换为UserDetails,从而可以获取用户名,密码以及权限等信息。代码如下:
UserDetails details = (UserDetails)authentication.getPrincipal();
GrantedAuthority[] authority = details.getAuthorities();
前面介绍了DaoAuthenticationProvider,它可以从数据库中读取用户信息,同样也可以从一个用户属性文件中读取.当然还会涉及到更深入的东西,比如根据自己系统的需要自定义UserDetails和 UserDetailsService,这个只是让你对整个系统有个简单的了解,所以我们使用用户属性文件(users.properties)来存储用户信息:
admin=admin,ROLE_SUPERVISOR
user1=user1,ROLE_USER
user2=user2,ROLE_USER
user3=user3,disabled,ROLE_USER
<bean id="userDetailsService"
class="org.springframework.security.userdetails.memory.InMemoryDaoImpl">
<property name="userProperties">
<bean class="org.springframework.beans.factory.config.PropertiesFactoryBean"
p:location="/WEB-INF/users.properties"/>
</property>
</bean>
InMemoryDaoImpl类是UserDetailsService接口的一个实现,它从属性文件里读取用户信息,Spring Security使用一个属性编辑器将用户信息为我们组织成一个 org.springframework.security.userdetails.memory.UserMap类的对象,我们也可以直接为它提供一个用户权限信息的列表,详见applicationContext-security.xml配置文件。
UserMap字符串的每一行都用键值对的形式表示,前面是用户名,然后是等号,后面是赋予该用户的密码/权限等信息,它们使用逗号隔开。比如:
PlaintextPasswordEncoder(默认)——不对密码进行编码,直接返回未经改变的密码;
Md4PasswordEncoder ——对密码进行消息摘要(MD4)编码;
Md5PasswordEncoder ——对密码进行消息摘要(MD5)编码;
ShaPasswordEncoder ——对密码进行安全哈希算法(SHA)编码。
在AuthenticationManager(认证管理器)中还配置了一个名为 sessionController的Bean,这个Bean可以阻止用户在进行了一次成功登录以后在进行一次成功的登录。在 applicationContext-security.xml配置文件添加sessionController的配置:
<bean id="concurrentSessionController"
class="org.springframework.security.concurrent.ConcurrentSessionControllerImpl"
p:maximumSessions="1"
p:exceptionIfMaximumExceeded="true"
p:sessionRegistry-ref="sessionRegistry"/>
<bean id="sessionRegistry"
class="org.springframework.security.concurrent.SessionRegistryImpl"/>
</beans>
maximumSessions属性配置了只允许同一个用户登录系统一次,exceptionIfMaximumExceeded属性配置了在进行第二次登录是是否让第一次登录失效。这里设置为true不允许第二次登录。要让此功能生效,我们还需要在web.xml文件中添加一个监听器,以让Spring Security能获取Session的生命周期事件,配置如下:
<listener>
<listener-class>
org.springframework.security.ui.session.HttpSessionEventPublisher
</listener-class>
</listener>
过滤器链的下一个过滤器securityContextHolderAwareRequestFilter:
<bean id="securityContextHolderAwareRequestFilter"
class="org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter"/>
rememberMeProcessingFilter过滤器配置:
<bean id="rememberMeProcessingFilter"
class="org.springframework.security.ui.rememberme.RememberMeProcessingFilter"
p:authenticationManager-ref="authenticationManager"
p:rememberMeServices-ref="rememberMeServices"/>
当SecurityContextHolder中不存在Authentication用户授权信息时,rememberMeProcessingFilter就会调用rememberMeServices 的autoLogin()方法从cookie中获取用户信息自动登录。
anonymousProcessingFilter过滤器配置:
<bean id="anonymousProcessingFilter"
class="org.springframework.security.providers.anonymous.AnonymousProcessingFilter"
p:key="springsecurity"
p:userAttribute="anonymousUser,ROLE_ANONYMOUS"/>
exceptionTranslationFilter(异常处理过滤器),该过滤器用来处理在系统认证授权过程中抛出的异常,主要是处理 AccessDeniedException和AuthenticationException两个异常并根据配置跳转到不同URL:
<bean id="exceptionTranslationFilter"
class="org.springframework.security.ui.ExceptionTranslationFilter"
p:accessDeniedHandler-ref="accessDeniedHandler"
p:authenticationEntryPoint-ref="authenticationEntryPoint"/>
<!-- 处理AccessDeniedException -->
<bean id="accessDeniedHandler"
class="org.springframework.security.ui.AccessDeniedHandlerImpl"
p:errorPage="/accessDenied.jsp"/>
<bean id="authenticationEntryPoint"
class="org.springframework.security.ui.webapp.AuthenticationProcessingFilterEntryPoint"
p:loginFormUrl="/login.jsp"
p:forceHttps="false"/>
这里我们使用AuthenticationProcessingFilterEntryPoint认证入口点,提供给用户一个友好的登录界面,只是为了给用户更好的体验。
filterSecurityInterceptor(过滤器安全拦截器),该过滤器首先调用认证管理器来判断用户是否已被成功验证,如果没有被验证则重定向到登录界面。否则,从Authentication获取用户的权限信息,然后从objectDefinitionSource中获取URL所对应的权限,最后调用accessDecisionManager(访问决策管理器)来判断用户当前拥有的权限是否与当前受保护的URL资源对应的权限匹配,如果匹配就可以访问该URL资源,否则将抛出AccessDeniedException异常并返回客户端浏览器一个403错误.
过滤器安全拦截器的配置如下:
<bean id="filterSecurityInterceptor"
class="org.springframework.security.intercept.web.FilterSecurityInterceptor"
p:authenticationManager-ref="authenticationManager"
p:accessDecisionManager-ref="accessDecisionManager">
<property name="objectDefinitionSource">
<value><![CDATA[
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/admins/**=ROLE_SUPERVISOR
/user/**=ROLE_USER,IS_AUTHENTICATED_REMEMBERED
/default.jsp=ROLE_USER,IS_AUTHENTICATED_REMEMBERED
/**=IS_AUTHENTICATED_ANONYMOUSLY
]]></value>
</property>
</bean>
从配置可以看出来,过滤器安全拦截器用到了我们前面配置的认证管理器,过滤器安全拦截器使用authenticationManager并调用它的 providers(提供者列表)来对用户的身份进行验证并获取用户拥有的权限。如果用户被成功认证,过滤器安全拦截器将会使用 accessDecisionManager(访问决策管理器)来判断已认证的用户是否有权限访问受保护的资源,这些受保护的资源由 objectDefinitionSource属性定义。
访问决策管理器(accessDecisionManager):
<bean id="accessDecisionManager"
class="org.springframework.security.vote.AffirmativeBased"
p:allowIfAllAbstainDecisions="false">
<property name="decisionVoters">
<list>
<bean class="org.springframework.security.vote.RoleVoter"/>
<bean class="org.springframework.security.vote.AuthenticatedVoter"/>
</list>
</property>
</bean>