开发场景:正在开发的这个项目后台登陆系统用的授权是oauth2.0机制,token有效期是8个小时,客户提了一个需求:使当前系统登陆之后半小时内没有操作就需要重新登陆,所以我这里决定用Redis缓存+拦截器来实现,前端每次请求除登陆之外的接口都需要走拦截器,首先我们登陆的时候会把用户id存入redis中,有效时间30分钟,其次访问其他接口的时候走拦截器,查询Redis中是否存在登陆信息,如果存在就重新插入redis,有效时间30分钟覆盖之前的redis数据,如果不存在说明超过30分中未进行其他操作,抛出异常提示客户端登陆超时重新登陆,自定义拦截器代码如下:
package com.gmac.backend.management.interceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import com.gmac.backend.management.controller.BaseController; import com.gmac.backend.management.redis.RedisService; import com.gmac.common.utils.StringUtil; import lombok.extern.slf4j.Slf4j; /** * 自定义登录拦截器 * @author V_PS68C7 * */ @Slf4j @Component public class LoginInterceptor extends BaseController implements HandlerInterceptor { @Autowired private RedisService redisService; /** * 在请求处理之前进行调用(Controller方法调用之前) */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) { log.debug("[START] - check userId"); String userId = super.getUserId(); //从Redis中读取userId if(StringUtil.isNullOrEmpty(userId)) { log.debug("[error] - userId is empty"); throw new InvalidTokenException("userId为空"); } else { //验证userId在Redis中是否存在 boolean check = redisService.checkUserId(userId); if(check) { //存在就重新添加覆盖掉之前的 redisService.addUserId(userId); log.debug("[DEBUG] - 重新添加用户id到Redis"); } else { //不存在就抛出异常 String errorMsg = "用户访问超时 用户ID : ".concat(userId); log.debug("[error] - Landing timeout return:{}", errorMsg); throw new InvalidTokenException(errorMsg); } } log.debug("[END] - check userId"); return true; } /** * 请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后) */ @Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object, ModelAndView modelAndView) throws Exception { } /** * 在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作) */ @Override public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object, Exception e) throws Exception { } }
package com.gmac.backend.management.interceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import com.gmac.common.constant.PermitAllUrl; /** * 加载拦截器 * @author V_PS68C7 * */ @Configuration public class WebMvcConfig implements WebMvcConfigurer { @Autowired private LoginInterceptor loginInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginInterceptor) .addPathPatterns("/**") .excludePathPatterns(PermitAllUrl.permitAllUrl("/users-anon/**","/login/**","/catalog/**")); } }