【版本】
当前版本号v20230208
版本 | 修改说明 |
---|
v20230208 | 优化了代码,把 thymeleaf 框架换为 beetl |
v20220413 | 修正登录的密码错误 |
v20220331 | 初始化 |
任务5.1 构建物联网云平台项目(iot-cloud)
【任务目的】
- 掌握数据库的建立和使用 Flyway 管理数据库脚本
- 掌握使用 SpringBoot 框架搭建项目
- 掌握使用 SpringBoot、MyBatis 框架开发项目
【任务环境】
- IDEA
- Maven 3.6
- MariaDB 10.4
- JDK 8
【任务说明】
- 结合任务2.2设计的表,实现从使用 Flyway 命令来管理数据库脚本。
Flyway是一个管理数据库脚本的框架,可以与Maven
集成,快速地构建数据库。
iot-cloud
是物联网云平台项目。使用 SpringBoot 集成了 SSM 框架和 Beetl 框架。
【任务效果】
- 启动
IoTCloudApplication
,访问http://localhost:8098/,可以看到以下界面

【任务步骤】
解压iot-cloud.zip
,这是物联网云平台项目,也是一个 Maven 项目。
使用IDEA 打开此项目。

参考任务3.1步骤5,设置iot-cloud
项目的 Maven 安装路径和 settings.xml 文件路径。
创建数据库和数据库用户
- 创建物联网云平台项目的 MariaDB 数据库(
iotcloud
)和用户,请使用数据库客户端执行以下SQL语句。
| 创建数据库 iotcloud |
| CREATE DATABASE iotcloud DEFAULT character set UTF8mb4 collate utf8mb4_bin; |
| |
| CREATE user 'iotcloud'@'localhost' IDENTIFIED BY 'CkA4NAbqHCz@t'; |
| |
| CREATE user 'iotcloud'@'%' IDENTIFIED BY 'CkA4NAbqHCz@t'; |
| |
| GRANT ALL ON iotcloud.* TO 'iotcloud'@'localhost'; |
| GRANT ALL ON iotcloud.* TO 'iotcloud'@'%'; |
| flush privileges; |
复制
数据库脚本创建
- 参考任务4.1,使用 Flyway 来构建物联网云平台项目的数据库。在
src\main\resources\db\migration
目录下创建以下3个SQL文件。
注意:这里的脚本命名V00X后面接2个下划线符号!
V001__CREATE_USER.sql
- 创建用户表,参考任务2.2V002__CREATE_DEVICE.sql
- 创建设备表,参考任务2.2V003__INIT_DATA.sql
- 初始化数据。
| INSERT INTO `user` (`user_id`, `pwd`, `user_name`, `user_secret`) VALUES ('zhangsan', '123456', '张三', 'JOGP9IEQNBOEOPRTJ'); |
| INSERT INTO `device` (`iot_id`, `dev_name`, `user_id`, `dev_type`, `status`, `dev_secret`, `description`,`create_time`) VALUES ('jt982tghj9r8g', 'Lock01', 'zhangsan', 'lock', 'enabled', 'jgiofjgdfiogj', '智能锁1号',now()); |
复制
Flyway 执行 SQL 脚本
- 参考任务4.1步骤7,配置和运行 Flyway 迁移任务,构建
iotcloud
数据库,构建成功的数据库应该包含以下表。
| device |
| user |
| flyway_schema_history |
复制
启动IoTCloudApplication
,访问http://localhost:8098/,可以看到以下界面

输入用户名zhangsan
和密码123456
测试是否能够登录成功,并跳转到设备列表界面。

任务5.2 物联网云平台实现用户登录功能
【任务目的】
- 掌握使用 Spring、SpringMVC、MyBatis、Beetl 框架开发项目
【任务环境】
- IDEA
- Maven 3.6
- MariaDB 10.4
- JDK 8
【任务说明】
- 本任务承接任务5.1,在此基础上对登录功能进行修改,实现能够从数据库读取用户密码信息进行匹配,只有匹配成功情况下才能成功登录。
【任务效果】
- 输入用户名
zhangsan
和密码123456
,登录成功。

