1.配置
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.security.web.session.HttpSessionEventPublisher;
import org.springframework.session.FindByIndexNameSessionRepository;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.session.security.SpringSessionBackedSessionRegistry;
@Configuration
public class HttpSessionConfig {
@SuppressWarnings("all")
@Bean
public SpringSessionBackedSessionRegistry springSessionBackedSessionRegistry(
FindByIndexNameSessionRepository sessionRepository) {
return new SpringSessionBackedSessionRegistry(sessionRepository);
}
@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
}
@Resource
private SpringSessionBackedSessionRegistry<?> springSessionBackedSessionRegistry;
http.sessionManagement()
.maximumSessions(1)
.sessionRegistry(springSessionBackedSessionRegistry)
.expiredSessionStrategy(event -> {
HttpServletResponse response = event.getResponse();
PublicUtil.responseByJSON(RespUtil.error(ResultCodeEnum.USER_NOT_LOGIN.getCode(), ResultCodeEnum.USER_NOT_LOGIN.getMsg()), response);
})
;
由于是自定义登录,需要设置在AuthenticationManager成功处理身份验证请求后立即调用的会话处理策略。如果没有设置,默认使用空实现,会导致并发控制不生效
2. 根据指定用户名踢人下线
import cn.anicert.basconsole.core.dao.xtgl.SysUserMapper;
import cn.anicert.basconsole.core.util.FutureUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.session.FindByIndexNameSessionRepository;
import org.springframework.session.Session;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.function.Supplier;
@Component
public class UserUtils {
private static FindByIndexNameSessionRepository<Session> sessionRepository;
private static SysUserMapper sysUserMapper;
private static boolean enable;
@Autowired
@SuppressWarnings("all")
public UserUtils(FindByIndexNameSessionRepository sessionRepository, SysUserMapper sysUserMapper) {
UserUtils.sessionRepository = sessionRepository;
UserUtils.sysUserMapper = sysUserMapper;
}
/**
* 根据指定用户名退出登录
*
* @param supplier 用户名
*/
public static void expireNowByUserName(Supplier<List<String>> supplier) {
if (!enable) {
return;
}
FutureUtils.runAsync(() -> {
List<String> userNames = supplier.get();
if (CollectionUtils.isEmpty(userNames)) {
return;
}
for (String userName : userNames) {
sessionRepository.findByPrincipalName(userName)
.forEach((k, session) -> sessionRepository.deleteById(session.getId()));
}
});
}
public static void expireNowByRoleId(String roleId) {
expireNowByUserName(() -> sysUserMapper.selectNameByRoleId(roleId));
}
public static void expireNowByMenuId(Integer menuId) {
expireNowByUserName(() -> sysUserMapper.selectNameByMenuId(menuId));
}
@Value("${cc.security.session.expire.enable}")
public void setEnable(boolean enable) {
UserUtils.enable = enable;
}
}
3. 自定义当前主体名称(即用户名)的会话索引
默认是username,但是我们项目的用户名是可以重复的,所以踢人下线用用户名就有问题。
在登录成功的地方,设置一下即可。
HttpSession session = request.getSession();
session.setAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME,sysuser.getLoginName());