src/main/java/com/ycl/jxkg/config/spring/security/RestAuthenticationSuccessHandler.java
@@ -1,10 +1,12 @@ package com.ycl.jxkg.config.spring.security; import com.ycl.jxkg.base.SystemCode; import com.ycl.jxkg.constants.CaffeineConstant; import com.ycl.jxkg.domain.entity.UserEventLog; import com.ycl.jxkg.enums.general.YesOrNoEnum; import com.ycl.jxkg.event.UserEvent; import com.ycl.jxkg.service.UserService; import com.ycl.jxkg.utils.CaffeineUtil; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationEventPublisher; @@ -12,6 +14,7 @@ import org.springframework.security.core.userdetails.User; import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; @@ -31,6 +34,7 @@ private final ApplicationEventPublisher eventPublisher; private final UserService userService; private final CaffeineUtil caffeineUtil; /** * Instantiates a new Rest authentication success handler. @@ -39,9 +43,10 @@ * @param userService the user service */ @Autowired public RestAuthenticationSuccessHandler(ApplicationEventPublisher eventPublisher, UserService userService) { public RestAuthenticationSuccessHandler(ApplicationEventPublisher eventPublisher, UserService userService, CaffeineUtil caffeineUtil) { this.eventPublisher = eventPublisher; this.userService = userService; this.caffeineUtil = caffeineUtil; } @Override @@ -49,6 +54,9 @@ Object object = authentication.getPrincipal(); if (null != object) { User springUser = (User) object; // 登录之后保存或更新 用户名与session的关系 String sessionId = request.getSession().getId(); caffeineUtil.put(CaffeineConstant.AUTH, springUser.getUsername(), sessionId); com.ycl.jxkg.domain.entity.User user = userService.getUserByUserName(springUser.getUsername()); if (null != user) { // 密码过期返回强制修改密码标识 src/main/java/com/ycl/jxkg/config/spring/security/RestLoginAuthenticationFilter.java
@@ -2,8 +2,10 @@ import com.ycl.jxkg.config.property.CookieConfig; import com.ycl.jxkg.utils.CaffeineUtil; import com.ycl.jxkg.utils.JsonUtil; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; @@ -16,6 +18,7 @@ import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; import java.util.Enumeration; /** @@ -25,7 +28,11 @@ * @date 2021/12/25 9:45 */ public class RestLoginAuthenticationFilter extends AbstractAuthenticationProcessingFilter { private final org.slf4j.Logger logger = LoggerFactory.getLogger(RestLoginAuthenticationFilter.class); @Autowired private CaffeineUtil caffeineUtil; /** * Instantiates a new Rest login authentication filter. @@ -40,6 +47,7 @@ try (InputStream is = request.getInputStream()) { AuthenticationBean authenticationBean = JsonUtil.toJsonObject(is, AuthenticationBean.class); request.setAttribute(TokenBasedRememberMeServices.DEFAULT_PARAMETER, authenticationBean.isRemember()); authRequest = new UsernamePasswordAuthenticationToken(authenticationBean.getUserName(), authenticationBean.getPassword()); } catch (IOException e) { logger.error(e.getMessage(), e); src/main/java/com/ycl/jxkg/config/spring/security/RestLogoutSuccessHandler.java
@@ -1,13 +1,16 @@ package com.ycl.jxkg.config.spring.security; import com.ycl.jxkg.base.SystemCode; import com.ycl.jxkg.constants.CaffeineConstant; import com.ycl.jxkg.domain.entity.User; import com.ycl.jxkg.domain.entity.UserEventLog; import com.ycl.jxkg.event.UserEvent; import com.ycl.jxkg.service.UserService; import com.ycl.jxkg.utils.CaffeineUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationEventPublisher; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler; import org.springframework.stereotype.Component; @@ -27,6 +30,7 @@ private final ApplicationEventPublisher eventPublisher; private final UserService userService; private final CaffeineUtil caffeineUtil; /** * Instantiates a new Rest logout success handler. @@ -35,15 +39,19 @@ * @param userService the user service */ @Autowired public RestLogoutSuccessHandler(ApplicationEventPublisher eventPublisher, UserService userService) { public RestLogoutSuccessHandler(ApplicationEventPublisher eventPublisher, UserService userService, CaffeineUtil caffeineUtil) { this.eventPublisher = eventPublisher; this.userService = userService; this.caffeineUtil = caffeineUtil; } @Override public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { org.springframework.security.core.userdetails.User springUser = (org.springframework.security.core.userdetails.User) authentication.getPrincipal(); if (null != springUser) { // 清除用户名和sessionId之间的绑定 caffeineUtil.remove(CaffeineConstant.AUTH, springUser.getUsername()); SecurityContextHolder.clearContext(); User user = userService.getUserByUserName(springUser.getUsername()); UserEventLog userEventLog = new UserEventLog(user.getId(), user.getUserName(), user.getRealName(), new Date()); userEventLog.setContent(user.getUserName() + " 登出了学之思开源考试系统"); src/main/java/com/ycl/jxkg/config/spring/security/SecurityConfigurer.java
@@ -67,6 +67,12 @@ this.restAccessDeniedHandler = restAccessDeniedHandler; } @Bean public SessionFilter sessionFilter() throws Exception { SessionFilter jwtTokenFilter = new SessionFilter(authenticationManagerBean()); return jwtTokenFilter; } /** * @param http http * @throws Exception exception @@ -95,6 +101,7 @@ .and().rememberMe().key(CookieConfig.getName()).tokenValiditySeconds(CookieConfig.getInterval()).userDetailsService(formDetailsService) .and().csrf().disable() .cors(); http.addFilter(sessionFilter()); } src/main/java/com/ycl/jxkg/config/spring/security/SessionFilter.java
New file @@ -0,0 +1,61 @@ package com.ycl.jxkg.config.spring.security; import com.ycl.jxkg.base.SystemCode; import com.ycl.jxkg.constants.CaffeineConstant; import com.ycl.jxkg.utils.CaffeineUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextImpl; import org.springframework.security.core.userdetails.User; import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Enumeration; import java.util.Objects; /** * @author 29443 * @date 2022/4/4 */ @Slf4j public class SessionFilter extends BasicAuthenticationFilter { @Autowired private CaffeineUtil caffeineUtil; public SessionFilter(AuthenticationManager authenticationManager) { super(authenticationManager); } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { // 检查请求中是否有security设置的认证信息。没有的话本Filter不做处理 SecurityContextImpl securityContext = (SecurityContextImpl) request.getSession().getAttribute("SPRING_SECURITY_CONTEXT"); if (Objects.isNull(securityContext)) { chain.doFilter(request, response); return; } // 有的话验证 Authentication authentication = securityContext.getAuthentication(); User authUser = (User) authentication.getPrincipal(); // 检查这个用户对应的sessionId是否和登录时的sessionId相等 String loginSessionId = (String) caffeineUtil.get(CaffeineConstant.AUTH, authUser.getUsername()); if (! request.getSession().getId().equals(loginSessionId)) { log.warn("检测到同一账号两个浏览器登录"); RestUtil.response(response, SystemCode.UNAUTHORIZED.getCode(), "当前登录已失效"); } else { chain.doFilter(request, response); } } } src/main/java/com/ycl/jxkg/utils/CaffeineUtil.java
@@ -56,6 +56,19 @@ } /** * 删除 * * @param database 数据库/ caffeine map的key * @param key map的key */ public void remove(String database, String key) { Map<String, Object> map = (Map<String, Object>) caffeineCache.getIfPresent(database); if (Objects.nonNull(map)) { map.remove(key); } } /** * 创建key * * @param database