玩转亚马逊 AWS IoT(3): SpringBoot 2.7 集成 AWS IoT 服务

1 AWSIoT 开发者文档集合

Amazon IoT Core 开发人员指南(设备端)

Amazon IoT Device SDK(设备端)

AWS IoT Core API 参考(服务端)

AWS SDK for Java 2.0(服务端)

服务端主要是定义 IoT相关的权限、物品类型、物品等功能;设备端主要是收发MQTT消息功能

准备工作:

玩转亚马逊 AWS IoT(1): IoT 业务梳理

玩转亚马逊 AWS IoT(2): IoT 控制台使用与开发操作文档

2 核心 Maven 依赖

./demo-aws-iot/pom.xml
        <!-- AWS iot server sdk -->
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>iot</artifactId>
            <version>${aws-iot-server.version}</version>
        </dependency>

        <!-- AWS iot device sdk -->
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-iot-device-sdk-java</artifactId>
            <version>${aws-iot-sdk-device.version}</version>
        </dependency>

其中依赖版本信息为:

        <aws-iot-server.version>2.17.230</aws-iot-server.version>
        <aws-iot-sdk-device.version>1.3.10</aws-iot-sdk-device.version>

3 IoT 配置信息

配置类

./demo-aws-iot/src/main/java/com/ljq/demo/springboot/aws/iot/common/config/AwsIotAccountConfig.java
package com.ljq.demo.springboot.aws.iot.common.config;

import lombok.Getter;
import lombok.ToString;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

/**
 * @Description: 亚马逊 iot 服务账号配置类
 * @Author: junqiang.lu
 * @Date: 2022/7/25
 */
@Getter
@ToString
@Configuration
public class AwsIotAccountConfig {

    /**
     * 终端节点
     */
    @Value(value = "${aws.iot.clientEndpoint}")
    private String clientEndpoint;

    /**
     * 接入公钥
     */
    @Value(value = "${aws.iot.accessKeyId}")
    private String accessKeyId;

    /**
     * 接入私钥
     */
    @Value(value = "${aws.iot.secretAccessKey}")
    private String secretAccessKey;

}

application.yml 配置

./demo-aws-iot/src/main/resources/application.yml
## aws iot config
aws:
  iot:
    clientEndpoint: xxxx.iot.cn-northwest-1.amazonaws.com.cn
    accessKeyId: xxxxx
    secretAccessKey: xxxxxxxx

4 核心代码

4.1 服务端

权限策略、证书、物品等操作工具类

工具类包含的 IoT操作方法: 创建物品类型、创建物品、创建证书、绑定物品与证书、创建策略、绑定证书与策略、更新策略

./demo-aws-iot/src/main/java/com/ljq/demo/springboot/aws/iot/server/util/AwsIotServerClientComponent.java
package com.ljq.demo.springboot.aws.iot.server.util;

import com.ljq.demo.springboot.aws.iot.common.config.AwsIotAccountConfig;
import com.ljq.demo.springboot.aws.iot.server.vo.CertificateVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProviderChain;
import software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider;
import software.amazon.awssdk.profiles.ProfileFile;
import software.amazon.awssdk.profiles.ProfileProperty;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.iot.IotClient;
import software.amazon.awssdk.services.iot.model.*;
import software.amazon.awssdk.utils.StringInputStream;

import java.util.Objects;

/**
 * @Description: AWS iot 服务端工具类
 * @Author: junqiang.lu
 * @Date: 2022/7/21
 */
@Slf4j
@Component
public class AwsIotServerClientComponent {

    private AwsIotServerClientComponent(){
    }

    @Autowired
    private AwsIotAccountConfig iotAccountConfig;

    private IotClient iotClient;

    /**
     * 初始化 iot 客户端
     *
     * @return
     */
    @Bean(value = "iotServerClient")
    public IotClient iotClient() {
        StringBuilder cfgBuilder = new StringBuilder("[default]\n");
        cfgBuilder.append(ProfileProperty.AWS_ACCESS_KEY_ID).append(" = ").append(iotAccountConfig.getAccessKeyId())
                .append("\n");
        cfgBuilder.append(ProfileProperty.AWS_SECRET_ACCESS_KEY).append(" = ").append(iotAccountConfig.getSecretAccessKey())
                .append("\n");
        ProfileFile profileFile = ProfileFile.builder()
                .content(new StringInputStream(cfgBuilder.toString()))
                .type(ProfileFile.Type.CONFIGURATION).build();
        AwsCredentialsProviderChain awsCredentialsProviderChain = AwsCredentialsProviderChain.of(
                ProfileCredentialsProvider.builder().profileFile(profileFile).build());
        if (Objects.isNull(iotClient)) {
            synchronized (AwsIotServerClientComponent.class) {
                if (Objects.isNull(iotClient)) {
                    iotClient = IotClient.builder()
                            .credentialsProvider(awsCredentialsProviderChain)
                            .region(Region.CN_NORTHWEST_1)
                            .build();
                }
            }
        }
        return iotClient;
    }

