一、前述
首先,介绍几种场景,读者可对号入座。
1)、默认登录的会话到期后直接跳转到登录页,再次登录后进行操作。
2)、同一账户限制同时登录用户数量,达到后踢掉之前登录的用户
3)、同一账户限制同时登录用户数量,达到不允许新的用户登录
二、场景
2.1、默认会话到期处理
一般情况下,在用户无任何操作30分钟后会话会默认到期。此时,则需要用户重新登录才可以进行相应操作。
.sessionManagement()//session到期提示
.invalidSessionUrl("/login")//分离前通过此配置跳转登录页进行登录
.invalidSessionStrategy(mMyAuthenctiationInvalidSessionStrategy)//session到期拦截并返回相应信息
上述代码中拦截处理部分mMyAuthenctiationInvalidSessionStrategy的代码:
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.web.session.InvalidSessionStrategy;
import org.springframework.stereotype.Component;
import net.cnki.common.msgreturn.ResultCode;
import net.cnki.common.msgreturn.ResultGenerator;
/**
* session到期
* @author ZhiPengyu
*
*/
@Component
public class MyAuthenctiationInvalidSessionStrategy implements InvalidSessionStrategy{
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
ResultGenerator resultGenerator;
@Override
public void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
logger.info("session到期!");
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(resultGenerator.getFreeResult(ResultCode.SESSION_EXPIRES).toString());
}
}
2.2、达到会话限制数量踢掉之前登陆用户
此时分离前后都一样的配置。但是需要注意实现的接口SessionInformationExpiredStrategy,与到期不同。
http.sessionManagement().maximumSessions(1).expiredSessionStrategy(myAuthenctiationSessionStrategy);//会话管理:用户仅允许一个登陆,踢出旧的登录
上面配置完session之后,下边是实现接口拦截。
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.web.session.SessionInformationExpiredEvent;
import org.springframework.security.web.session.SessionInformationExpiredStrategy;
import org.springframework.stereotype.Component;
import net.cnki.common.msgreturn.ResultCode;
import net.cnki.common.msgreturn.ResultGenerator;
/**
* 已被其他用户登录
* @author ZhiPengyu
*
*/
@Component
public class MyAuthenctiationSessionInformationExpiredStrategy implements SessionInformationExpiredStrategy{
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
ResultGenerator resultGenerator;
@Override
public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException, ServletException {
// TODO Auto-generated method stub
logger.info("已被其他用户登录!");
HttpServletResponse response = event.getResponse();
response.setContentType("application/json;charset=UTF-8");
response.getWriter().print(resultGenerator.getFreeResult(ResultCode.SESSION_EXPIRES_OTHER_LOGIN).toString());
response.flushBuffer();
}
}
2.3、达到会话限制数量不允许新用户登陆
此处则不需要拦截,会返回对应的状态以及信息。但是值得注意的是,需要将HttpSessionEventPublisher加入spring容器中。否则会出现当以登录的用户退出后在登陆也会被阻止的情况。此处需要讲到源码的实现,不多解释,简单说是因为监听不在容器,所以监听不到容器中的session状态。
http.sessionManagement().maximumSessions(1).maxSessionsPreventsLogin(true);//只允许一个登陆,不允许新的登录
同时在WebSecurityConfig配置类中添加如下即可:(springboot利用@Configuration注解在配置类加@Bean不多说了)
@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
当新用户登陆时,会返回401,并提示已经达到最大值。登录失败
{
"code": "401",
"message": "Maximum sessions of 1 for this principal exceeded"
}
三、总结
session的情况多种多样,security的功能还是很强大的,我所遇到的情况也并不多。