-
自定义拦截注解 @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; }
-
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; } }
-
@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; }
Spring 多次访问后,自定义注解进行验证码验证
猜你喜欢
转载自my.oschina.net/u/3792723/blog/1633467
今日推荐
周排行