    /**
     * 创建物品类型
     *
     * @param thingType 物品类型
     */
    public boolean createThingType(String thingType) {
        Tag tag = Tag.builder().key("type").value(thingType).build();
        CreateThingTypeRequest request = CreateThingTypeRequest.builder()
                .thingTypeName(thingType)
                .tags(tag).build();
        CreateThingTypeResponse response = iotClient.createThingType(request);
        return response.sdkHttpResponse().isSuccessful();
    }

    /**
     * 创建物品
     *
     * @param thingName 物品名称
     * @param thingType 物品类型
     * @return
     */
    public boolean createThing(String thingName, String thingType) {
        CreateThingRequest request = CreateThingRequest.builder()
                .thingName(thingName)
                .thingTypeName(thingType).build();
        CreateThingResponse response = iotClient.createThing(request);
        return response.sdkHttpResponse().isSuccessful();
    }

    /**
     * 创建证书
     *
     * @return
     */
    public CertificateVo createCert() {
        CreateKeysAndCertificateRequest request = CreateKeysAndCertificateRequest.builder().setAsActive(true).build();
        CreateKeysAndCertificateResponse response = iotClient.createKeysAndCertificate(request);
        if (response.sdkHttpResponse().isSuccessful()) {
            CertificateVo certVo = new CertificateVo();
            certVo.setCertificateArn(response.certificateArn());
            certVo.setCertificateId(response.certificateId());
            certVo.setCertificatePem(response.certificatePem());
            certVo.setPublicKey(response.keyPair().publicKey());
            certVo.setPrivateKey(response.keyPair().privateKey());
            return certVo;
        }
        return null;
    }

    /**
     * 绑定物品与证书
     *
     * @param certArn 证书资源唯一标识
     * @param thingId 物品 ID
     * @return
     */
    public boolean bindThingAndCert(String certArn, String thingId) {
        AttachThingPrincipalRequest request = AttachThingPrincipalRequest.builder()
                .thingName(thingId)
                .principal(certArn).build();
        AttachThingPrincipalResponse response = iotClient.attachThingPrincipal(request);
        return response.sdkHttpResponse().isSuccessful();
    }

    /**
     * 创建策略
     *
     * @param policyName 策略名称
     * @param policyContent 策略内容(json 格式)
     * @return
     */
    public boolean createPolicy(String policyName, String policyContent) {
        CreatePolicyRequest request = CreatePolicyRequest.builder()
                .policyName(policyName)
                .policyDocument(policyContent).build();
        CreatePolicyResponse response = iotClient.createPolicy(request);
        return response.sdkHttpResponse().isSuccessful();
    }

    /**
     * 绑定证书与策略
     *
     * @param certArn
     * @param policyName
     * @return
     */
    public boolean bindCertAndPolicy(String certArn, String policyName) {
        AttachPolicyRequest request = AttachPolicyRequest.builder()
                .policyName(policyName)
                .target(certArn)
                .build();
        AttachPolicyResponse response = iotClient.attachPolicy(request);
        return response.sdkHttpResponse().isSuccessful();
    }

    /**
     * 更新策略
     *
     * @param policyName
     * @param policyContent
     * @return
     */
    public boolean updatePolicy(String policyName, String policyContent) {
        // 查询策略的所有版本
        ListPolicyVersionsRequest listPolicyVersionsRequest = ListPolicyVersionsRequest.builder()
                .policyName(policyName).build();
        ListPolicyVersionsResponse listPolicyVersionsResponse = iotClient.listPolicyVersions(listPolicyVersionsRequest);
        if (!listPolicyVersionsResponse.sdkHttpResponse().isSuccessful()) {
            log.warn("删除策略失败,查询策略列表出错");
            return false;
        }
        if (CollectionUtils.isEmpty(listPolicyVersionsResponse.policyVersions())) {
            log.warn("删除策略失败,策略列表为空");
            return false;
        }
        // 删除非活跃版本
        listPolicyVersionsResponse.policyVersions().forEach(version -> {
            if (!version.isDefaultVersion()) {
                DeletePolicyVersionRequest deletePolicyVersionRequest = DeletePolicyVersionRequest.builder()
                        .policyName(policyName)
                        .policyVersionId(version.versionId()).build();
                iotClient.deletePolicyVersion(deletePolicyVersionRequest);
            }
        });
        // 创建策略版本并设置为活跃状态
        CreatePolicyVersionRequest request = CreatePolicyVersionRequest.builder()
                .policyName(policyName)
                .policyDocument(policyContent)
                .setAsDefault(true).build();
        CreatePolicyVersionResponse response = iotClient.createPolicyVersion(request);
        return response.sdkHttpResponse().isSuccessful();
    }

}
4.2 设备端
4.2.1 MQTT 消息推送类
./demo-aws-iot/src/main/java/com/ljq/demo/springboot/aws/iot/device/pubsub/IotPublisher.java
package com.ljq.demo.springboot.aws.iot.device.pubsub;

