蚂蚁金服零号云客服遇到爬坑

8 篇文章 0 订阅


最近接一个sdk,也就是蚂蚁金服零号云客服的一个自定义卡片的功能

一、准备

  • 官方文档
  • 加密解密
  • jar依赖

二、代码

2.1、背景

卡片信息有两种展示,这里使用自带的接口方式展示吧

  • 客户端窗口
    在这里插入图片描述
  • 客服这边
    在这里插入图片描述
    此时自定义信息那边都是默认的信息,现在我们想要加上玩家id,玩家姓名和等级

步骤:出于安全考虑,阿里云那边采用加密的方式来传输用户信息。首先由后端java对访客的真实信息进行加密,然后将加密后的信息传给阿里云,再进行解密,最后再展示出解密后的访客信息。

相关依赖以及官方文档在 [这里]

https://help.aliyun.com/document_detail/68241.html?spm=a2c4g.11186623.6.585.1471525cV9FlSF

2.2、依赖

<!-- 这里是引入了本地的jar包 这个jar包可以联系云客服索要 -->
		<dependency>
			<groupId>com.alipay.fc.csplatform</groupId>
			<artifactId>fccsplatform-common-crypto</artifactId>
			<version>1.0.0.20161108</version>
			<scope>system</scope>
			<systemPath>${pom.basedir}/lib/fccsplatform-common-crypto-1.0.0.20161108.jar</systemPath>
		</dependency>
		<dependency>
			<groupId>com.google.guava</groupId>
			<artifactId>guava</artifactId>
			<version>19.0</version>
		</dependency>

pom.xml中再加上这个,免得打包的时候不能同时带上jar包

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <executions>
        <execution>
            <id>copy-dependencies</id>
            <phase>prepare-package</phase>
            <goals>
                <goal>copy-dependencies</goal>
            </goals>
            <configuration>
                <outputDirectory>${project.build.directory}/classes/lib</outputDirectory>
                <includeScope>compile</includeScope>
            </configuration>
        </execution>
    </executions>
</plugin>

2.3、代码

@CrossOrigin
@Controller
@RequestMapping("/api")
@Slf4j
public class GmExclusiveCustomerServicePopupApiController extends BaseController {


    // 公钥
//    @Value("${business_card_key}")
    private static String pub_key = "MIIBIjANBgkqhkiG9w********************* ";

    /**
     * 阿里云云客服URL
     */
    private static final String HOST_URL = "https://cschat.antcloud.com.cn/index.htm";


    /**
     * 租户 ID,由零号云客服提供
     */
    private static final String tntInstId = "*************";
    /**
     * 聊天窗编码,由零号云客服提供。(天天)
     */
    private static final String scene = "*************";

