Spring专题

Spring security深入使用(3)

上页

记得我Remember-me验证

  这是指网站能够记住会话之间主要的标识。Spring通过发送一个cookie到成功进行身份验证的浏览器,在Cookie中是如下:

  base64(username + “:” + expirationTime + “:” + md5Hex(username + “:” + expirationTime + “:” password + “:” + key));

  当浏览器发出下一个请求到服务器,它也随之发送这个cookie。Spring执行以下操作:
(一)从后端为给定的用户名获取密码
(二)根据uername获取从数据库中pasword,并计算用户名 密码,expirationTime和key的md5Hex,并比较它在cookie的值
(三)如果它们匹配 - 成功登录!如果不匹配,那么你已经提供的伪造Cookie或用户名/密码/密钥中的一个发生了变化。

激活 remember-me 通过http下面的配置:

<sec:http>

<!-- Other filter declarations here -->

<sec:remember-me key="myAppKey"/>

</sec:http>

有一点要注意的是,有一个潜在的安全问题在这里,令牌可以被捕获并可能被滥用,因为它是有效的,直到它过期。使用滚动令牌可避免。这里是如何实现基于令牌的记得我的服务:

<sec:http access-decision-manager-ref="accessDecisionManager">

   <!-- Other filter declarations here -->

   <remember-me services-alias="rememberMeService" data-source-ref="dataSource"/>
   <!-- <remember-me data-source-ref="dataSource" key="pramati"/> -->

</sec:http>

<bean id="tokenRepository" class="org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl">
   <property name="dataSource" ref="dataSource"/>
   <property name="createTableOnStartup" value="true"/>
</bean>

<bean id="rememberMeService" class="org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices">
   <property name="userDetailsService" ref="userDetailsService"/>
   <property name="tokenRepository" ref="tokenRepository"/>
</bean>

( a)在数据库中必须创建新表persistent_logins,因为我们已经指定“ createTableOnStartup '在构造” tokenRepository “时 。下面是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);

( b)我们没有更多的定义我们自己的安全令牌。 Spring会自动生成令牌,并把persistent_tokens表更新。当用户从一个浏览器通过选择“记住我”的选项登录时,创建本表的一条记录,下一次用户从同一个浏览器登录时,用户会被自动记录,并将在数据库中的令牌值更改为一个新的值,但该系列值保持不变。假设现在用户从不同的浏览器转而选择记住我登录时,一个新的记录将为该浏览器创建。当他访问从浏览器访问应用程序时,后续更新会发生。

  因此,使用这种方法的好处是,攻击者将只能使用一个偷来的cookie,一直到受害者用户下一次访问应用程序之前,而不是非动态令牌时需要记住cookie的完整生命周期。当受害者接下来访问网站,他将使用相同的cookie 。这时 Spring会抛出一个CookieTheftException ,它可以用来通知盗窃发生的用户。

下面是为安全链的自定义过滤器:

<sec:custom-filter position="REMEMBER_ME_FILTER" ref="rememberMeFilter" />

<bean id="rememberMeFilter" class="org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter">
  <property name="rememberMeServices" ref="rememberMeServices"/>
  <property name="authenticationManager" ref="theAuthenticationManager" />
</bean>

<bean id="tokenRepository" class="org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl">
   <property name="dataSource" ref="dataSource"/>
   <property name="createTableOnStartup" value="false"/>
</bean>

<bean id="rememberMeServices" class="org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices">
   <property name="userDetailsService" ref="userDetailsService"/>
   <property name="tokenRepository" ref="tokenRepository"/>
</bean>

<bean id="rememberMeAuthenticationProvider" class="org.springframework.security.authentication.rememberme.RememberMeAuthenticationProvider"/>

 

并发会发管理

假设我们不希望用户在应用的同时多个地方登录,下面是我们如何做到这一点:

<sec:http>

  <!-- Other filter declarations here -->

  <sec:session-management session-authentication-error-url="/login.jsp?error=alreadyLoggedin" >
    <sec:concurrency-control max-sessions="1" error-if-maximum-exceeded="true"
              expired-url="/login.jsp?error=alreadyLoggedin"/>
  </sec:session-management>
</sec:http>

或者我们在web.xml中定义监听者,当用户登出以后发出一个事件org.springframework.security.core.session.SessionDestroyedEvent:

<listener>
<listener-class>
org.springframework.security.web.session.HttpSessionEventPublisher
</listener-class>
</listener>

Spring使用类似于我们一直在讨论的安全过滤器。除此之外,还采用ApplicationEvents。当Spring看到所需要的并发控制,它可以维持与主要关联会话列表。Map结构看起来像(在org.springframework.security.core.session.SessionRegistryImpl实际定义):

ConcurrentMap<Object,Set<String>> principals =
new ConcurrentHashMap<Object,Set<String>>();

这里映射的键是用户对象,值设置为与它相关联的会话ID。所以,当这个数据集的大小超过在<concurrency-control>元素中定义MAX-会话的值将引发异常。当Spring看到定义的并发控制元件,SessionRegistryImpl(Map的定义地方)将其内部ConcurrentSessionControlStrategy注入UsernamePasswordAuthenticationFilter。当用户认证成功,Spring放入Map一条记录。

当用户注销时,在web.xml中定义的侦听器将发出SessionDestroyedEvent,SessionRegistryImpl侦听此事件,并从维持map中删除会话ID条目。没有删除掉,用户将永远无法重新登录,即使他们登出另一个会话或会话超时。因此,这里是<concurrency-control>等效的配置:

<sec:http>
  <sec:custom-filter position="CONCURRENT_SESSION_FILTER" ref="concurrencyFilter" />
  <sec:custom-filter position="FORM_LOGIN_FILTER" ref="myAuthFilter" />

  <!-- Other filter declarations here -->

  <sec:session-management session-authentication-strategy-ref="sessionAuthenticationStrategy"/>
</sec:http>

<bean id="concurrencyFilter" class="org.springframework.security.web.session.ConcurrentSessionFilter">
 <property name="sessionRegistry" ref="sessionRegistry" />
 <property name="expiredUrl" value="/session-expired.htm" />
</bean>

<bean id="myAuthFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
  <property name="sessionAuthenticationStrategy" ref="sessionAuthenticationStrategy" />
  <property name="authenticationManager" ref="authenticationManager" />
</bean>

<bean id="sessionAuthenticationStrategy" class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy">
  <constructor-arg name="sessionRegistry" ref="sessionRegistry" />
  <property name="maximumSessions" value="1" />
</bean>

<bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl" />

 

 

.基于容器的用户安全认证授权系统

权限专题

spring security安全登陆源码下载