import com.amazonaws.services.iot.client.AWSIotMessage;
import com.amazonaws.services.iot.client.AWSIotQos;
import lombok.extern.slf4j.Slf4j;

/**
 * @Description: AWS iot 消息推送类
 * @Author: junqiang.lu
 * @Date: 2022/7/21
 */
@Slf4j
public class IotPublisher extends AWSIotMessage {

    public IotPublisher(String topic, AWSIotQos qos, byte[] payload) {
        super(topic, qos, payload);
    }

    @Override
    public void onSuccess() {
        log.info("[MQTT消息推送-Success]topic: {},data: {}",topic, getStringPayload());
        // 业务处理
    }

    @Override
    public void onFailure() {
        log.info("[MQTT消息推送-Error]topic: {},data: {}",topic, getStringPayload());
        // 业务处理
    }

    @Override
    public void onTimeout() {
        log.info("[MQTT消息推送-time out]topic: {},data: {}",topic, getStringPayload());
        // 业务处理
    }

}
4.2.2 MQTT 消息监听类
./demo-aws-iot/src/main/java/com/ljq/demo/springboot/aws/iot/device/pubsub/IotDemoListener.java
package com.ljq.demo.springboot.aws.iot.device.pubsub;

import com.amazonaws.services.iot.client.AWSIotMessage;
import com.amazonaws.services.iot.client.AWSIotQos;
import com.amazonaws.services.iot.client.AWSIotTopic;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * @Description: AWS iot 主题监听类
 * @Author: junqiang.lu
 * @Date: 2022/7/21
 */
@Slf4j
@Component
public class IotDemoListener extends AWSIotTopic {

    private static final String TOPIC = "hello/demo/xxx";

    public IotDemoListener() {
        super(TOPIC, AWSIotQos.QOS1);
    }

    @Override
    public void onMessage(AWSIotMessage message) {
        String content = message.getStringPayload();
        log.info("message content: {}", content);
        // 业务处理
    }
}
./demo-aws-iot/src/main/java/com/ljq/demo/springboot/aws/iot/device/pubsub/IotConnectEventListener.java
package com.ljq.demo.springboot.aws.iot.device.pubsub;

import com.amazonaws.services.iot.client.AWSIotMessage;
import com.amazonaws.services.iot.client.AWSIotQos;
import com.amazonaws.services.iot.client.AWSIotTopic;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * @Description: AWS iot 设备上下线事件监听类
 * @Author: junqiang.lu
 * @Date: 2022/7/21
 */
@Slf4j
@Component
public class IotConnectEventListener extends AWSIotTopic {

    private static final String TOPIC = "hello/demo/connect/event";

    public IotConnectEventListener() {
        super(TOPIC, AWSIotQos.QOS1);
    }

    @Override
    public void onMessage(AWSIotMessage message) {
        String content = message.getStringPayload();
        log.info("设备上下线通知: {}", content);
        // 业务处理

    }
}
4.2.3 设备端操作工具类
./demo-aws-iot/src/main/java/com/ljq/demo/springboot/aws/iot/device/util/AwsIotDeviceClientComponent.java
package com.ljq.demo.springboot.aws.iot.device.util;

import com.amazonaws.regions.Regions;
import com.amazonaws.services.iot.client.AWSIotException;
import com.amazonaws.services.iot.client.AWSIotMqttClient;
import com.amazonaws.services.iot.client.AWSIotQos;
import com.amazonaws.services.iot.client.auth.Credentials;
import com.amazonaws.services.iot.client.auth.StaticCredentialsProvider;
import com.ljq.demo.springboot.aws.iot.common.config.AwsIotAccountConfig;
import com.ljq.demo.springboot.aws.iot.device.pubsub.IotConnectEventListener;
import com.ljq.demo.springboot.aws.iot.device.pubsub.IotDemoListener;
import com.ljq.demo.springboot.aws.iot.device.pubsub.IotPublisher;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

