调查问卷Type Form的集成

news2024/9/21 15:51:25

简介

Typeform是一家制作线上调查问卷的公司。

Muñoz 和 David Okuniev两人于2012年创作出一个更加动态、更具交互性的用户调查工具,每次只提一个问题,并且根据用户的回答为其呈现下一个问题,像和朋友间的对话一样,让用户在不知不觉中就完成了问卷。

Typeform将帮你获得有关产品和经验的反馈,创建和分享反馈、建立联系人表单,进行客户开发调查,结果将以交互式表格的形式快速发送到你的智能手机、平板电脑和台式电脑上。

Type Form 配置

1. 首先注册帐号,然后创建自己的调查问卷模版。 

 2.配置后端服务的回调地址和验签的密钥。

3. 根据需要可以配置隐藏的参数,隐藏参数可以通过回调接口传过来。 

 回调代码

1.开放Type Fome接口无需Token校验。

spring.security 配置不拦截的url.
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    ...

  @Override
  public void configure(WebSecurity web) {
    web.ignoring().antMatchers("/auth/**", "/actuator/health", "/satisfaction/callback");
  }

}

2.拦截Type Fome接口,校验签名。

注册Bean进行全局的url拦截。

  @Bean
  public TypeformFilter typeformContentCacheFilter() {
    return new TypeformFilter();
  }

拦截Type Form的url,然后进行 HmacSha256Signature 签名校验。

注意: filterChain.doFilter(servletRequestWrapper, httpServletResponse),由于HttpServletRequest去读一次后就会释放掉资源,所以需要把请求报文缓存起来。

@Slf4j
@Order(value = Ordered.HIGHEST_PRECEDENCE)
public class TypeformFilter extends OncePerRequestFilter {

  public static final String TYPEFORM_SATISFACTION_CALLBACKS = "/satisfaction/callback";

  @Value("${typeform.sha.key}")
  private String key;

  @Override
  public void destroy() {

  }

  @Override
  protected void doFilterInternal(@NonNull HttpServletRequest httpServletRequest,
      @NonNull HttpServletResponse httpServletResponse,
      @NonNull FilterChain filterChain) throws ServletException, IOException {
    CachedBodyHttpServletRequest servletRequestWrapper = new CachedBodyHttpServletRequest(
        httpServletRequest);
    String requestUri = servletRequestWrapper.getRequestURI();
    if (requestUri.equals(TYPEFORM_SATISFACTION_CALLBACKS)) {
      String signature = servletRequestWrapper.getHeader("Typeform-Signature");
      String payload = servletRequestWrapper.read();
      if (!validateHmacSha256Signature(signature, payload, key)) {
        httpError(httpServletResponse);
        return;
      }
    }
    filterChain.doFilter(servletRequestWrapper, httpServletResponse);
  }

  private static boolean validateHmacSha256Signature(String signature, String payload, String key) {
    return getHmacSha256(payload, key).equals(signature);
  }

  private static String getHmacSha256(String payload, String key) {
    return "sha256=" + HashUtil.hmacWithAlgorithm("HmacSHA256", payload, key);
  }

  public static void httpError(HttpServletResponse response) throws IOException {
    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
    PrintWriter out = response.getWriter();
    response.setContentType("application/json");
    response.setCharacterEncoding("UTF-8");
    out.print("Invalid Request");
    out.flush();
    out.close();
  }

}

hmac算法的工具类,通过算法名称,请求报文,签名密钥获取加密后的报文。 

@Slf4j
public class HashUtil {

  public static String hmacWithAlgorithm(String algorithm, String payload, String key) {
    try {
      SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), algorithm);
      Mac mac = Mac.getInstance(algorithm);
      mac.init(secretKeySpec);
      return Base64.getEncoder().encodeToString((mac.doFinal(payload.getBytes())));
    } catch (NoSuchAlgorithmException | InvalidKeyException e) {
      log.error("hmacWithAlgorithm error", e);
      return null;
    }
  }
}

 CachedBodyHttpServletRequest 缓存了请求报文,以便后续拦截器过滤使用。

缓存代码:this.requestBody = StreamUtils.copyToByteArray(is);

public class CachedBodyHttpServletRequest extends HttpServletRequestWrapper {

  private final byte[] requestBody;

  public CachedBodyHttpServletRequest(HttpServletRequest request) throws IOException {
    super(request);
    try (InputStream is = request.getInputStream();) {
      this.requestBody = StreamUtils.copyToByteArray(is);
    }
  }

  @Override
  public ServletInputStream getInputStream() {
    return new ServletInputStreamWrapper(this.requestBody);
  }

  @Override
  public BufferedReader getReader() {
    return new BufferedReader(new InputStreamReader(new ByteArrayInputStream(this.requestBody)));
  }

