Commit a0a26c3d64fe25465b7367a6f8b5c43c1013d6d8

Authored by YunaiV
1 parent 643cc4cf

feat:【IoT 物联网】增加网关 HTTP 协议的鉴权,基于 JWT 轻量级(已测试)

yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java
@@ -179,7 +179,7 @@ public class GlobalExceptionHandler { @@ -179,7 +179,7 @@ public class GlobalExceptionHandler {
179 if(ex.getCause() instanceof InvalidFormatException) { 179 if(ex.getCause() instanceof InvalidFormatException) {
180 InvalidFormatException invalidFormatException = (InvalidFormatException) ex.getCause(); 180 InvalidFormatException invalidFormatException = (InvalidFormatException) ex.getCause();
181 return CommonResult.error(BAD_REQUEST.getCode(), String.format("请求参数类型错误:%s", invalidFormatException.getValue())); 181 return CommonResult.error(BAD_REQUEST.getCode(), String.format("请求参数类型错误:%s", invalidFormatException.getValue()));
182 - }else { 182 + } else {
183 return defaultExceptionHandler(ServletUtils.getRequest(), ex); 183 return defaultExceptionHandler(ServletUtils.getRequest(), ex);
184 } 184 }
185 } 185 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceApiImpl.java
@@ -10,6 +10,7 @@ import jakarta.annotation.security.PermitAll; @@ -10,6 +10,7 @@ import jakarta.annotation.security.PermitAll;
10 import org.springframework.context.annotation.Primary; 10 import org.springframework.context.annotation.Primary;
11 import org.springframework.validation.annotation.Validated; 11 import org.springframework.validation.annotation.Validated;
12 import org.springframework.web.bind.annotation.PostMapping; 12 import org.springframework.web.bind.annotation.PostMapping;
  13 +import org.springframework.web.bind.annotation.RequestBody;
13 import org.springframework.web.bind.annotation.RestController; 14 import org.springframework.web.bind.annotation.RestController;
14 15
15 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 16 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@@ -30,7 +31,7 @@ public class IoTDeviceApiImpl implements IotDeviceCommonApi { @@ -30,7 +31,7 @@ public class IoTDeviceApiImpl implements IotDeviceCommonApi {
30 @Override 31 @Override
31 @PostMapping(RpcConstants.RPC_API_PREFIX + "/iot/device/auth") 32 @PostMapping(RpcConstants.RPC_API_PREFIX + "/iot/device/auth")
32 @PermitAll 33 @PermitAll
33 - public CommonResult<Boolean> authDevice(IotDeviceAuthReqDTO authReqDTO) { 34 + public CommonResult<Boolean> authDevice(@RequestBody IotDeviceAuthReqDTO authReqDTO) {
34 return success(deviceService.authDevice(authReqDTO)); 35 return success(deviceService.authDevice(authReqDTO));
35 } 36 }
36 37
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java
@@ -235,6 +235,6 @@ public interface IotDeviceService { @@ -235,6 +235,6 @@ public interface IotDeviceService {
235 * @param authReqDTO 认证信息 235 * @param authReqDTO 认证信息
236 * @return 是否认证成功 236 * @return 是否认证成功
237 */ 237 */
238 - boolean authDevice(IotDeviceAuthReqDTO authReqDTO); 238 + boolean authDevice(@Valid IotDeviceAuthReqDTO authReqDTO);
239 239
240 } 240 }
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpAuthHandler.java
@@ -2,11 +2,13 @@ package cn.iocoder.yudao.module.iot.gateway.protocol.http.router; @@ -2,11 +2,13 @@ package cn.iocoder.yudao.module.iot.gateway.protocol.http.router;
2 2
3 import cn.hutool.core.lang.Assert; 3 import cn.hutool.core.lang.Assert;
4 import cn.hutool.core.map.MapUtil; 4 import cn.hutool.core.map.MapUtil;
  5 +import cn.hutool.core.util.BooleanUtil;
5 import cn.hutool.core.util.StrUtil; 6 import cn.hutool.core.util.StrUtil;
6 import cn.hutool.extra.spring.SpringUtil; 7 import cn.hutool.extra.spring.SpringUtil;
7 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 8 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
8 import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi; 9 import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;
9 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; 10 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;
  11 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
10 import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer; 12 import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer;
11 import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; 13 import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;
12 import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpUpstreamProtocol; 14 import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpUpstreamProtocol;
@@ -47,7 +49,7 @@ public class IotHttpAuthHandler extends IotHttpAbstractHandler { @@ -47,7 +49,7 @@ public class IotHttpAuthHandler extends IotHttpAbstractHandler {
47 49
48 @Override 50 @Override
49 public CommonResult<Object> handle0(RoutingContext context) { 51 public CommonResult<Object> handle0(RoutingContext context) {
50 - // 解析参数 52 + // 1. 解析参数
51 JsonObject body = context.body().asJsonObject(); 53 JsonObject body = context.body().asJsonObject();
52 String clientId = body.getString("clientId"); 54 String clientId = body.getString("clientId");
53 if (StrUtil.isEmpty(clientId)) { 55 if (StrUtil.isEmpty(clientId)) {
@@ -62,20 +64,23 @@ public class IotHttpAuthHandler extends IotHttpAbstractHandler { @@ -62,20 +64,23 @@ public class IotHttpAuthHandler extends IotHttpAbstractHandler {
62 throw invalidParamException("password 不能为空"); 64 throw invalidParamException("password 不能为空");
63 } 65 }
64 66
65 - // 执行认证 67 + // 2.1 执行认证
66 CommonResult<Boolean> result = deviceClientService.authDevice(new IotDeviceAuthReqDTO() 68 CommonResult<Boolean> result = deviceClientService.authDevice(new IotDeviceAuthReqDTO()
67 .setClientId(clientId).setUsername(username).setPassword(password)); 69 .setClientId(clientId).setUsername(username).setPassword(password));
68 - if (result == null || !result.isSuccess()) { 70 + result.checkError();;
  71 + if (!BooleanUtil.isTrue(result.getData())) {
69 throw exception(DEVICE_AUTH_FAIL); 72 throw exception(DEVICE_AUTH_FAIL);
70 } 73 }
71 -  
72 - // 生成 Token 74 + // 2.2 生成 Token
73 IotDeviceAuthUtils.DeviceInfo deviceInfo = deviceTokenService.parseUsername(username); 75 IotDeviceAuthUtils.DeviceInfo deviceInfo = deviceTokenService.parseUsername(username);
74 Assert.notNull(deviceInfo, "设备信息不能为空"); 76 Assert.notNull(deviceInfo, "设备信息不能为空");
75 String token = deviceTokenService.createToken(deviceInfo.getProductKey(), deviceInfo.getDeviceName()); 77 String token = deviceTokenService.createToken(deviceInfo.getProductKey(), deviceInfo.getDeviceName());
76 Assert.notBlank(token, "生成 token 不能为空位"); 78 Assert.notBlank(token, "生成 token 不能为空位");
77 79
78 - // TODO @芋艿:发送上线消息; 80 + // 3. 执行上线
  81 + deviceMessageProducer.sendDeviceMessage(IotDeviceMessage.of(deviceInfo.getProductKey(), deviceInfo.getDeviceName(),
  82 + protocol.getServerId())
  83 + .ofStateOnline());
79 84
80 // 构建响应数据 85 // 构建响应数据
81 return success(MapUtil.of("token", token)); 86 return success(MapUtil.of("token", token));
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/IotDeviceClientServiceImpl.java
1 package cn.iocoder.yudao.module.iot.gateway.service.device; 1 package cn.iocoder.yudao.module.iot.gateway.service.device;
2 2
  3 +import cn.hutool.core.lang.Assert;
3 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi; 5 import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;
5 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; 6 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;
@@ -31,7 +32,7 @@ public class IotDeviceClientServiceImpl implements IotDeviceCommonApi { @@ -31,7 +32,7 @@ public class IotDeviceClientServiceImpl implements IotDeviceCommonApi {
31 public void init() { 32 public void init() {
32 IotGatewayProperties.RpcProperties rpc = gatewayProperties.getRpc(); 33 IotGatewayProperties.RpcProperties rpc = gatewayProperties.getRpc();
33 restTemplate = new RestTemplateBuilder() 34 restTemplate = new RestTemplateBuilder()
34 - .rootUri(rpc.getUrl() + "/rpc-api/iot/device/") 35 + .rootUri(rpc.getUrl() + "/rpc-api/iot/device")
35 .readTimeout(rpc.getReadTimeout()) 36 .readTimeout(rpc.getReadTimeout())
36 .connectTimeout(rpc.getConnectTimeout()) 37 .connectTimeout(rpc.getConnectTimeout())
37 .build(); 38 .build();
@@ -39,7 +40,7 @@ public class IotDeviceClientServiceImpl implements IotDeviceCommonApi { @@ -39,7 +40,7 @@ public class IotDeviceClientServiceImpl implements IotDeviceCommonApi {
39 40
40 @Override 41 @Override
41 public CommonResult<Boolean> authDevice(IotDeviceAuthReqDTO authReqDTO) { 42 public CommonResult<Boolean> authDevice(IotDeviceAuthReqDTO authReqDTO) {
42 - return doPost("auth", authReqDTO); 43 + return doPost("/auth", authReqDTO);
43 } 44 }
44 45
45 @SuppressWarnings("unchecked") 46 @SuppressWarnings("unchecked")
@@ -48,6 +49,7 @@ public class IotDeviceClientServiceImpl implements IotDeviceCommonApi { @@ -48,6 +49,7 @@ public class IotDeviceClientServiceImpl implements IotDeviceCommonApi {
48 CommonResult<Boolean> result = restTemplate.postForObject(url, requestBody, 49 CommonResult<Boolean> result = restTemplate.postForObject(url, requestBody,
49 (Class<CommonResult<Boolean>>) (Class<?>) CommonResult.class); 50 (Class<CommonResult<Boolean>>) (Class<?>) CommonResult.class);
50 log.info("[doPost][url({}) requestBody({}) result({})]", url, requestBody, result); 51 log.info("[doPost][url({}) requestBody({}) result({})]", url, requestBody, result);
  52 + Assert.notNull(result, "请求结果不能为空");
51 return result; 53 return result;
52 } catch (Exception e) { 54 } catch (Exception e) {
53 log.error("[doPost][url({}) requestBody({}) 发生异常]", url, requestBody, e); 55 log.error("[doPost][url({}) requestBody({}) 发生异常]", url, requestBody, e);