/**
 * @Description: AWS iot 设备端 MQTT 操作工具类
 * @Author: junqiang.lu
 * @Date: 2022/7/21
 */
@Slf4j
@Component
public class AwsIotDeviceClientComponent implements ApplicationRunner {

    public AwsIotDeviceClientComponent(){
    }

    public static final String CLIENT_ID = "demo-server-202207";

    @Autowired
    private AwsIotAccountConfig iotAccountConfig;

    private AWSIotMqttClient mqttClient;

    @Autowired
    private IotDemoListener iotDemoListener;
    @Autowired
    private IotConnectEventListener iotConnectEventListener;


    @Override
    public void run(ApplicationArguments args) throws Exception {
        mqttClient.connect();
        log.info("aws mqtt server 客户端连接成功");
        // 主题订阅
        mqttClient.subscribe(iotDemoListener, true);
        mqttClient.subscribe(iotConnectEventListener, true);
    }

    @Bean(name = "awsIotMqttClient")
    public AWSIotMqttClient awsIotMqttClient() {
        log.info("创建 awsIotMqttClient bean");
        Credentials credentials = new Credentials(iotAccountConfig.getAccessKeyId(),
                iotAccountConfig.getSecretAccessKey());
        StaticCredentialsProvider credentialsProvider = new StaticCredentialsProvider(credentials);
        mqttClient = new AWSIotMqttClient(iotAccountConfig.getClientEndpoint(), CLIENT_ID, credentialsProvider,
                Regions.CN_NORTHWEST_1.getName());
        return mqttClient;
    }

    /**
     * 消息推送
     * @param topic
     * @param data
     * @throws AWSIotException
     */
    public void pushMessage(String topic, byte[] data) throws AWSIotException {
        log.info("server topic={},>>> {}", topic, new String(data));
        mqttClient.publish(new IotPublisher(topic, AWSIotQos.QOS1, data));
    }

}

5 测试

接口示例:

创建策略

POST
http://127.0.0.1:9000/api/iot/server/policy/create

请求参数:

{
    "policyName": "testPolicy",
    "policyContent": "{ \"Version\": \"2012-10-17\", \"Statement\": [ { \"Effect\": \"Allow\", \"Action\": \"iot:Connect\", \"Resource\": \"arn:aws-cn:iot:cn-northwest-1:xxxxx:client/${iot:Connection.Thing.ThingName}\" }, { \"Effect\": \"Allow\", \"Action\": \"iot:Publish\", \"Resource\": \"arn:aws-cn:iot:cn-northwest-1:xxxxx:topic/hello/demo/xxx\" }, { \"Effect\": \"Allow\", \"Action\": \"iot:Subscribe\", \"Resource\": \"arn:aws-cn:iot:cn-northwest-1:xxxxx:topicfilter/hello/demo/client/${iot:Connection.Thing.ThingName}\" }, { \"Effect\": \"Allow\", \"Action\": \"iot:Receive\", \"Resource\": \"*\" } ] }"
}

其他接口参考源码:

服务端:

./demo-aws-iot/src/main/java/com/ljq/demo/springboot/aws/iot/server/controller/IotServerDemoController.java
package com.ljq.demo.springboot.aws.iot.server.controller;

import com.ljq.demo.springboot.aws.iot.server.param.*;
import com.ljq.demo.springboot.aws.iot.server.util.AwsIotServerClientComponent;
import com.ljq.demo.springboot.aws.iot.server.vo.CertificateVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

/**
 * @Description: iot 服务端示例控制层
 * @Author: junqiang.lu
 * @Date: 2022/7/25
 */
@Slf4j
@RestController
@RequestMapping(value = "/api/iot/server")
public class IotServerDemoController {

    @Autowired
    private AwsIotServerClientComponent iotClientComponent;

    /**
     * 创建策略
     *
     * @param createPolicyParam
     * @return
     */
    @PostMapping(value = "/policy/create", produces = {MediaType.APPLICATION_JSON_VALUE})
    public ResponseEntity<Object> createPolicy(@RequestBody IotServerCreatePolicyParam createPolicyParam) {
        boolean flag = iotClientComponent.createPolicy(createPolicyParam.getPolicyName(),
                createPolicyParam.getPolicyContent());
        return ResponseEntity.ok(flag);
    }

    /**
     * 更新策略
     *
     * @param updatePolicyParam
     * @return
     */
    @PutMapping(value = "/policy/update", produces = {MediaType.APPLICATION_JSON_VALUE})
    public ResponseEntity<Object> updatePolicy(@RequestBody IotServerUpdatePolicyParam updatePolicyParam) {
        boolean flag = iotClientComponent.updatePolicy(updatePolicyParam.getPolicyName(),
                updatePolicyParam.getPolicyContent());
        return ResponseEntity.ok(flag);
    }

