在之前的教程四中一笔带过式的讲了下RememberMe记住密码的功能,那篇的Remember功能是最简易的配置,其功能和安全性都不强。这里就配置下security中RememberMe的各种方式。本人也是在学习中,若有错误,欢迎指正,一起学习。
一 概述
二 基于简单加密的方式
<beans:bean id="userDetailsService"
class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
<beans:property name="usersByUsernameQuery"
value="select username,password,status as enabled from user where username = ?" />
<beans:property name="authoritiesByUsernameQuery"
value="select user.username,role.name from user,role,user_role
where user.id=user_role.user_id and
user_role.role_id=role.id and user.username=?" />
<beans:property name="dataSource" ref="dataSource" />
</beans:bean>
说明:dataSource就是连接数据库的数据源;usersByUsernameQuery就是配置
jdbc-user-service时候的users-by-username-query,这个是根据用户名来查询用户的sql语句;同理authoritiesByUsernameQuery就是对应的authorities-by-username-query,这个用来根据用户名查询对应的权限。
<!-- Remember-Me 对应的 Filter -->
<beans:bean id="rememberMeFilter"
class="org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter">
<beans:property name="rememberMeServices" ref="rememberMeServices" />
<beans:property name="authenticationManager" ref="authenticationManager" />
</beans:bean>
这个过滤器仅仅这样配置是不会起作用的,还要把它加入的Security的FilterChain中去,用<custom-filter ref="rememberMeFilter" position="REMEMBER_ME_FILTER"/>即可,另外这个过滤器要提供一个rememberMeServices和一个用户认证的authenticationManager,后面这个其实就是authentication-manager所配置的东西,而前面这个需要另外配置,配置方式如下:
<bean id="rememberMeServices"
class="org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
<property name="userDetailsService" ref="userDetailsService" />
<property name="key" value="zmc" />
<!-- 指定 request 中包含的用户是否选择了记住我的参数名 -->
<property name="parameter" value="rememberMe"/>
</bean>
这个配置中的userDetailsService就是上面配置的,直接引用上面的即可;key就是token中的key,这个key可以用来方式token被修改,另外这个key要和后面配置的rememberMeAuthenticationProvider的key要一样;parameter就是登陆界面的点击记住密码的checkbox的name值,这个一定要一直,要不然没有效果的。
<bean id="rememberMeAuthenticationProvider"
class="org.springframework.security.authentication.RememberMeAuthenticationProvider">
<property name="key" value="zmc" />
</bean>
同时还要将其添加到authentication-manager标签中去
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="userDetailsService">
</authentication-provider>
<!-- 记住密码 -->
<authentication-provider ref="rememberMeAuthenticationProvider"></authentication-provider>
</authentication-manager>
最后面将rememberMeServices添加到myUsernamePasswordAuthenticationFilter中去。
<?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/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<http pattern="/login.jsp" security="none"></http>
<http auto-config="false" entry-point-ref="loginUrlAuthenticationEntryPoint">
<!-- <form-login login-page="/login.jsp" default-target-url="/index.jsp"
authentication-failure-url="/login.jsp?error=true" /> -->
<logout invalidate-session="true" logout-success-url="/login.jsp"
logout-url="/j_spring_security_logout" />
<custom-filter ref="myUsernamePasswordAuthenticationFilter"
position="FORM_LOGIN_FILTER" />
<!--替换默认REMEMBER_ME_FILTER-->
<custom-filter ref="rememberMeFilter" position="REMEMBER_ME_FILTER"/>
<!-- 通过配置custom-filter来增加过滤器,before="FILTER_SECURITY_INTERCEPTOR"表示在SpringSecurity默认的过滤器之前执行。 -->
<custom-filter ref="filterSecurityInterceptor" before="FILTER_SECURITY_INTERCEPTOR" />
</http>
<beans:bean id="loginUrlAuthenticationEntryPoint"
class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<beans:property name="loginFormUrl" value="/login.jsp" />
</beans:bean>
<!-- 数据源 -->
<beans:bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<!-- 此为c3p0在spring中直接配置datasource c3p0是一个开源的JDBC连接池 -->
<beans:property name="driverClass" value="com.mysql.jdbc.Driver" />
<beans:property name="jdbcUrl"
value="jdbc:mysql://localhost:3306/springsecuritydemo?useUnicode=true&characterEncoding=UTF-8" />
<beans:property name="user" value="root" />
<beans:property name="password" value="" />
<beans:property name="maxPoolSize" value="50"></beans:property>
<beans:property name="minPoolSize" value="10"></beans:property>
<beans:property name="initialPoolSize" value="10"></beans:property>
<beans:property name="maxIdleTime" value="25000"></beans:property>
<beans:property name="acquireIncrement" value="1"></beans:property>
<beans:property name="acquireRetryAttempts" value="30"></beans:property>
<beans:property name="acquireRetryDelay" value="1000"></beans:property>
<beans:property name="testConnectionOnCheckin" value="true"></beans:property>
<beans:property name="idleConnectionTestPeriod" value="18000"></beans:property>
<beans:property name="checkoutTimeout" value="5000"></beans:property>
<beans:property name="automaticTestTable" value="t_c3p0"></beans:property>
</beans:bean>
<beans:bean id="builder" class="com.zmc.demo.JdbcRequestMapBulider">
<beans:property name="dataSource" ref="dataSource" />
<beans:property name="resourceQuery"
value="select re.res_string,r.name from role r,resc re,resc_role rr where
r.id=rr.role_id and re.id=rr.resc_id" />
</beans:bean>
<beans:bean id="myUsernamePasswordAuthenticationFilter"
class="com.zmc.demo.MyUsernamePasswordAuthenticationFilter
">
<beans:property name="filterProcessesUrl" value="/j_spring_security_check" />
<beans:property name="authenticationManager" ref="authenticationManager" />
<beans:property name="authenticationSuccessHandler"
ref="loginLogAuthenticationSuccessHandler" />
<beans:property name="authenticationFailureHandler"
ref="simpleUrlAuthenticationFailureHandler" />
<beans:property name="rememberMeServices" ref="rememberMeServices" />
</beans:bean>
<beans:bean id="loginLogAuthenticationSuccessHandler"
class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<beans:property name="targetUrlParameter" value="/index.jsp" />
</beans:bean>
<beans:bean id="simpleUrlAuthenticationFailureHandler"
class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<beans:property name="defaultFailureUrl" value="/login.jsp" />
</beans:bean>
<!-- 认证过滤器 -->
<beans:bean id="filterSecurityInterceptor"
class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<!-- 用户拥有的权限 -->
<beans:property name="accessDecisionManager" ref="accessDecisionManager" />
<!-- 用户是否拥有所请求资源的权限 -->
<beans:property name="authenticationManager" ref="authenticationManager" />
<!-- 资源与权限对应关系 -->
<beans:property name="securityMetadataSource" ref="securityMetadataSource" />
</beans:bean>
<!-- acl领域模型 -->
<beans:bean class="com.zmc.demo.MyAccessDecisionManager" id="accessDecisionManager">
</beans:bean>
<!-- -->
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="userDetailsService
</authentication-provider>
<!-- 记住密码 -->
<authentication-provider ref="rememberMeAuthenticationProvider"></authentication-provider>
</authentication-manager>
<!-- 配置userDetailsService -->
<beans:bean id="userDetailsService"
class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
<beans:property name="usersByUsernameQuery"
value="select username,password,status as enabled from user where username = ?" />
<beans:property name="authoritiesByUsernameQuery"
value="select user.username,role.name from user,role,user_role
where user.id=user_role.user_id and
user_role.role_id=role.id and user.username=?" />
<beans:property name="dataSource" ref="dataSource" />
</beans:bean>
<!-- Remember-Me 对应的 Filter -->
<beans:bean id="rememberMeFilter"
class="org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter">
<beans:property name="rememberMeServices" ref="rememberMeServices" />
<beans:property name="authenticationManager" ref="authenticationManager" />
</beans:bean>
<!-- rememberService -->
<!-- RememberMeServices 的实现 -->
<beans:bean id="rememberMeServices"
class="org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
<beans:property name="userDetailsService" ref="userDetailsService" />
<beans:property name="key" value="zmc" />
<!-- 指定 request 中包含的用户是否选择了记住我的参数名 -->
<beans:property name="parameter" value="rememberMe" />
</beans:property>
</beans:bean>
<!-- 记住密码 -->
<!-- key 值需与对应的 RememberMeServices 保持一致 -->
<beans:bean id="rememberMeAuthenticationProvider"
class="org.springframework.security.authentication.RememberMeAuthenticationProvider">
<beans:property name="key" value="zmc" />
</beans:bean>
<beans:bean id="securityMetadataSource"
class="com.zmc.demo.MyFilterInvocationSecurityMetadataSource">
<beans:property name="builder" ref="builder"></beans:property>
</beans:bean>
</beans:beans>
因为这个例子是在之前的例子上进行修改的,所以其它的没讲到的一些配置在之前博客都有详细的讲解,请参考之前的博客。
二 基于持久化的方式配置
在将这配置之前先对上面rememberService中的实现类TokenBasedRememberMeServices进行简单的讲解,该类主要是基于简单加密 token 的一个实现类。TokenBasedRememberMeServices 会在用户选择了记住我成功登录后,生成一个包含 token 信息的 cookie 发送到客户端;如果用户登录失败则会删除客户端保存的实现 Remember-Me 的 cookie。需要自动登录时,它会判断 cookie 中所包含的关于 Remember-Me 的信息是否与系统一致,一致则返回一个 RememberMeAuthenticationToken 供 RememberMeAuthenticationProvider 处理,不一致则会删除客户端的 Remember-Me cookie。TokenBasedRememberMeServices 还实现了 Spring Security 的 LogoutHandler 接口,所以它可以在用户退出登录时立即清除 Remember-Me cookie。
而基础持久化方式配置的实质就是这个类不同,基于持久化方式配置的所用的实现类为:PersistentTokenBasedRememberMeServices,一看名字就知道其作用,就是将token进行持久化保存起来,要保存数据相应的就是为其制定保存的地方,这个保存的地方就是用PersistentTokenRepository来指定的,Spring Security 对此有两种实现,InMemoryTokenRepositoryImpl 和 JdbcTokenRepositoryImpl。前者是将 token 存放在内存中的,通常用于测试,而后者是将 token 存放在数据库中。PersistentTokenBasedRememberMeServices 默认使用的是前者,我们可以通过其 tokenRepository 属性来指定使用的 PersistentTokenRepository。这例子用JdbcTokenRepositoryImpl来进行持久化保存,显然要往数据库保存数据,肯定要有一张表,这个表security也有提供,sql语句为:create table persistent_logins (username varchar(64) not null, series varchar(64) primary key, token varchar(64) not null, last_used timestamp not null)。在数据库中创建该表即可。
创建后的表为:
所以持久化方式配置只需要将第一种方式的TokenBasedRememberMeServices进行修改就可以,用以下代码提换就可以了:
<beans:bean id="rememberMeServices"
class="org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices">
<beans:property name="userDetailsService" ref="userDetailsService" />
<beans:property name="key" value="zmc" />
<!-- 指定 request 中包含的用户是否选择了记住我的参数名 -->
<beans:property name="parameter" value="rememberMe" />
<beans:property name="tokenRepository">
<beans:bean class="org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl">
<!-- 数据源 -->
<beans:property name="dataSource" ref="dataSource"/>
<!-- 是否在启动时创建持久化 token 的数据库表 若为true,但数据有这个表时,会启动失败,提示表已存在 -->
<beans:property name="createTableOnStartup" value="false"/>
</beans:bean>
</beans:property>
</beans:bean>