spring security3教程系列--如何踢出使用者
阿新 • • 發佈:2019-01-06
本文章摘編、轉載需要註明來源 http://write.blog.csdn.net/postedit/8572467
對於spring security我個人是比較喜歡的一個安全框架,我們的系統中一般需要提供強制將使用者踢出的功能,這個功能security也有提供,
首先我們要操作需要獲取sessionRegistry中認證使用者的所有SessionInformation,然後逐個呼叫SessionInformation裡的expireNow()方法,然後ConcurrentSessionFilter
就會執行使用者登入登出的功能;對於這個sessionRegistry.removeSessionInformation(sessionInformation
.getSessionId());我不知道是不是我的用法不對,呼叫了也不會強制登出使用者的session,因為官方原始碼中說呼叫removeSessionInformation的時候會讓session的監聽器響應到,但是我是沒成功(望使用這個方式成功的朋友留言告訴下我);下面我們就看為什麼呼叫SessionInformation裡的expireNow()方法就能登出使用者
/** * 把當前使用者踢出系統 */ public void shotOff() { List<SessionInformation> sessionInformations = sessionRegistry .getAllSessions(SpringSecurityManager.getAuthentication() .getPrincipal(), false); for (SessionInformation sessionInformation : sessionInformations) { sessionInformation.expireNow(); // sessionRegistry.removeSessionInformation(sessionInformation .getSessionId()); } }
我們來看下SessionInformation的原始碼
private Date lastRequest; private final Object principal; private final String sessionId; private boolean expired = false; //~ Constructors =================================================================================================== public SessionInformation(Object principal, String sessionId, Date lastRequest) { Assert.notNull(principal, "Principal required"); Assert.hasText(sessionId, "SessionId required"); Assert.notNull(lastRequest, "LastRequest required"); this.principal = principal; this.sessionId = sessionId; this.lastRequest = lastRequest; } //~ Methods ======================================================================================================== public void expireNow() { this.expired = true; } public Date getLastRequest() { return lastRequest; } public Object getPrincipal() { return principal; } public String getSessionId() { return sessionId; } public boolean isExpired() { return expired; } /** * Refreshes the internal lastRequest to the current date and time. */ public void refreshLastRequest() { this.lastRequest = new Date(); }
看到的是呼叫expireNow方法時候會將屬性expired設定為true;
然後我們來看下需要處理的ConcurrentSessionFilter過濾器的原始碼
private SessionRegistry sessionRegistry;
private String expiredUrl;
private LogoutHandler[] handlers = new LogoutHandler[] {new SecurityContextLogoutHandler()};
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
//~ Methods ========================================================================================================
/**
* @deprecated Use constructor which injects the <tt>SessionRegistry</tt>.
*/
public ConcurrentSessionFilter() {
}
public ConcurrentSessionFilter(SessionRegistry sessionRegistry) {
this(sessionRegistry, null);
}
public ConcurrentSessionFilter(SessionRegistry sessionRegistry, String expiredUrl) {
this.sessionRegistry = sessionRegistry;
this.expiredUrl = expiredUrl;
}
@Override
public void afterPropertiesSet() {
Assert.notNull(sessionRegistry, "SessionRegistry required");
Assert.isTrue(expiredUrl == null || UrlUtils.isValidRedirectUrl(expiredUrl),
expiredUrl + " isn't a valid redirect URL");
}
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
HttpSession session = request.getSession(false);
if (session != null) {
SessionInformation info = sessionRegistry.getSessionInformation(session.getId());
if (info != null) {
if (info.isExpired()) {
// Expired - abort processing
doLogout(request, response);
String targetUrl = determineExpiredUrl(request, info);
if (targetUrl != null) {
redirectStrategy.sendRedirect(request, response, targetUrl);
return;
} else {
response.getWriter().print("This session has been expired (possibly due to multiple concurrent " +
"logins being attempted as the same user).");
response.flushBuffer();
}
return;
} else {
// Non-expired - update last request date/time
sessionRegistry.refreshLastRequest(info.getSessionId());
}
}
}
chain.doFilter(request, response);
}
protected String determineExpiredUrl(HttpServletRequest request, SessionInformation info) {
return expiredUrl;
}
private void doLogout(HttpServletRequest request, HttpServletResponse response) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
for (LogoutHandler handler : handlers) {
handler.logout(request, response, auth);
}
}
/**
* @deprecated use constructor injection instead
*/
@Deprecated
public void setExpiredUrl(String expiredUrl) {
this.expiredUrl = expiredUrl;
}
/**
* @deprecated use constructor injection instead
*/
@Deprecated
public void setSessionRegistry(SessionRegistry sessionRegistry) {
this.sessionRegistry = sessionRegistry;
}
public void setLogoutHandlers(LogoutHandler[] handlers) {
Assert.notNull(handlers);
this.handlers = handlers;
}
public void setRedirectStrategy(RedirectStrategy redirectStrategy) {
this.redirectStrategy = redirectStrategy;
}
很明顯程式碼中有遇到if (info.isExpired())邏輯判斷,這個時候如果表示式的值為true就會執行裡面的doLogout方法,而這個方法裡面就會呼叫我們配置的登出監聽器,當前使用者在之前認證成功後的狀態也會失效了
然後看下xml裡怎麼配置
<!-- SESSION管理 -->
<bean id="sessionRegistry"
class="org.springframework.security.core.session.SessionRegistryImpl" />
<bean id="concurrentSessionFilter"
class="org.springframework.security.web.session.ConcurrentSessionFilter">
<property name="sessionRegistry" ref="sessionRegistry" />
<property name="expiredUrl" value="/apply/skip/restimeout.html" />
<property name="logoutHandlers">
<list>
<ref local="logoutHandler" />
</list>
</property>
</bean>
<!-- 登出監聽器 -->
<bean id="logoutHandler"
class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler">
<property name="InvalidateHttpSession" value="true" />
</bean>