    /**
     * 创建物品类型
     *
     * @param createThingTypeParam
     * @return
     */
    @PostMapping(value = "/thingType/create", produces = {MediaType.APPLICATION_JSON_VALUE})
    public ResponseEntity<Object> createThingType(@RequestBody IotServerCreateThingTypeParam createThingTypeParam) {
        boolean flag = iotClientComponent.createThingType(createThingTypeParam.getThingTypeName());
        return ResponseEntity.ok(flag);
    }

    /**
     * 创建物品
     *
     * @param createThingParam
     * @return
     */
    @PostMapping(value = "/thing/create", produces = {MediaType.APPLICATION_JSON_VALUE})
    public ResponseEntity<Object> createThing(@RequestBody IotServerCreateThingParam createThingParam) {
        boolean flag = iotClientComponent.createThing(createThingParam.getThingName(),
                createThingParam.getThingTypeName());
        return ResponseEntity.ok(flag);
    }

    /**
     * 创建证书
     *
     * @param createCertParam
     * @return
     */
    @PostMapping(value = "/cert/create", produces = {MediaType.APPLICATION_JSON_VALUE})
    public ResponseEntity<Object> createCert(@RequestBody IotServerCreateCertParam createCertParam) {
        CertificateVo cert = iotClientComponent.createCert();
        return ResponseEntity.ok(cert);
    }

    /**
     * 绑定物品与证书
     *
     * @param bindThingAndCertParam
     * @return
     */
    @PostMapping(value = "/thing/cert/bind", produces = {MediaType.APPLICATION_JSON_VALUE})
    public ResponseEntity<Object> bindThingAndCert(@RequestBody IotServerBindThingAndCertParam bindThingAndCertParam) {
        boolean flag = iotClientComponent.bindThingAndCert(bindThingAndCertParam.getCertArn(),
                bindThingAndCertParam.getThingName());
        return ResponseEntity.ok(flag);
    }

    /**
     * 绑定证书与策略
     *
     * @param bindCertAndPolicyParam
     * @return
     */
    @PostMapping(value = "/cert/policy/bind", produces = {MediaType.APPLICATION_JSON_VALUE})
    public ResponseEntity<Object> bindCertAndPolicy(@RequestBody IotServerBindCertAndPolicyParam bindCertAndPolicyParam) {
        boolean flag = iotClientComponent.bindCertAndPolicy(bindCertAndPolicyParam.getCertArn(),
                bindCertAndPolicyParam.getPolicyName());
        return ResponseEntity.ok(flag);
    }
}

设备端:

./demo-aws-iot/src/main/java/com/ljq/demo/springboot/aws/iot/device/controller/IotDeviceDemoController.java
package com.ljq.demo.springboot.aws.iot.device.controller;

import com.amazonaws.services.iot.client.AWSIotException;
import com.ljq.demo.springboot.aws.iot.device.param.IotDevicePublishParam;
import com.ljq.demo.springboot.aws.iot.device.util.AwsIotDeviceClientComponent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.nio.charset.StandardCharsets;

/**
 * @Description: iot 设备示例控制层
 * @Author: junqiang.lu
 * @Date: 2022/7/22
 */
@RestController
@RequestMapping(value = "/api/iot/device")
public class IotDeviceDemoController {

    @Autowired
    private AwsIotDeviceClientComponent deviceClientComponent;

    @PostMapping(value = "/publish", produces = {MediaType.APPLICATION_JSON_VALUE})
    public ResponseEntity<Object> publish(@RequestBody IotDevicePublishParam publishParam) throws AWSIotException {
        String topic = "hello/demo/client/" + publishParam.getClientId();
        deviceClientComponent.pushMessage(topic, publishParam.getMessage().getBytes(StandardCharsets.UTF_8));
        return ResponseEntity.ok(System.currentTimeMillis());
    }

}

6 推荐参考文档

Amazon IoT Core 开发人员指南(设备端)

Amazon IoT Device SDK(设备端)

AWS IoT Core API 参考(服务端)

AWS SDK for Java 2.0(服务端)

Amazon IoT 规则操作

IoT Events

Amazon IoT SQL 参考

MQTT 主题-主题筛选条件

生命周期事件

Amazon IoT Core 策略操作

Amazon IoT Core 策略示例

基本任务策略示例

7 Github 源码

Gtihub 源码地址 : https://github.com/Flying9001/springBootDemo

个人公众号:404Code,分享半个互联网人的技术与思考,感兴趣的可以关注.
404Code

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值