前言
在平时,有一些网站会有很多处需要验证码验证的地方,遂今天研究验证码的验证方法,为了是验证码的代码,一次编写,到处运行,而且还得是实现可配置的操作。
正文
一:java注解的基本操作
实现可插拔,最主要的是注解,因为注解可以降低代码的入侵性,只需要配置注解,就可以实现不同的功能
二:spring的拦截器的基本操作
可以这样想象,在一个提供的接口前,如果把所有的验证码都利用拦截器来进行处理,那么就实现了一次编写到处使用,而且我们在编写的过程中不必纠结于验证码的操作,是我们的开发工作能更好的专注于我们的业务,以及业务的逻辑,那么我们的开发的效率会大大提高。
三:综上所述,我们要这样设计我们的网站验证码的统一操纵:
首先:我们要定义这个的一个注解;
package com.breakpoint.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 接口防刷限流注解方式 * * @author :breakpoint/赵立刚 * @date : 2018/04/02 */ @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface AccessLimit { /** * 两次点击间隔时间 默认时间1000ms 单位ms * * @return */ long interval() default 1000L; /** * 是否需要进行登录 * * @return */ boolean isLogIn() default true; /** * 冻结的账户是否可以进行通过 必须是 isLogIn 为true的情况下才有效 * * @return */ boolean freezeCanAccess() default false; /** * 该接口是否可用 也可以说接口是否可达 * * @return */ boolean enable() default true; /** * 验证码是否需要进行验证 * * @return */ boolean isVerifyCode() default false; /** * 权限值 大于等于才能进行操作 * <p> * 当然 首先得闲登陆 * * @return */ int permissionInt() default 0; }
其次:我们要定义一个Interceptor
package com.breakpoint.configue; import com.breakpoint.annotation.AccessLimit; import com.breakpoint.constans.RetCodeConstant; import com.breakpoint.util.ExploreWriteUtils; import com.breakpoint.util.TokenUtils; import com.breakpoint.util.UserInfo; import com.redis.RedisSetObject; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * 检验用户登陆以及其他操作 * Created by Administrator on 2018/4/29 0029. */ @Slf4j public class AccessLimitInterceptor extends HandlerInterceptorAdapter { private RedisTemplate redisTemplate; public AccessLimitInterceptor(RedisTemplate redisTemplate) { this.redisTemplate = redisTemplate; } /** * 限流防止频繁刷新 * * @param request * @param response * @param handler * @return * @throws Exception */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (handler instanceof HandlerMethod) { /** * 向下强转 */ HandlerMethod handlerMethod = (HandlerMethod) handler; AccessLimit methodAnnotation = handlerMethod.getMethodAnnotation(AccessLimit.class);//获取到我们的那个注解,之后根据我们的配置,来进行是否操作 if (null != methodAnnotation) { /** * 具体的操作逻辑 */ ValueOperations valueOperations = redisTemplate.opsForValue(); /** * 验证验证码是否正确 */ if (methodAnnotation.isVerifyCode()) { /** * 首先verifyCode存储在redis * */ String key = request.getParameter("verifyCodeKey"); if (StringUtils.isEmpty(key)) { ExploreWriteUtils.writeMessage(RetCodeConstant.FAIL, request, response, "请求参数中没有verifyCodeKey"); return false; } /** * 获取到存储的key */ String verifyCodeKey = RedisSetObject.getKey(key); /** * 验证码的基本操作 1min 时间 */ String verifyCode = (String) valueOperations.get(verifyCodeKey); if (StringUtils.isEmpty(verifyCode)) { ExploreWriteUtils.writeMessage(RetCodeConstant.ALERT, request, response, "请刷新验证码"); return false; } /** * 提交过来的验证码 */ String verifyCode1 = request.getParameter("verifyCode"); if (StringUtils.isEmpty(verifyCode1)) { ExploreWriteUtils.writeMessage(RetCodeConstant.FAIL, request, response, "请填写验证码"); return false; } /** * 检验验证码是否一直 */ String verifyCodeRedis = verifyCode.toLowerCase().trim(); String verifyCodeReq = verifyCode1.toLowerCase().trim(); log.info(verifyCode); log.info(verifyCodeRedis); log.info(verifyCodeReq); if (!verifyCodeRedis.equals(verifyCodeReq)) { ExploreWriteUtils.writeMessage(RetCodeConstant.FAIL, request, response, "验证码不正确"); return false; } } } } return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { if (response.getStatus() == 500) { modelAndView.setViewName("/errorpage/500"); } else if (response.getStatus() == 404) { modelAndView.setViewName("404"); } super.postHandle(request, response, handler, modelAndView); } }
之后,我们在请求的时候的请求的数据,
$("#inner_liuyan_button").click(function () { addLoading($("#header_inner_show")); var message = $("#inner_liuyan_message").val(); if(message.length<5||message.length>300){ alert("输入的留言留言信息5到300字"); completeLoading(); return; } var postData = { message: message, verifyCodeKey:localStorage.getItem("verifyCodeKey"),//y验证码的取值的key verifyCode:$("#inner_liuyan_verifyCode").val()//用户提交的验证码 }; /** * 请求的基本操作 */ $.post("v1/liuyan/save", postData, function (returnData) { completeLoading(); if(returnData.respCode==200){ alert("发表成功"); window.location.reload(); }else { alert(returnData.data); } }); }); Controller方面的基本配置 /** * 新增加 留言信息 * * @return */ @AccessLimit(isLogIn = false,isVerifyCode = true)//配置注解的基本操作,校验是否需要登陆位false,是否校验验证码为true @PostMapping("/save") public Object saveTopic(@RequestParam("message") String message, HttpServletRequest request) { try { String userName = request.getRemoteAddr(); Object insert = liuYanService.insert(userName, message); return ResponseResult.createOK(insert); } catch (Exception e) { return ResponseResult.createFailResult("操作失败", e.getMessage()); } }
根据以上步骤,我们就配置好了如何进行校验验证码的整体的流,这样整体上,下次在需要使用校验验证码的时候仅仅使用注解
@AccessLimit(isLogIn = false,isVerifyCode = true)//配置注解的基本操作,校验是否需要登陆位false,是否校验验证码为true
加上前台请求的具体的请求格式即可,这样我们才能更加专注的编写我们自己项目的业务代码逻辑,节约更多的时间