【任务步骤】
- 修改
LoginController.signIn
方法,删除硬编码相关代码,实现从数据库查询用户信息。请同学们自己完成。
提示:可以调用 UserService 的方法获取用户信息
- 修改
src\main\java\iot\cloud\platform\cloud\mapper\UserMapper.xml
,实现 SQL 语句。请同学们自己完成。
任务5.3 物联网云平台实现设备列表查询功能
【任务目的】
- 掌握使用 Spring、SpringMVC、MyBatis 框架开发项目
【任务环境】
- IDEA
- Maven 3.6
- MariaDB 10.4
- JDK 8
【任务说明】
- 本任务承接任务5.2,在此基础上对用户设备列表功能进行修改,实现能够从数据库读取当前用户的设备列表。
【任务效果】
- 用户登录以后显示当前用户的设备列表信息。

【任务步骤】
- 修改
DeviceController.getDeviceList
方法,删除硬编码相关代码,实现从数据库查询用户设备列表信息。请同学们自己完成。
提示:可以调用 DeviceService 的方法获取用户设备信息
- 修改
src\main\java\iot\cloud\platform\cloud\mapper\DeviceMapper.xml
,实现 SQL 语句。请同学们自己完成。
任务5.4 物联网云平台实现公开API——获取访问令牌
【任务目的】
- 掌握使用 Spring、SpringMVC、MyBatis 框架开发项目
- 掌握使用 Swagger 框架编写公开API
【任务环境】
- IDEA
- Maven 3.6
- MariaDB 10.4
- JDK 8
【任务说明】
- 本任务承接任务5.3,在此基础上增加实现公开API功能,这里仅实现一个获取访问令牌功能。
参数名称 | 描述 |
---|
user_id | 用户ID |
secret | 用户密钥 |
| http://localhost:8098/token?user_id=zhangsan&secret=jgiofjgdfiogj |
复制
| { |
| "errcode": "0", |
| "errmsg": "获取令牌成功", |
| "data": { |
| "userId": "zhangsan", |
| "token": "7e75e015cf66ad0942df0413eb9d1a4c12342c61bd42bca3f70b2cd6c315d682", |
| "expiredTime": "2022-04-03T23:10:52.010+00:00", |
| "expiredTs": 1649027452010 |
| } |
| } |
复制
【任务步骤】
Token 表创建
- 在
src\main\resources\db\migration
新增一个 SQL 脚本V004__CREATE_TOKEN.sql
,创建一个token
表用于保存用户访问令牌。
| create table `token`( |
| `token` VARCHAR(128) NOT NULL COMMENT '访问令牌', |
| `user_id` VARCHAR(32) NOT NULL COMMENT '用户id', |
| `expired_time` DATETIME NOT NULL COMMENT '令牌超时时间', |
| `expired_ts` BIGINT NOT NULL COMMENT '令牌超时时间戳', |
| UNIQUE KEY (`token`) USING BTREE |
| ) |
| COMMENT='用户令牌' |
| COLLATE='utf8mb4_general_ci' |
| ENGINE=InnoDB |
| ROW_FORMAT=DYNAMIC |
| ; |
复制
TokenEntity
- 根据
Token
表创建一个TokenEntity
类,用于装载Token
表数据。其他属性部分、getter和setter请同学们自行完成。
| public class TokenEntity { |
| private Long expiredTs; |
| |
| public boolean expired(){ |
| long now=new Date().getTime(); |
| return expiredTs<now; |
| } |
| |
| |
| } |
复制
pom.xml
- 在
pom.xml
内新增swagger
相关包。
| |
| <dependency> |
| <groupId>io.springfox</groupId> |
| <artifactId>springfox-swagger-ui</artifactId> |
| <version>2.6.1</version> |
| </dependency> |
| <dependency> |
| <groupId>io.springfox</groupId> |
| <artifactId>springfox-swagger2</artifactId> |
| <version>2.6.1</version> |
| </dependency> |
| |
复制
TokenMapper
- 新增
TokenMapper
类。
| package iot.cloud.platform.cloud.mapper; |
| |
| import iot.cloud.platform.cloud.entity.TokenEntity; |
| |
| public interface TokenMapper { |
| |
| |
| |
| |
| |
| TokenEntity getToken(String token); |
| |
| |
| |
| |
| |
| |
| TokenEntity getTokenByUserId(String userId); |
| |
| |
| |
| |
| |
| |
| boolean saveToken(TokenEntity token); |
| |
| |
| |
| |
| |
| |
| boolean updateToken(TokenEntity token); |
| } |
复制
TokenMapper.xml
- 在
src\main\resources\iot\cloud\platform\cloud\mapper
新增TokenMapper.xml
,根据TokenMapper.java
的注释提示完成 SQL 语句编写。
| <?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="iot.cloud.platform.cloud.mapper.TokenMapper"> |
| <select id="getTokenByUserId" resultType="iot.cloud.platform.cloud.entity.TokenEntity"> |
| |
| </select> |
| |
| <select id="getToken" resultType="iot.cloud.platform.cloud.entity.TokenEntity"> |
| |
| </select> |
| |
| <insert id="saveToken" parameterType="iot.cloud.platform.cloud.entity.TokenEntity"> |
| |
| </insert> |
| |
| <update id="updateToken" parameterType="iot.cloud.platform.cloud.entity.TokenEntity"> |
| |
| </update> |
| </mapper> |
复制
TokenService 和 TokenServiceImpl
- 新增
TokenService
和TokenServiceImpl
类。
| package iot.cloud.platform.cloud.service; |
| |
| import iot.cloud.platform.cloud.entity.TokenEntity; |
| |
| public interface TokenService { |
| |
| |
| |
| |
| |
| boolean isValidToken(String token); |
| |
| |
| |
| |
| |
| |
| TokenEntity getTokenByUserId(String userId); |
| |
| |
| |
| |
| |
| |
| TokenEntity generateToken(String userId); |
| |
| |
| |
| |
| |
| |
| TokenEntity getToken(String token); |
| |
| } |
复制
| package iot.cloud.platform.cloud.service.impl; |
| |
| import iot.cloud.platform.cloud.entity.TokenEntity; |
| import iot.cloud.platform.cloud.mapper.TokenMapper; |
| import iot.cloud.platform.cloud.mapper.UserMapper; |
| import iot.cloud.platform.cloud.service.TokenService; |
| import iot.cloud.platform.cloud.utils.IDUtils; |
| import iot.cloud.platform.cloud.utils.MsgDigestUtils; |
| import org.apache.commons.lang3.RandomStringUtils; |
| import org.springframework.beans.factory.annotation.Autowired; |
| import org.springframework.stereotype.Service; |
| |
| import java.util.Date; |
| |
| @Service |
| public class TokenServiceImpl implements TokenService { |
| |
| private long tokenValidPeriod=1000*60*60*8; |
| |
| @Autowired |
| private TokenMapper tokenMapper; |
| |
| @Autowired |
| private UserMapper userService; |
| |
| @Override |
| public boolean isValidToken(String token) { |
| TokenEntity te=getToken(token); |
| return te!=null && !te.expired(); |
| } |
| |
| @Override |
| public TokenEntity getTokenByUserId(String userId) { |
| return tokenMapper.getTokenByUserId(userId); |
| } |
| |
| @Override |
| public TokenEntity generateToken(String userId) { |
| TokenEntity token=new TokenEntity(); |
| long expiredTs=new Date().getTime()+tokenValidPeriod; |
| Date expiredTime=new Date(expiredTs); |
| token.setUserId(userId); |
| token.setExpiredTs(expiredTs); |
| token.setExpiredTime(expiredTime); |
| token.setToken(IDUtils.genUniqueId()); |
| if(getTokenByUserId(userId)==null) { |
| tokenMapper.saveToken(token); |
| }else{ |
| tokenMapper.updateToken(token); |
| } |
| return token; |
| } |
| |
| @Override |
| public TokenEntity getToken(String token) { |
| return tokenMapper.getToken(token); |
| } |
| |
| |
| } |
复制
UserService 和 UserServiceImpl
UserService
和UserServiceImpl
新增verifySecret
方法,用于校验用户ID和用户密钥是否匹配,匹配返回true。
| package iot.cloud.platform.cloud.service; |
| |
| import iot.cloud.platform.cloud.entity.UserEntity; |
| |
| public interface UserService { |
| UserEntity getUserById(String userId); |
| UserEntity getUserByIdOrName(String idOrName); |
| boolean verifySecret(String userId,String secret); |
| } |
复制
| package iot.cloud.platform.cloud.service.impl; |
| |
| import iot.cloud.platform.cloud.entity.UserEntity; |
| import iot.cloud.platform.cloud.mapper.UserMapper; |
| import iot.cloud.platform.cloud.service.UserService; |
| import org.apache.commons.lang3.StringUtils; |
| import org.springframework.beans.factory.annotation.Autowired; |
| import org.springframework.stereotype.Service; |
| |
| @Service |
| public class UserServiceImpl implements UserService { |
| |
| @Autowired |
| private UserMapper userMapper; |
| |
| @Override |
| public UserEntity getUserById(String userId) { |
| return userMapper.getUserById(userId); |
| } |
| |
| @Override |
| public UserEntity getUserByIdOrName(String idOrName) { |
| return userMapper.getUserByIdOrName(idOrName); |
| } |
| |
| @Override |
| public boolean verifySecret(String userId, String secret) { |
| |
| } |
| } |
复制
SwaggerConfig
- 新增
SwaggerConfig
类
| package iot.cloud.platform.cloud.config; |
| |
| import io.swagger.annotations.ApiOperation; |
| import org.springframework.context.annotation.Bean; |
| import org.springframework.context.annotation.Configuration; |
| import springfox.documentation.builders.ApiInfoBuilder; |
| import springfox.documentation.builders.PathSelectors; |
| import springfox.documentation.builders.RequestHandlerSelectors; |
| import springfox.documentation.service.ApiInfo; |
| import springfox.documentation.spi.DocumentationType; |
| import springfox.documentation.spring.web.plugins.Docket; |
| import springfox.documentation.swagger2.annotations.EnableSwagger2; |
| |
| @Configuration |
| @EnableSwagger2 |
| public class SwaggerConfig { |
| @Bean |
| public Docket createRestApi() { |
| return new Docket(DocumentationType.SWAGGER_2) |
| |
| .apiInfo(apiInfo()) |
| .select() |
| |
| .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) |
| |
| |
| .paths(PathSelectors.any()) |
| |
| .build(); |
| } |
| |
| |
| |
| |
| private ApiInfo apiInfo() { |
| return new ApiInfoBuilder() |
| |
| .title("物联网云平台开放API") |
| |
| .description("物联网云平台开放API") |
| |
| .version("1.0") |
| |
| .build(); |
| } |
| } |
复制
TokenController
- 新增
TokenController
类,补充缺失的代码。
| package iot.cloud.platform.cloud.controller; |
| |
| import io.swagger.annotations.Api; |
| import io.swagger.annotations.ApiImplicitParam; |
| import io.swagger.annotations.ApiImplicitParams; |
| import io.swagger.annotations.ApiOperation; |
| import iot.cloud.platform.cloud.entity.TokenEntity; |
| import iot.cloud.platform.cloud.service.TokenService; |
| import iot.cloud.platform.cloud.service.UserService; |
| import iot.cloud.platform.cloud.vo.ResMsg; |
| import org.springframework.beans.factory.annotation.Autowired; |
| import org.springframework.stereotype.Controller; |
| import org.springframework.web.bind.annotation.GetMapping; |
| import org.springframework.web.bind.annotation.RequestParam; |
| import org.springframework.web.bind.annotation.ResponseBody; |
| |
| @Controller |
| @Api("平台访问令牌接口") |
| public class TokenController { |
| |
| @Autowired |
| private TokenService tokenService; |
| |
| @Autowired |
| private UserService userService; |
| |
| @GetMapping("/token") |
| @ResponseBody |
| @ApiOperation("/获取用户访问令牌") |
| @ApiImplicitParams ({ |
| @ApiImplicitParam(name = "user_id",value = "用户ID",required = true,paramType = "query",dataType = "string"), |
| @ApiImplicitParam(name = "secret",value = "用户密钥",required = true,paramType = "query",dataType = "string") |
| }) |
| public ResMsg token(@RequestParam("user_id") String userId,@RequestParam("secret") String secret){ |
| ResMsg msg=new ResMsg(); |
| if(userId!=null || secret!=null){ |
| if(){ |
| TokenEntity token=; |
| if(token!=null) { |
| if (token.expired()) { |
| token=; |
| msg.setErrcode("0"); |
| msg.setErrmsg("获取令牌成功"); |
| msg.setData(token); |
| } else { |
| msg.setErrcode("0"); |
| msg.setErrmsg("获取令牌成功"); |
| msg.setData(token); |
| } |
| }else{ |
| token=; |
| msg.setErrcode("0"); |
| msg.setErrmsg("获取令牌成功"); |
| msg.setData(token); |
| } |
| } |
| }else{ |
| msg.setErrcode("1003"); |
| msg.setErrmsg("用户不存在或secret不匹配"); |
| } |
| return msg; |
| } |
| } |
复制
工具类 IDUtils 和 MsgDigestUtils
- 新增工具类
IDUtils
用于生成唯一ID,MsgDigestUtils
用于加密信息。
| package iot.cloud.platform.cloud.utils; |
| |
| import org.apache.commons.lang3.RandomStringUtils; |
| |
| import java.util.Date; |
| |
| public class IDUtils { |
| |
| public static String genUniqueId(){ |
| return MsgDigestUtils.encodeSHA256(RandomStringUtils.randomAlphanumeric(16)+new Date().getTime()); |
| } |
| |
| } |
复制
| package iot.cloud.platform.cloud.utils; |
| |
| import java.io.UnsupportedEncodingException; |
| import java.security.MessageDigest; |
| import java.security.NoSuchAlgorithmException; |
| |
| public class MsgDigestUtils { |
| |
| |
| |
| |
| |
| |
| public static String pwdEncrypt(String pwd,String salt){ |
| final int loop=3; |
| String pwdEnc=pwd; |
| for(int i=0;i<loop;i++){ |
| pwdEnc=encodeSHA256(pwdEnc+salt); |
| } |
| return pwdEnc; |
| } |
| |
| |
| |
| |
| |
| |
| public static String encodeSHA256(String str){ |
| MessageDigest messageDigest; |
| String encdeStr = ""; |
| try { |
| messageDigest = MessageDigest.getInstance("SHA-256"); |
| byte[] hash = messageDigest.digest(str.getBytes("UTF-8")); |
| encdeStr = byte2Hex(hash); |
| } catch (NoSuchAlgorithmException e) { |
| e.printStackTrace(); |
| } catch (UnsupportedEncodingException e) { |
| e.printStackTrace(); |
| } |
| return encdeStr; |
| } |
| |
| private static String byte2Hex(byte[] bytes){ |
| StringBuffer stringBuffer = new StringBuffer(); |
| String temp = null; |
| for (int i=0;i<bytes.length;i++){ |
| temp = Integer.toHexString(bytes[i] & 0xFF); |
| if (temp.length()==1){ |
| |
| stringBuffer.append("0"); |
| } |
| stringBuffer.append(temp); |
| } |
| return stringBuffer.toString(); |
| } |
| |
| } |
复制
- 访问http://localhost:8098/swagger-ui.html,输入
用户ID
和用户密钥
,点击Try it out
,测试是否能够获取令牌信息。