  public String read() throws IOException {
    BufferedReader reader = this.getReader();
    String line;
    StringBuilder payloadBuilder = new StringBuilder();
    while ((line = reader.readLine()) != null) {
      payloadBuilder.append(line).append(System.lineSeparator());
    }
    return payloadBuilder.toString();
  }

}

@Override
  public ServletInputStream getInputStream() {
    return new ServletInputStreamWrapper(this.requestBody);
  }

因为这里需要返回 ServletInputStream所以还需要包装一个类,因此有了ServletInputStreamWrapper。

 ServletInputStreamWrapper 包装了inputStream类,以便CachedBodyHttpServletRequest进行读取。

package com.veoride.mech.api.component;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import lombok.extern.slf4j.Slf4j;

/**
 * @author darmi
 */
@Slf4j
public class ServletInputStreamWrapper extends ServletInputStream {

  public static final int REACH_END = 0;

  private final InputStream inputStream;

  public ServletInputStreamWrapper(byte[] cachedBody) {
    this.inputStream = new ByteArrayInputStream(cachedBody);
  }

  @Override
  public boolean isFinished() {
    try {
      return inputStream.available() == REACH_END;
    } catch (IOException e) {
      log.error("isFinished fail", e);
    }
    return false;
  }

  @Override
  public boolean isReady() {
    return true;
  }

  @Override
  public void setReadListener(ReadListener readListener) {
    throw new UnsupportedOperationException();
  }

  @Override
  public int read() throws IOException {
    return inputStream.read();
  }
}

3.回调业务编写。

根据自己需要完成业务代码编写。

@RestController
@RequestMapping
@Slf4j
public class Controller {

  @PostMapping("/satisfaction/callback")
  public void satisfactionCallback(@RequestBody @Valid WebhookDTO webhookDTO) {
    ...
  }

}
package com.veoride.mech.api.dto.typeform;

import com.fasterxml.jackson.annotation.JsonAlias;
import javax.validation.constraints.NotNull;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

/**
 * @author darmi
 */
@Getter
@Setter
@ToString
public class WebhookDTO {

  @JsonAlias("form_response")
  @NotNull
  private FormResponse formResponse;

  public Long getPhone() {
    return this.formResponse.getHidden().getPhone();
  }

  @Getter
  @Setter
  @ToString
  public static class FormResponse {

    @NotNull
    private Hidden hidden;
  }

  @Getter
  @Setter
  @ToString
  public static class Hidden {

    @NotNull
    private Long phone;
  }


}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/673721.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

边缘计算开源平台

边缘计算 文章目录 边缘计算前言01 边缘计算开源平台概述02 面向物联网端的边缘计算开源平台2.1 EdgeXFoundry2.2 ApacheEdgent 03 面向边缘云的边缘计算开源平台3.1 CORD3.2 Akraino EdgeStack 04 面向云边融合的边缘计算开源平台05 构建边缘计算平台的开源软件06 结束语 前言…

类皮肤全属性凝胶电子皮肤

目前电子皮肤相关工作仅①提升单一或几个刺激感知属性(压力、温度等);②研究部分理化属性和感知属性的结合。但这些工作仅覆盖皮肤的两种或三种属性,距离实现皮肤般丰富的刺激感知感官和理化特性还有很大差距。而覆盖人类皮肤的所…

linux-virtualbox安装centOS7.9

一、windows10安装virtualbox 安装包->右键->以管理员身份运行->下一步 ->选择安装位置->下一步 ->是(网络中断?) ->是(安装一些辅助工具包) ->安装 ->完成 二、virtualbox新建centOS7.9虚…

【Python 基础篇】Python 文件操作

文章目录 导言一、文件操作的作用二、文件的基本操作1、打开文件2、读写文件① 读取文件② 写入文件 3、关闭文件 三、文件备份四、文件和文件夹的操作结语 导言 在编程领域中,文件操作是一项基础且常见的任务。无论是读取配置文件、处理数据文件,还是备…

MySQL保姆安装教程

