uniapp滑块验证码对接tianai-captcha

滑块验证码的功能大家都不陌生,天爱大佬的滑块验证码给我的体验感非常的好,下文详细讲解如何在uniapp中对接滑块验证码
blog.csdnimg.cn/direct/cc91ab54560b4104a3f09b1bd97119ef.png)

tianai-captcha是是什么,怎么用

tianai-captcha其实是滑块验证码的后端,从后端生成图片,前端操作后提交到后端进行验证

  • tianai-captcha 目前支持的行为验证码类型
  • 滑块验证码
  • 旋转验证码
  • 滑动还原验证码
  • 文字点选验证码

  • 本文只拿旋转验证码作为例子

    1. 导入maven依赖
    <dependency>
        <groupId>cloud.tianai.captcha</groupId>
        <artifactId>tianai-captcha</artifactId>
        <version>1.5.1</version>
    </dependency>
    
    2.添加配置类
    @Component
    @RequiredArgsConstructor
    public class CaptchaResourceConfiguration {
    
        private final ResourceStore resourceStore;
    
        @PostConstruct
        public void init() {
    
            // 滑块验证码 模板 (系统内置) ,这里添加的模板等同于  captcha.init-default-resource=true , 如果配置中设置了加载默认模板,这里模板可不用配置
            ResourceMap template1 = new ResourceMap("default", 4);
            template1.put(SliderCaptchaConstant.TEMPLATE_ACTIVE_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/1/active.png")));
            template1.put(SliderCaptchaConstant.TEMPLATE_FIXED_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/1/fixed.png")));
            ResourceMap template2 = new ResourceMap("default", 4);
            template2.put(SliderCaptchaConstant.TEMPLATE_ACTIVE_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/2/active.png")));
            template2.put(SliderCaptchaConstant.TEMPLATE_FIXED_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/2/fixed.png")));
            // 旋转验证码 模板 (系统内置)
            ResourceMap template3 = new ResourceMap("default", 4);
            template3.put(SliderCaptchaConstant.TEMPLATE_ACTIVE_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, StandardSliderImageCaptchaGenerator.DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/3/active.png")));
            template3.put(SliderCaptchaConstant.TEMPLATE_FIXED_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, StandardSliderImageCaptchaGenerator.DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/3/fixed.png")));
    
            // 1. 添加一些模板
            addTemplate(CaptchaTypeConstant.SLIDER, template1);
            addTemplate(CaptchaTypeConstant.SLIDER, template2);
            addTemplate(CaptchaTypeConstant.ROTATE, template3);
    
            // 2. 添加自定义背景图片, resource 的参数1为资源类型(默认支持 classpath/file/url ), resource 的参数2为资源路径, resource 的参数3为标签
            resourceStore.addResource(CaptchaTypeConstant.SLIDER, new Resource("classpath", "bgimages/a.jpg", "default"));
            resourceStore.addResource(CaptchaTypeConstant.SLIDER, new Resource("classpath", "bgimages/b.jpg", "default"));
            resourceStore.addResource(CaptchaTypeConstant.SLIDER, new Resource("classpath", "bgimages/c.jpg", "default"));
            resourceStore.addResource(CaptchaTypeConstant.SLIDER, new Resource("classpath", "bgimages/d.jpg", "default"));
            resourceStore.addResource(CaptchaTypeConstant.SLIDER, new Resource("classpath", "bgimages/e.jpg", "default"));
            resourceStore.addResource(CaptchaTypeConstant.SLIDER, new Resource("classpath", "bgimages/g.jpg", "default"));
            resourceStore.addResource(CaptchaTypeConstant.SLIDER, new Resource("classpath", "bgimages/h.jpg", "default"));
            resourceStore.addResource(CaptchaTypeConstant.SLIDER, new Resource("classpath", "bgimages/i.jpg", "default"));
            resourceStore.addResource(CaptchaTypeConstant.SLIDER, new Resource("classpath", "bgimages/j.jpg", "default"));
            resourceStore.addResource(CaptchaTypeConstant.ROTATE, new Resource("classpath", "bgimages/48.jpg", "default"));
            resourceStore.addResource(CaptchaTypeConstant.CONCAT, new Resource("classpath", "bgimages/48.jpg", "default"));
            resourceStore.addResource(CaptchaTypeConstant.WORD_IMAGE_CLICK, new Resource("classpath", "bgimages/c.jpg", "default"));
        }
    }
    
    3.添加yaml相关配置
    # 滑块验证码配置, 详细请看 cloud.tianai.captcha.autoconfiguration.ImageCaptchaProperties 类
    captcha:
      # 如果项目中使用到了redis,滑块验证码会自动把验证码数据存到redis中, 这里配置redis的key的前缀,默认是captcha:slider
      prefix: captcha
      # 验证码过期时间,默认是2分钟,单位毫秒, 可以根据自身业务进行调整
      expire:
        # 默认缓存时间 2分钟
        default: 10000
        # 针对 点选验证码 过期时间设置为 2分钟, 因为点选验证码验证比较慢,把过期时间调整大一些
        WORD_IMAGE_CLICK: 20000
      # 使用加载系统自带的资源, 默认是 false(这里系统的默认资源包含 滑动验证码模板/旋转验证码模板,如果想使用系统的模板,这里设置为true)
      init-default-resource: true
      # 缓存控制, 默认为false不开启
      local-cache-enabled: true
      # 验证码会提前缓存一些生成好的验证数据, 默认是20
      local-cache-size: 20
      # 缓存拉取失败后等待时间 默认是 5秒钟
      local-cache-wait-time: 5000
      # 缓存检查间隔 默认是2秒钟
      local-cache-period: 2000
      # 配置字体包,供文字点选验证码使用,可以配置多个,不配置使用默认的字体
      font-path:
        - classpath:font/SimHei.ttf
      secondary:
        # 二次验证, 默认false 不开启
        enabled: false
        # 二次验证过期时间, 默认 2分钟
        expire: 120000
        # 二次验证缓存key前缀,默认是 captcha:secondary
        keyPrefix: "captcha:secondary"
    
    4.在springgateway中增加路由

    在springgateway中增加路由
    如果集成springsecurity框架,也可以通过鉴权的控制去写controller层代码
    本文是以第一种为示例

    @Slf4j
    @Configuration
    @AllArgsConstructor
    public class RouterFunctionConfiguration {
    
    	private final WxminiImageCodeCreateHandler wxminiImageCodeCreateHandler;
    
    	private final WxminiImageCheckHandler wxminiImageCheckHandler;
    
    
    	@Bean
    	public RouterFunction routerFunction() {
    		return RouterFunctions
    				.route(RequestPredicates.path("/wxminiCaptcha").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)),
    						wxminiImageCodeCreateHandler)
    				.andRoute( RequestPredicates.POST("/wxminiCheck").and(RequestPredicates.accept(MediaType.APPLICATION_JSON)),
    						wxminiImageCheckHandler);
    
    
    	}
    
    }
    

    下面是验证码一个生成处理器

      @SneakyThrows
        @Override
        public Mono<ServerResponse> handle(ServerRequest serverRequest) {
            CaptchaResponse<ImageCaptchaVO> responseModel = imageCaptchaApplication.generateCaptcha(CaptchaTypeConstant.ROTATE);
    
            return ServerResponse.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON)
                    .body(BodyInserters.fromValue(objectMapper.writeValueAsString(R.ok(responseModel))));
        }
    

    此方法通过创建图片二维码返给前端,返参如下
    在这里插入图片描述
    重要的是两个图片的base64码,对于旋转和滑块来说重要的还有背景图的横坐标
    下面是验证的后端代码

     @Override
        public Mono<ServerResponse> handle(ServerRequest request) {
    
            return request.body(BodyExtractors.toMono(ImageData.class))
                    // 得到Body中的数据,进行逻辑操作
                    // 操作完成最终将结果进行映射为最终的响应类型Mono<ServerResponse>
                    .flatMap(entity ->
                            ServerResponse.ok()
                            .contentType(MediaType.APPLICATION_JSON)
                            .bodyValue(imageCaptchaApplication.matching(entity.getId(),entity.getData())));
        }
    
    

    值得一提的是路由获取post参数让人头疼,经过多次尝试采用BodyExtractors.toMono(ImageData.class)).flatMap最终才能取到参数
    ImageData.class如下所示

    @Data
    public class ImageData {
        private String  id;
        private ImageCaptchaTrack data;
    }
    

    一切准备就绪,看下tianai包中校验的底层逻辑
    入参如下
    在这里插入图片描述
    校验逻辑如下

     public boolean doValidSliderCaptcha(ImageCaptchaTrack imageCaptchaTrack, AnyMap imageCaptchaValidData, Float tolerant, String type) {
            Float oriPercentage = imageCaptchaValidData.getFloat("percentage");
            if (oriPercentage == null) {
                return false;
            } else {
                List<Track> trackList = imageCaptchaTrack.getTrackList();
                Track firstTrack = (Track)trackList.get(0);
                Track lastTrack = (Track)trackList.get(trackList.size() - 1);
                float calcPercentage = this.calcPercentage(lastTrack.getX().floatValue() - firstTrack.getX().floatValue(), imageCaptchaTrack.getBgImageWidth());
                boolean percentage = this.checkPercentage(calcPercentage, oriPercentage, tolerant.floatValue());
                if (percentage) {
                    imageCaptchaValidData.put("user_current_percentage", String.valueOf(calcPercentage));
                    imageCaptchaValidData.put("user_current_percentage_std", String.valueOf(calcPercentage - oriPercentage.floatValue()));
                }
    
                return percentage;
            }
        }
    

    对于旋转和滑块来说,很明显他是拿前端传递的bgimagewidth,取第一个track中的值和鼠标抬起最后一个tack值 和背景图的长度计算出比例再和缓存中生成图片产生的比例进行比对,误差大小在0.03左右,如此后端的讲解就完成啦

    参考

    http://doc.captcha.tianai.cloud/
    
Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