Spring 多次访问后,自定义注解进行验证码验证

  1. 自定义拦截注解
    @Retention(RUNTIME)------- 它所注解的注解保留多久
                       ------- 保留至运行时,所以我们可以通过反射去获取注解信息
    @Target(METHOD) --------------Target通过ElementType来指定注解可使用范围的枚举集合
                    -------METHOD    可用于方法上
    public @interface VerifyCode {
       
       /**
        * 接口安全限制,两次接口调用间隔超过该时间(单位是毫秒),则必须进行验证码校验
        * @return
        */
       long limit();
       
    
       /**
        * 最多调用次数,超过该限定次数,则必须进行验证码校验
        * @return
        */
       int max() default -1;
       
       /**
        * 需要验证码服务的接口名称    //TODO 应该枚举值,保持全局唯一
        * @return
        */
       String name();
       
       /**
        * 默认时间是1小时
        * @return
        */
       int time() default 1;
       
       /**
        * 默认单位是小时
        * @return
        */
       TimeUnit timeUnit() default TimeUnit.HOURS;
    }
  2. public class VerifyCodeInterceptor extends BaseInterceptor {
       
       private static final Logger LOGGER = LoggerFactory.getLogger(VerifyCodeInterceptor.class);
    
       @Autowired
       private StringRedisTemplate stringRedisTemplate;
       
       /**
        * 
        * 验证码处理
        * @throws ServiceException 
        * 
        */
       @Override
       protected boolean preHandle(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod,String requestUri, Object handler) throws ServiceException {
          if(handlerMethod == null) {
             return true;
          }
          
          VerifyCode verifyCodeAnnotation = handlerMethod.getMethodAnnotation(VerifyCode.class);
          if(verifyCodeAnnotation == null) {
             //木有注解,不拦截
             return true;
          }
          
          String clientIp = WebUtils.getClientIP(request);
          
    //    if(!RegUtils.match(clientIp, RegUtils.IP_V4)) {
    //       return true;
    //    }
          
          String redisKey = RedisKeys.REQUEST_LIMIT + verifyCodeAnnotation.name() + "_" + clientIp;
          
          String record = this.stringRedisTemplate.opsForValue().get(redisKey);
          
          if(GeneralUtils.isEmpty(record)) { 
             //该ip首次调用
             JSONObject jsonObject = new JSONObject();
             //接口调用时间
             jsonObject.put("timestamp", DateUtils.timestamp());
             //调用次数
             jsonObject.put("count", 1);
             this.stringRedisTemplate.opsForValue().set(redisKey, jsonObject.toJSONString(),verifyCodeAnnotation.time(),verifyCodeAnnotation.timeUnit() );
             return true;
          }
          
          JSONObject jsonObject = JSON.parseObject(record);
          
          int count = jsonObject.getIntValue("count");
          
          long timestamp = jsonObject.getLongValue("timestamp");
          
          //刷新调用时间 & 次数
          jsonObject.put("timestamp", DateUtils.timestamp());
          jsonObject.put("count", (count >= 99999 ) ? 99999 : count + 1);
          this.stringRedisTemplate.opsForValue().set(redisKey, jsonObject.toJSONString(),verifyCodeAnnotation.time(),verifyCodeAnnotation.timeUnit() );
          
          /**
           * 
           * 两次调用超过间隔时间,或者是调用次数超过限制,必须进行验证码校验
           * 
           */
          
          boolean flag = false;
          
          if(verifyCodeAnnotation.limit() > 0 && (DateUtils.timestamp() - timestamp) < verifyCodeAnnotation.limit()){
             flag = true;
          }
          
          if(verifyCodeAnnotation.max() > 0 && (verifyCodeAnnotation.max() < count)) {
             flag = true;
          }
             
          
          if(flag) {
             
             LOGGER.debug("验证码校验:{}",verifyCodeAnnotation.name());
             
             //超过限制,需要进行验证码校验
             String verifyCode = request.getParameter("verifyCode");
             
             String validateCode = request.getParameter("codeKey");
             
             if(GeneralUtils.isEmpty(verifyCode) || GeneralUtils.isEmpty(validateCode)) {
                //缺少验证码
                throw ServiceExceptions.MISSING_VERIFY_CODE;
             }
             
             String code = this.stringRedisTemplate.opsForValue().get(RedisKeys.VERIFY_CODE + validateCode);
             
             if(code != null) {
                //一码一验证
                this.stringRedisTemplate.delete(RedisKeys.VERIFY_CODE + validateCode);
                if(!code.equalsIgnoreCase(verifyCode)) {
                   throw ServiceExceptions.VERIFY_CODE_FAIL;
                }
             }else {
                throw ServiceExceptions.VERIFY_CODE_FAIL;
             }
          }
          return true;
       }
    }
  3. @PostMapping("/login")
    @ResponseBody
    @VerifyCode(limit = 2000,max = 10,name = "login")
    public Message<Void> login(HttpServletRequest request,
                                   HttpServletResponse response,
                                   @CookieValue(value = CookieKeys.USER_SESSION,required = false)String existsSession,
                                   @RequestParam("account")String account,
                                   @RequestParam("password")String password,
                                   @RequestParam(value = "remenber",defaultValue = "false")Boolean remenber)throws Exception{
    
       LOGGER.debug("执行登录:{},{}",account,password);
       
           UserEntity userEntity = this.userService.queryByAccount(account);
           
           if(userEntity == null || !userEntity.getPass().equals(DigestUtils.md5DigestAsHex(DigestUtils.md5DigestAsHex(password.getBytes()).getBytes()))){
               return Messages.LOGIN_FAILD;
           }
    
           String sessionKey = GeneralUtils.getUUID();
    
           Cookie cookie = new Cookie(CookieKeys.USER_SESSION,sessionKey);
           cookie.setPath("/");
           //Emmmmmmm windows下肯定是测试环境了
           cookie.setDomain(GeneralUtils.isWindows() ? "127.0.0.1" : "javaweb.io");
           //cookie 7天
           cookie.setMaxAge(remenber ? (60 * 60 * 24 * 7) : -1);
           cookie.setHttpOnly(Boolean.TRUE);
    
           response.addCookie(cookie);
    
           if(!GeneralUtils.isEmpty(existsSession)) {
               //多次登录,删除上次会话
               this.stringRedisTemplate.delete(RedisKeys.SESSION_USER + existsSession);
           }
           
           if(!GeneralUtils.isEmpty(userEntity.getSessionId())) {
               //禁止用户重复登录 
               //TODO 别直接删除会话记录,设置session别处登录 flag
               this.stringRedisTemplate.delete(RedisKeys.SESSION_USER + userEntity.getSessionId());
           }
           
           UserSession userSession = new UserSession();
           userSession.setRemenber(remenber);
           userSession.setUser(userEntity);
           
           if(remenber) {
                // redis 缓存session 7 天
               this.stringRedisTemplate.opsForValue().set(RedisKeys.SESSION_USER + sessionKey, JSON.toJSONString(userSession), 7,TimeUnit.DAYS);
           }else {
               // redis 缓存session 30分钟
                this.stringRedisTemplate.opsForValue().set(RedisKeys.SESSION_USER + sessionKey, JSON.toJSONString(userSession), 30,TimeUnit.MINUTES);
           }
    
           //广播登录信息
           if(userEntity.getLoginRadio()) {
               NowEndpoint.broadCast(new SocketMessage(SocketMessage.Type.LOGIN, MessageFormat.format(this.loginNoitfy,userEntity.getUserId(),userEntity.getPortrait(),userEntity.getName())));
           }
           
           //更新session id
           userEntity.setSessionId(sessionKey);
           this.userService.updateByPrimaryKeySelective(userEntity);
    
    
           //登录日志
           this.executorService.submit(() -> {
               LOGGER.debug("登录日志记录");
               LoginRecordEntity loginRecordEntity = new LoginRecordEntity();
               loginRecordEntity.setRecordId(GeneralUtils.getUUID());
               //客户端真实ip,由nginx反向代理设置真实客户端ip请求头
               loginRecordEntity.setIp(request.getHeader(this.clientIpHeader));
               loginRecordEntity.setUserAgent(request.getHeader(HttpHeaders.USER_AGENT));
               loginRecordEntity.setUserId(userEntity.getUserId());
               loginRecordEntity.setCreateDate(new Date());
               try {
             this.loginRecordService.createRecord(loginRecordEntity);
          } catch (Exception e) {
             e.printStackTrace();
             LOGGER.error("登录日志记录异常:{}",e);
          }
           });
       return Messages.SUCCESS;
    }
     

猜你喜欢

转载自my.oschina.net/u/3792723/blog/1633467