文章目录 前言一、MySQL官网二、离线安装包步骤三、环境配置四、验证安装是否成功在这里插入图片描述 五、可视化工具连接总结 前言 对于第一次安装mysql是有点难度,现在我会把安装流程搞出来。 一、MySQL官网 MySQL Installer 8.0.33安装(不想安装这个…

Windows下redis的安装与使用

一、下载 redis.io 官网没有Windows版本的,需要去redis-windows下载。 二、使用与配置 2.1 解压安装 redis的Windows版本,有.msi和.zip两种格式。这里使用更为方便,直接解压就可以使用的.zip格式。 将Redis-x64-5.0.14.1.zip解压至D盘的…

【Java-SpringBoot+Vue+MySql】Day3.3-MybatisPlus说明与使用

目录 一、MybatisPlus快速入门 1、知识轰炸 2、实操演练 (1)新建项目 (2)添加依赖 (3)添加配置 (4)添加映射层 (5)启动类中添加注解 (6&a…

【通过Data Studio连接openGauss】---快速入门

【通过Data Studio连接openGauss】---快速入门 🔻 一、访问openGauss🔰 1.1 确认连接信息(单节点)🔰 1.2 使用gsql访问openGauss(本地连接数据库)🔰 1.3 使用gsql访问openGauss&…

玩机搞机------安卓手机分区操作中的的各种工具 提取分区 备份分区 檫除分区 推荐一

喜欢玩机搞机的朋友们经常对手机系统进行安装玩机操作。但有时候指令类输入和操作步骤比较繁琐。耽误时间。有些工具就比较方便操作。陆续会整理一些玩机搞机中的简单工具给爱好者使用。本期整理一些关于手机分区的工具推荐 💔💔💔 AB AVB分…

Android的详细介绍

目录: Android诞生 Android系统架构 Android系统版本 Android应用开发特色 Android诞生 2003年10月,Andy Rubin等人创建了Android公司,并组建了Android团队。 2005年8月17日,Google低调收购了成立仅22个月的高科技企…

【新星计划·2023】Linux目录结构

作者:Insist-- 个人主页:insist--个人主页 作者会持续更新网络知识和python基础知识,期待你的关注 前言 本文将讲解Linux目录结构与功能,以及目录解释,最近这段时间会持续更新关于Linux的基础知识,期待你的…

机器视觉初步6-1:基于梯度的图像分割

把基于梯度的图像分割单独拿出来。 文章目录 一、图像梯度相关算子的原理1. Sobel算子2. Prewitt算子3. Roberts算子 二、python和halcon算子实现1.python实现2.halcon实现 基于梯度的图像分割方法利用像素之间的梯度信息来进行图像分割。 梯度 1是图像中像素灰度值变化最快的…

macOS Monterey 12.6.7 (21G651) 正式版发布,ISO、IPSW、PKG 下载

macOS Monterey 12.6.7 (21G651) 正式版发布,ISO、IPSW、PKG 下载 本站下载的 macOS 软件包,既可以拖拽到 Applications(应用程序)下直接安装,也可以制作启动 U 盘安装,或者在虚拟机中启动安装。另外也支持…

macOS Big Sur 11.7.8 (20G1351) 正式版 ISO、PKG、DMG、IPSW 下载

macOS Big Sur 11.7.8 (20G1351) 正式版 ISO、PKG、DMG、IPSW 下载 本站下载的 macOS 软件包,既可以拖拽到 Applications(应用程序)下直接安装,也可以制作启动 U 盘安装,或者在虚拟机中启动安装。另外也支持在 Window…

Windows 10 |VMware开启虚拟化的最全面说明

前言: Windows作为工作机,对于计算机系的同学来说,主要是在于利用图形化的界面直观的创建虚拟机(典型的有代表性的是virtualbox和VMware这两家公司的桌面级虚拟化软件),尤其是小白这样的初学者&#xff0c…

吴恩达ChatGPT《Prompt Engineering》笔记

ChatGPT 提示词工程师教程 1. 课程介绍 1.1 ChatGPT 相关术语 LLM:Large Language Model,大语言模型 Instruction Tuned LLM:经过指令微调的大语言模型 Prompt:提示词 RLHF:Reinforcement Learning from Human F…

英特尔oneAPI人工智能黑客松 - 机器视觉挑战案例

写在前面:博主是一只经过实战开发历练后投身培训事业的“小山猪”,昵称取自动画片《狮子王》中的“彭彭”,总是以乐观、积极的心态对待周边的事物。本人的技术路线从Java全栈工程师一路奔向大数据开发、数据挖掘领域,如今终有小成…

K8S 生态周报| Kubernetes 公布两个全版本受影响的漏洞

“ 「K8S 生态周报」内容主要包含我所接触到的 K8S 生态相关的每周值得推荐的一些信息。欢迎订阅知乎专栏「k8s生态」[1]。 ” 大家好,我是张晋涛。 KIND v0.20.0 正式发布 KIND 是我一直参与,也日常一直在使用的项目,用于快速的在本地或者 C…

强化学习:AI领域的下一步里程碑

第一章:引言 近年来,人工智能(AI)的快速发展引起了全球范围内的广泛关注。在AI的众多技术领域中,强化学习(Reinforcement Learning)作为一种类似于人类学习的方式,在解决复杂问题方…

VMware虚拟机中安装Ubuntu20.04小白教程

安装Ubuntu20.04 1.Ubuntu镜像下载2.配置Ubuntu 2.1创建新的虚拟机,进入新建虚拟机向导2.2选择自定义类型配置2.3选择硬件兼容性2.4选择稍后安装操作系统2.5选择客户机操作系统2.6命名虚拟机2.7处理器配置2.8 虚拟机内存2.9配置网络类型2.10选择I/O控制器类型2.11选…