在最近的一個項目中需要用到同時只能允許一個用戶在線,該系統是一個線上考試系統,要求同一個考生在不同的瀏覽器上同時只能有一個在線。框架用的Spring Security,Spring Security本身時有關於Session控制的,只需要在配置文件中進行配置就可以實現。
第一步需要在web.xml裏配置HttpSessionEventPublisher,如下所示,只要在web.xml文件里加上這段配置就可以
<listener>
<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>
第二部需要配置Spring Security的配置文件,我這裏的文件名字是security.xml,這個名字可能會不一樣,這要看各自的具體命名。
如果只是簡單的應用Spring Security,只需要在配置文件中加上下面這段配置就可以了
<security:session-management
invalid-session-url="/user-login-page"
session-authentication-error-url="/user-login-page">
<security:concurrency-control
max-sessions="1" expired-url="/home" error-if-maximum-exceeded="false" />
</security:session-management>
其中invalid-session-url是配置會話失效轉向地址;max-sessions是設置單個用戶最大並行會話數;error-if-maximum-exceeded是配置當用戶登錄數達到最大時是否報錯,設置爲true時會報錯且後登錄的會話不能登錄,默認爲false不報錯且將前一會話置爲失效。
配置完後使用不同瀏覽器登錄系統,就可以看到同一用戶後來的會話不能登錄或將已登錄會話踢掉。
但是如果spring security的一段<http/>中使用了自定義過濾器<custom-filter/>(特別是FORM_LOGIN_FILTER),或者配置了AuthenticationEntryPoint,或者使用了自定義的UserDetails、AccessDecisionManager、AbstractSecurityInterceptor、FilterInvocationSecurityMetadataSource、UsernamePasswordAuthenticationFilter等,上面的簡單配置可能就不會生效了,就需要自行配置ConcurrentSessionFilter、ConcurrentSessionControlStrategy和SessionRegistry,雖然配置內容和缺省一致。配置如下:
<bean id="concurrencyFilter"
class="org.springframework.security.web.session.ConcurrentSessionFilter">
<property name="sessionRegistry" ref="sessionRegistry" />
<property name="expiredUrl" value="/user-login-page?result=failed" />
</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">
</bean>
此外還需要在<security:http中加上下面的配置
<security:custom-filter after="CONCURRENT_SESSION_FILTER" ref="concurrencyFilter" />
這個地方之所以用after="CONCURRENT_SESSION_FILTER",是因爲我的配置文件裏已經有了一個custom-filter,爲了區分先後順序,所以把原來的custom-filter改爲<security:custom-filter before="FORM_LOGIN_FILTER" ref="securityFilter" />。
基本上經過上述步驟以後啓動項目,分別打開瀏覽器然後進行的登錄就可以看到效果了。
網上有的文章還說如果自己有自定義的UserDetail的話還需要在自己的類裏面加上
@Override
public boolean equals(Object rhs) {
if (rhs instanceof User) {
return username.equals(((User) rhs).username);
}
return false;
}
/**
* Returns the hashcode of the {@code username}.
*/
@Override
public int hashCode() {
return username.hashCode();
}
這兩個方法,雖然我這裏也自定義了UserDetail類,但是並沒有添加這兩個方法,也可以正常實現效果。