  /**
     * 对访客信息进行加密, 聊天窗埋点的时候, 生成埋点URL中的cinfo和key参数 使用此方法.
     *
     * @param request  用户名、用户信息
     * @param response url地址
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/encryptCard", method = RequestMethod.GET)
    public R encryptCard(HttpServletRequest request, HttpServletResponse response) {
        try {
            String userId = request.getParameter("userId") == null ? "" : request.getParameter("userId");
            String userName = request.getParameter("userName") == null ? "游客" : request.getParameter("userName");
            String vip = request.getParameter("vip") == null ? "" : request.getParameter("vip");
            log.info("userName玩家名称:" + userName);

            // 个人信息
            JSONObject extInfo = new JSONObject();
            extInfo.put("userId", userId);
            extInfo.put("userName", userName);
            extInfo.put("vip", vip);

            JSONObject cinfo = new JSONObject();
            cinfo.put("tel", "");
            cinfo.put("extInfo", extInfo);
            cinfo.put("userId", userId);
            cinfo.put("cardId", "");
            cinfo.put("timestamp", System.currentTimeMillis());



            //生成公钥和私钥,公钥加密,私钥解密
            log.info("1cinfo:" + cinfo);
            PublicKey publicKey = getPubKey();
            log.info("2publicKey:" + publicKey);
            Map<String, String> map = CustomerInfoCryptoUtil.encryptByPublicKey(cinfo.toString(), publicKey);
            log.info(cinfo.toString() + "\t加密后的字符串为:" + map);

            //样例输出, 由于加密含有随机内容, 每次输入不会相同
            StringBuilder stringBuilder = new StringBuilder(HOST_URL);
            stringBuilder.append("?tntInstId=" + tntInstId);
            stringBuilder.append("&scene=" + scene);
            stringBuilder.append("&key=" + map.get("key"));
            stringBuilder.append("&cinfo=" + map.get("text"));
            String url = stringBuilder.toString();
            log.info("url地址:" + url);
            return R.ok(url);
        } catch (Exception e) {
            log.error(e.toString());
            return R.error("服务器异常:"+e.toString());
        }
    }


    /**
     * 通过外部接口方式接入访客名片.
     * 工作台显示访客信息, 请使用此方法.
     * note : 本接口必须支持跨域访问.
     *
     * @param request  params消息体 和 key 公钥
     * @param response
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/decryptCard")
    public JSONObject decryptCard(HttpServletRequest request, HttpServletResponse response) {
        try {
            //这两个参数是云客服工作台调用该接口 post body带入的参数.
            String params = request.getParameter("params");
            String key = request.getParameter("key");
            log.error("1、回调参数params:"+ params + "key:" + key);
            log.error("2、回调参数key:"+ key);
            if (org.springframework.util.StringUtils.isEmpty(params) && org.springframework.util.StringUtils.isEmpty(key)) {
                log.error("名片参数为空!");
            }
            //还原出RSA公钥对象
            PublicKey publicKey = getPubKey();
            String cinfo = CustomerInfoCryptoUtil.decryptByPublicKey(URLEncoder.encode(params, "UTF-8"), URLEncoder.encode(key,"UTF-8"), publicKey);
            log.error("3、还原后的字符串为:" + cinfo);
            JSONObject jsonObject = JSONObject.parseObject(cinfo);
            JSONObject extInfo = jsonObject.getJSONObject("extInfo");
            String userId = extInfo.getString("userId");
            String userName = extInfo.getString("userName");
            String vip = extInfo.getString("vip");

            log.error("3、还原后的字符串为:" + cinfo);
            //查询到的用户信息按照下面格式拼凑成一个json格式内容, 返回. 请严格按照下面结构形态。note : 自定义参数要在schema中和result下面一一对应.
            String mockResult = "{\n" +
                    "  \"message\": \"查询成功\", //成功的消息\n" +
                    "  \"success\": true, //表示查询成功\n" +
                    "  \"result\": { //下面是自定义用户信息内容\n" +
                    "    \"userId\": \""+userName+"\",\n" +
                    "    \"userName\": \""+userId+"\",\n" +
                    "    \"vip\": \""+vip+"\",\n" +
                    "    \"schema\": { //会按照 schema 的结构来展示用户信息\n" +
                    "      \"properties\": {\n" +
                    "        \"userId\": {\n" +
                    "          \"name\": \"userId\",\n" +
                    "          \"type\": \"text\"\n" +
                    "        },\n" +
                    "        \"userName\": {\n" +
                    "          \"name\": \"userName\",\n" +
                    "          \"type\": \"text\"\n" +
                    "        },\n" +
                    "        \"vip\": {\n" +
                    "          \"name\": \"vip\",\n" +
                    "          \"type\": \"text\"\n" +
                    "        },\n" +
                    "      }\n" +
                    "    }\n" +
                    "  }\n" +
                    "}";
            log.error("返回的信息:"+mockResult);
            return JSONObject.parseObject(mockResult);
        } catch (Exception e) {
            //这里建议使用log记录错误. 减少干扰信息, DEMO里不使用LOG
            e.printStackTrace();
            return null;
        }
    }


    //还原出 RSA 公钥对象
    private static PublicKey getPubKey() throws Exception {
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64Util.decode(pub_key));
        KeyFactory keyFactory;
        keyFactory = KeyFactory.getInstance("RSA");
        PublicKey key = keyFactory.generatePublic(keySpec);
        return key;
    }

}

注意:该Controller中的第二个接口必须POST
该接口必须支持跨域请求。
接口协议必须是https。
响应头中 Content-Type 为 application/json; charset=utf-8。

另外:第一个解密信息放在cinfo,然后回调的时候必须用https并且post请求和网址形式。还有在回调的时候你获取的这个key实则他经过转换成私钥了,保证了公钥加密,私钥解密的原则

三、配置回调地址

在这里插入图片描述
在这里插入图片描述

四、测试

在这里插入图片描述

基金网址:xiu.idearyou.cn
后言
本文章来自自己的博客:纯洁的麦田
公众号:纯洁的麦田
百度:纯洁的麦田
新鲜的面试宝典和技术分享和教你玩基金赚钱
欢迎收藏访问

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值