Spring Security为我们提供了SessionAuthenticationStrategy接口来定制针对Session的一些特殊管理,如防Session的固定攻击, 防Session的单用户多次登陆等, 这些特殊的管理功能Spring Security都为我们提供了相应的类,如下示:
1. SessionFixationProtectionStrategy, 这个实现SessionAuthenticationStrategy的类是用于防止Session的固定攻击的
这里先从Session的固定攻击说起, 话说什么是Session的固定攻击呢, 当我们尝试登录时, Spring Security会为我们创建一个Session, 并将SessionId放置在Cookie或URL中, 如果没有使用Spring Security的防Session固定攻击的功能的话, 当用户登录成功后, 这个SessionId是不会改变的, 如果在登录前这个SessionId就因为某种原因而被窃取了,那就会让别有用心者在不知道用户账户的情况下, 通过使用SessionId来达到查看需用户登录的资源。 那么Spring Security又是如何来解决这个问题的呢, 办法很简单, 在用户登录成功后重新创建一个Session, 并将旧有Session的信息转移到新建的Session中, 当然这是可选的, 至于你想不想转移这些信息或者转移哪些信息这些都是可以选择的, 可由用户自主选择。
源码解释
//是否需要转移Session中的值
private boolean migrateSessionAttributes = true;
//需要转移的Session中的值的列表
private List<String> retainedAttributes = null;
//是否创建新的Session
private boolean alwaysCreateSession;
//该方法是实现Session管理的主方法, 也就是SessionAuthenticationStrategy接口中的方法
public void onAuthentication(Authentication authentication, HttpServletRequest request, HttpServletResponse response) {
boolean hadSessionAlready = request.getSession(false) != null;
//如果没有Session存在则返回不做处理
if (!hadSessionAlready && !alwaysCreateSession) {
return;
}
// 创建一个Session
HttpSession session = request.getSession();
if (hadSessionAlready && request.isRequestedSessionIdValid()) {
// We need to migrate to a new session
String originalSessionId = session.getId();
if (logger.isDebugEnabled()) {
logger.debug("Invalidating session with Id '" + originalSessionId +"' " + (migrateSessionAttributes ?
"and" : "without") + " migrating attributes.");
}
Map<String, Object> attributesToMigrate = extractAttributes(session);
session.invalidate();
session = request.getSession(true); // we now have a new session
if (logger.isDebugEnabled()) {
logger.debug("Started new session: " + session.getId());
}
if (originalSessionId.equals(session.getId())) {
logger.warn("Your servlet container did not change the session ID when a new session was created. You will" +
" not be adequately protected against session-fixation attacks");
}
//转移Session中的属性值
transferAttributes(attributesToMigrate, session);
//Session转移后需要做的额外工作, 当前类中的方法为空方法,可由用户自主实现
onSessionChange(originalSessionId, session, authentication);
}
}
2. ConcurrentSessionControlStrategy这个类是用于防止Session的单用户多次登陆的, 值得一提的是ConcurrentSessionControlStrategy是SessionFixationProtectionStrategy的子类,所以SessionFixationProtectionStrategy类也具有防Session的固定攻击的功能
在没有这个辅助类的情况下, 有这样一种情况我们无法避免, 那就是同一个用户在不同机器的不同浏览器上可以进行多次登录, 如果我们希望杜绝这种情况的发生, 就需要使用到这个类, 用来控制同一个用户同一时刻最多的登录次数, 如果设置maximumSessions为1, 表时只希望用户在同一时刻只能登录一次。那么Spring Security又是如何控制用户登录次数的呢, 这就需要讲到sessionRegistry这个接口了, 它有个默认实现SessionRegistryImpl, 当用户登录成功后,会调用registerNewSession方法,将当前用户登录的一些基本信息如SessionId, 用户名等放置在内存中, 那么当同一用户再次登录时, 会调用sessionRegistry的getAllSessions方法, 获取登录用户的登录次数, 再与设置的最大次数比较就可以知道用户是否超出了登录限制。sessionRegistry的getAllPrincipals方法可以得到当前系统中的已登录用户,即在线用户,这非常有用。
SessionRegistry源码
package org.springframework.security.core.session; import java.util.List; /** * Maintains a registry of <code>SessionInformation</code> instances. * * @author Ben Alex */ public interface SessionRegistry { //获取当前系统的在线用户 List<Object> getAllPrincipals(); //根据传入的用户名参数, 获取当前用户的登录信息列表 List<SessionInformation> getAllSessions(Object principal, boolean includeExpiredSessions); //根据SessionId, 获取Session的基本信息,如最后一次访问时间,是否过期等 SessionInformation getSessionInformation(String sessionId); //根据SessionId,更新最后访问时间 void refreshLastRequest(String sessionId); //登录成功后注册一个新的SessionInformation到内存中 void registerNewSession(String sessionId, Object principal); //根据SessionId移除SessionInformation void removeSessionInformation(String sessionId); }
3. Spring Security还为我们提供了一个类叫NullAuthenticatedSessionStrategy,查看源码可以发现只有一个空的方法实现, 从类名也可顾名思义,知道这个类的作用就是什么事也不做