天猫精灵与公司智能设备的对接。刚接到这个任务的时候对于一个刚毕业一个月的应届生我来说,有点像巨石,不过经历了10多天的自己琢磨,还是终于把这个新东西搞出来了。楼主是学的java,但在查询相关文章的时候发现没有一篇是用java实现的。所以在这里写一篇关于实现的文章阐述一下这个对接里面所需要的一些细节以及,实现流程。首先我们需要了解的是Aligenie开发者平台。
https://open.bot.tmall.com/
这里有必要贴一张整个接入的流程图
这个平台的登录可以用淘宝账号登录。登录后
我们需要先进行技能的创建,点击添加新技能
技能信息这一栏信息我就不做过多的阐述自己随便填一下就是了。作为测试取名Test或者什么其他的都OK。填写完成后点击下一步:
这个页面对开发者来说真的可谓是重中之重。
第一栏账户授权链接应填写我们开发者为Aligenie平台提供的授权登录页面(自己编写)
第二栏,第三栏Client ID和Client Secret这里设置的作用就是平台识别你新建技能用的可以自己随便填写。
Aceess Token URL这个地址需要解释一下官方文档中标识如下:
这个地址其实就是当你完成授权登录这一步后会返回一个code值,然后将code值添加进OAuth2.0请求中将整个请求转发给
你填写AccessTokenURL方法接口中去。通过传递的code换取访问令牌
https://XXXXX/token?grant_type=authorization_code&client_id=XXXXX&client_secret=XXXXXX&code=XXXXXXXX&redirect_uri=https%3A%2F%2Fopen.bot.tmall.com%2Foauth%2Fcallback
最后一个必填项 开发者网关地址是当平台接受到你的返回的AccessToken后要将平台的智能家居协议发送到的一个接口。该接口用来接收协议然后按照官方文档给出的返回json体的格式返回你要接入的智能设备的一些参数。
上述的设备发现请求就是平台向网关地址中Post协议。而下面的就是你需要返回的东西。注册技能的地址填写就这么几个要点。
这边填写完后先不用上线什么的先做一个本地的测试再说。填完后我们返回技能主页会出现该技能的图标点进去。这时我们需要测试自己的技能,点击账户配置会跳转到你所写的登录页面。登录页的账号密码填写公司中存储的用户的账号密码就可。
当跳转到这个界面时我们看网址信息
http://www.XXXXX.com/test2/merchantHTML/merchantlogin.jsp?redirect_uri=https%3A%2F%2Fopen.bot.tmall.com%2Foauth%2Fcallback%3FskillId%3D18105%26token%3DMjM0MDgzODYwMEFGRUhJTkZEVlE%3D&client_id=hbsycjn2010&response_type=code&state=0.5252341172795741
从redirect后的信息都是平台为我们自己加上的。登录成功后我们需要将请求转发给我们生成Code并生成与Code对应的AcessToken的这个接口中为平台反馈AcessToken。
我这里用的是SSM框架实现的
userService.java
package com.obj.service;import javax.annotation.Resource;import org.springframework.stereotype.Service;import com.obj.dao.userDao;
import com.obj.entity.user;@Service
public class userService {@Resourceprivate userDao userdao;public user UserLogin(String username,String password){return userdao.UserLogin(username, password);}public user IdentifyUsername(String username){return userdao.IdentifyUsername(username);}public user IdentifyPassword(String username,String password){return userdao.IdentifyPassword(username, password);}
}
userDao.java
package com.obj.dao;
import com.obj.entity.*;
public interface userDao {public user UserLogin(String username,String password);public user IdentifyUsername(String username);public user IdentifyPassword(String username,String password);}
userMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.obj.dao.userDao"> <resultMap type="user" id="userResult"> <result property="Id" column="Id"/> <result property="username" column="username"/> <result property="password" column="password"/> </resultMap> <select id="UserLogin" parameterType="String" resultType="user"> select * from user where user=#{0} and password=#{1} </select> <select id="IdentifyUsername" parameterType="String" resultType="user"> select * from user where user=#{0}; </select> <select id="IdentifyPassword" parameterType="String" resultType="user"> select * from user where user=#{0} and password=#{1} </select>
</mapper>
testController.java
package com.obj.controller;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.UUID;import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;import org.apache.oltu.oauth2.as.issuer.MD5Generator;
import org.apache.oltu.oauth2.as.issuer.OAuthIssuer;
import org.apache.oltu.oauth2.as.issuer.OAuthIssuerImpl;
import org.apache.oltu.oauth2.as.request.OAuthAuthzRequest;
import org.apache.oltu.oauth2.as.request.OAuthTokenRequest;
import org.apache.oltu.oauth2.as.response.OAuthASResponse;
import org.apache.oltu.oauth2.client.OAuthClient;
import org.apache.oltu.oauth2.client.URLConnectionClient;
import org.apache.oltu.oauth2.client.request.OAuthBearerClientRequest;
import org.apache.oltu.oauth2.client.request.OAuthClientRequest;
import org.apache.oltu.oauth2.client.response.OAuthAccessTokenResponse;
import org.apache.oltu.oauth2.client.response.OAuthResourceResponse;
import org.apache.oltu.oauth2.common.OAuth;
import org.apache.oltu.oauth2.common.error.OAuthError;
import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
import org.apache.oltu.oauth2.common.message.OAuthResponse;
import org.apache.oltu.oauth2.common.message.types.GrantType;
import org.apache.oltu.oauth2.common.message.types.ResponseType;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.obj.entity.application;
import com.obj.entity.user;
import com.obj.service.applicationService;
import com.obj.service.userService;@Controller
@RequestMapping("/Test")
public class testController {@Resourcepublic userService userservice;@Resourcepublic applicationService applicationservice;String grant_type = "authorization";String clientId ="xxxxx";String clientSecret = "xxxxx";String accessTokenUrl = "http://www.xxxx.com:8081/index.jsp";String userInfoUrl = null;String redirectUrl = "https://open.bot.tmall.com/oauth/callback";String response_type = "code";String code= null;String cutURL = "http://www.xxxx.com:8081/test2/merchantHTML/merchantlogin.jsp?";String OAuthURL = "http://www.xxxx.com:8081/test2/Test/responseCode.do?";int cutlength = cutURL.length();@RequestMapping("/userlogin")public String userlogin(HttpServletRequest request) throws IOException, OAuthSystemException{String url = request.getHeader("referer");System.out.println("请求跳转地址:"+url);String username = request.getParameter("username");String password = request.getParameter("password");user IdentifyUsername = userservice.IdentifyUsername(username);user IdentifyPassword = userservice.IdentifyPassword(username, password);if(IdentifyUsername != null ){if(IdentifyPassword != null){System.out.println("登录成功!");String outURL = java.net.URLDecoder.decode(url, "GBK");System.out.println("decode后跳转的地址:"+outURL);int outlength = outURL.length();String responseURL = outURL.substring(cutlength, outlength);System.out.println(responseURL);OAuthURL = OAuthURL + responseURL;System.out.println("decode后要跳转的地址:"+OAuthURL);return "redirect:" + OAuthURL;}else{System.out.println("密码错误!");}}else{System.out.println("用户名不存在!");}return "redirect:/";}@RequestMapping("/responseCode")public Object toShowUser(Model model, HttpServletRequest request) throws IOException{System.out.println("----------服务端/responseCode--------------------------------------------------------------");try {//构建OAuth 授权请求 OAuthAuthzRequest oauthRequest = new OAuthAuthzRequest(request); oauthRequest.getClientId();oauthRequest.getResponseType();oauthRequest.getRedirectURI();String token = oauthRequest.getParam("token");System.out.println(oauthRequest.getClientId());System.out.println(oauthRequest.getResponseType());System.out.println(oauthRequest.getRedirectURI());System.out.println(oauthRequest.getParam("token"));if(oauthRequest.getClientId()!=null&&oauthRequest.getClientId()!=""){//设置授权码 String authorizationCode = UUID.randomUUID().toString().replace("-", "").substring(0, 18);System.out.println(authorizationCode);//利用oauth授权请求设置responseType,目前仅支持CODE,另外还有TOKEN String responseType = oauthRequest.getParam(OAuth.OAUTH_RESPONSE_TYPE);//进行OAuth响应构建OAuthASResponse.OAuthAuthorizationResponseBuilder builder =OAuthASResponse.authorizationResponse(request, HttpServletResponse.SC_FOUND);//设置授权码builder.setParam("token", token); builder.setParam("state","11");builder.setCode(authorizationCode);//得到到客户端重定向地址String redirectURI = oauthRequest.getParam(OAuth.OAUTH_REDIRECT_URI);//构建响应final OAuthResponse response = builder.location(redirectURI).buildQueryMessage();System.out.println("服务端/responseCode内,返回的回调路径:"+response.getLocationUri());System.out.println("----------服务端/responseCode--------------------------------------------------------------");String responceUri =response.getLocationUri();System.out.println(responceUri);//根据OAuthResponse返回ResponseEntity响应HttpHeaders headers = new HttpHeaders();try {headers.setLocation(new URI(response.getLocationUri()));} catch (URISyntaxException e) {// TODO Auto-generated catch blocke.printStackTrace();}String strURL = "http://www.xxxx.com/test2/Test/responseAccessToken.do?grant_type=authorization_code&client_id=hbsycjn2010&client_secret=ainipinxin&redirect_uri=https://open.bot.tmall.com/oauth/callback&code=" + authorizationCode;URL url = new URL(strURL);HttpURLConnection connection = (HttpURLConnection) url.openConnection();connection.setDoOutput(true);connection.setDoInput(true);connection.setUseCaches(false);connection.setInstanceFollowRedirects(true);connection.setRequestMethod("POST"); // 设置请求方式connection.setRequestProperty("Accept", "application/json"); // 设置接收数据的格式connection.setRequestProperty("Content-Type", "application/json"); // 设置发送数据的格式connection.connect(); return "redirect:"+responceUri;// https://open.bot.tmall.com/oauth/callback?skillId=18105&code=0b58444322e04d9c8e&state=11&token=MjM0MDgzODYwMEFGRUhJTkZEVlE%3D}} catch (OAuthSystemException e) {e.printStackTrace();} catch (OAuthProblemException e) {e.printStackTrace();}System.out.println("----------服务端/responseCode--------------------------------------------------------------");return null; }@RequestMapping(value = "/responseAccessToken",method = RequestMethod.POST) public HttpEntity token(HttpServletRequest request) throws OAuthSystemException{JSONObject jsonObject = new JSONObject();System.out.println("--------服务端/responseAccessToken-----------------------------------------------------------");OAuthIssuer oauthIssuerImpl=null;OAuthResponse response=null;//构建OAuth请求 try {OAuthTokenRequest oauthRequest = new OAuthTokenRequest(request);String authCode = oauthRequest.getParam(OAuth.OAUTH_CODE); String clientSecret = oauthRequest.getClientSecret();if(clientSecret!=null||clientSecret!=""){//生成Access TokenoauthIssuerImpl = new OAuthIssuerImpl(new MD5Generator());final String accessToken = oauthIssuerImpl.accessToken();final String refreshToken = oauthIssuerImpl.refreshToken();jsonObject.put("access_token", accessToken);jsonObject.put("refresh_token", refreshToken);jsonObject.put("expires_in", 1760000);System.out.println(jsonObject.toString());System.out.println("--oooo---");//生成OAuth响应response = OAuthASResponse.tokenResponse(HttpServletResponse.SC_OK).setAccessToken(accessToken).setRefreshToken(refreshToken).setParam("expires_in", "17600000").buildJSONMessage();System.out.println(response.getBody());}System.out.println("--------服务端/responseAccessToken-----------------------------------------------------------");//根据OAuthResponse生成ResponseEntityreturn new ResponseEntity(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));} catch (OAuthSystemException e) {response = OAuthASResponse.tokenResponse(HttpServletResponse.SC_OK).setParam("error", "101").setParam("error_description", "内部错误").buildJSONMessage();// TODO Auto-generated catch blockjsonObject.put("error", 101);jsonObject.put("error_dercription", "内部错误");System.out.println(jsonObject.toString());return new ResponseEntity(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));} catch (OAuthProblemException e) {// TODO Auto-generated catch blockresponse = OAuthASResponse.tokenResponse(HttpServletResponse.SC_OK).setParam("error", "102").setParam("error_description", "参数错误").buildJSONMessage();jsonObject.put("error", 102);jsonObject.put("error_dercription", "参数错误");System.out.println(jsonObject.toString());return new ResponseEntity(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));}// System.out.println("--------服务端/responseAccessToken-----------------------------------------------------------");// return null;}@RequestMapping(value ="/getMessage",method = RequestMethod.POST)@ResponseBodypublic JSONObject getMessage(HttpServletRequest request, HttpServletResponse response,BufferedReader br){//System.out.println("hi");//Header部分JSONObject MerchineList = new JSONObject();JSONArray jSONArray = new JSONArray();JSONObject header = new JSONObject();JSONObject payload = new JSONObject();List<JSONObject> devices = new ArrayList();List<JSON> properties = new ArrayList();List actions = new ArrayList();JSONObject extentions = new JSONObject();System.out.print(request.getHeaderNames());Enumeration<?> enum1 = request.getHeaderNames();while (enum1.hasMoreElements()) {String key = (String) enum1.nextElement();String value = request.getHeader(key);System.out.println(key + "\t" + value);}//body部分String inputLine;String str = "";try {while ((inputLine = br.readLine()) != null) {str += inputLine;}br.close();} catch (IOException e) {System.out.println("IOException: " + e);}System.out.println("str:" + str);JSONObject recieveHeader = new JSONObject();recieveHeader = JSON.parseObject(str);String str1 = recieveHeader.getString("header");System.out.println("header:" + recieveHeader.getString("header"));JSONObject recieveMessageId = new JSONObject();recieveMessageId = JSON.parseObject(str1);System.out.println("messageId:" + recieveMessageId.getString("messageId"));header.put("namespace", "AliGenie.Iot.Device.Discovery");header.put("name", "DiscoveryDevicesResponse");header.put("messageId", recieveMessageId.getString("messageId"));header.put("payLoadVersion", "1");JSONObject device = new JSONObject();JSONObject propertie = new JSONObject();device.put("deviceId", "34ea34cf2e63");device.put("deviceName", "单孔插座");device.put("deviceType", "outlet");device.put("zone", "test");device.put("brand", "test");device.put("model", "test");device.put("icon", "https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1531878000&di=c989660f4b827a0049c3b7aec4fe38e1&src=http://img.czvv.com/sell/599adfe4d2f0b1b2f118606f/20170905113247194.jpg");propertie.put("name", "powerstate");propertie.put("value", "off");properties.add(propertie);device.put("properties", properties);actions.add("TurnOn");actions.add("TurnOff");device.put("actions", actions);extentions.put("extension1", "tset");extentions.put("extension2", "test");device.put("extentions", extentions);devices.add(device);payload.put("devices", devices);MerchineList.put("header", header);MerchineList.put("payload", payload);System.out.println(MerchineList.toString());return MerchineList;}}
点击登录并授权,如果在生成CODE,通过生成的CODE换取AccessToken返回给平台这几步上都没有差错则会跳转到一下界面。
此时说明已经将智能设备接入到了平台中。这是你可以操作自己的天猫精灵,说打开单孔插座,天猫精灵就会做出相应的反应同样会发送一条JSON数据段给你的开发者网关地址。以上便是整个接入的过程。
web开发的小伙伴在第一次跳转到这个界面的时候可能会出现您还没有智能设备接入的空白界面。不要急着质疑是不是自己的代码写错了。这时你可以打开手机上的天猫精灵APP在App中点击智能设备看看有没有相关的智能设备接入进去,如果没有这时再返回去仔细的检查代码。