mirror of
https://gitee.com/beecue/fastbee.git
synced 2026-11-06 17:09:11 +08:00
fix(安全): MQTT认证回调强制校验登录态并外置JWT密钥
修复 #31:/iot/tool/mqtt/auth 与 /authv5 对 web/phone 客户端仅验签名即放行, 现要求 JWT 有效且 Redis 存在活跃会话、用户状态正常(与内置 broker 一致), 伪造 token 无会话即被拒;token.secret 支持经 TOKEN_SECRET 注入唯一值。
This commit is contained in:
@@ -77,7 +77,7 @@ user:
|
|||||||
# token配置
|
# token配置
|
||||||
token:
|
token:
|
||||||
header: Authorization # 令牌自定义标识
|
header: Authorization # 令牌自定义标识
|
||||||
secret: abcdefghijklfastbeesmartrstuvwxyz # 令牌密钥
|
secret: ${TOKEN_SECRET:abcdefghijklfastbeesmartrstuvwxyz} # 令牌密钥(生产请通过环境变量 TOKEN_SECRET 注入唯一高强度值)
|
||||||
expireTime: 1440 # 令牌有效期(默认30分钟)1440为一天
|
expireTime: 1440 # 令牌有效期(默认30分钟)1440为一天
|
||||||
|
|
||||||
# mybatis-plus配置
|
# mybatis-plus配置
|
||||||
|
|||||||
@@ -4,12 +4,15 @@ import com.alibaba.fastjson2.JSONObject;
|
|||||||
import com.fastbee.common.ProtocolDeCodeService;
|
import com.fastbee.common.ProtocolDeCodeService;
|
||||||
import com.fastbee.common.annotation.Log;
|
import com.fastbee.common.annotation.Log;
|
||||||
import com.fastbee.common.config.RuoYiConfig;
|
import com.fastbee.common.config.RuoYiConfig;
|
||||||
|
import com.fastbee.common.constant.CacheConstants;
|
||||||
import com.fastbee.common.constant.Constants;
|
import com.fastbee.common.constant.Constants;
|
||||||
import com.fastbee.common.core.controller.BaseController;
|
import com.fastbee.common.core.controller.BaseController;
|
||||||
import com.fastbee.common.core.domain.AjaxResult;
|
import com.fastbee.common.core.domain.AjaxResult;
|
||||||
import com.fastbee.common.core.domain.entity.SysUser;
|
import com.fastbee.common.core.domain.entity.SysUser;
|
||||||
|
import com.fastbee.common.core.domain.model.LoginUser;
|
||||||
import com.fastbee.common.core.iot.response.DeCodeBo;
|
import com.fastbee.common.core.iot.response.DeCodeBo;
|
||||||
import com.fastbee.common.core.page.TableDataInfo;
|
import com.fastbee.common.core.page.TableDataInfo;
|
||||||
|
import com.fastbee.common.core.redis.RedisCache;
|
||||||
import com.fastbee.common.enums.BusinessType;
|
import com.fastbee.common.enums.BusinessType;
|
||||||
import com.fastbee.common.exception.file.FileNameLengthLimitExceededException;
|
import com.fastbee.common.exception.file.FileNameLengthLimitExceededException;
|
||||||
import com.fastbee.common.utils.MessageUtils;
|
import com.fastbee.common.utils.MessageUtils;
|
||||||
@@ -84,6 +87,9 @@ public class ToolController extends BaseController {
|
|||||||
@Value("${token.secret}")
|
@Value("${token.secret}")
|
||||||
private String secret;
|
private String secret;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private RedisCache redisCache;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private ProtocolDeCodeService deCodeService;
|
private ProtocolDeCodeService deCodeService;
|
||||||
@Resource
|
@Resource
|
||||||
@@ -111,6 +117,27 @@ public class ToolController extends BaseController {
|
|||||||
return getDataTable(list);
|
return getDataTable(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Web/移动端 MQTT 认证:JWT 签名合法还不够,必须回查登录态与用户状态
|
||||||
|
*/
|
||||||
|
private boolean isValidWebSession(String token) {
|
||||||
|
try {
|
||||||
|
Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
|
||||||
|
String uuid = (String) claims.get(Constants.LOGIN_USER_KEY);
|
||||||
|
if (StringUtils.isEmpty(uuid)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LoginUser loginUser = redisCache.getCacheObject(CacheConstants.LOGIN_TOKEN_KEY + uuid);
|
||||||
|
if (loginUser == null || loginUser.getUser() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
SysUser user = loginUser.getUser();
|
||||||
|
return "0".equals(user.getStatus()) && !"2".equals(user.getDelFlag());
|
||||||
|
} catch (Exception ex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ApiOperation("mqtt认证")
|
@ApiOperation("mqtt认证")
|
||||||
@PostMapping("/mqtt/auth")
|
@PostMapping("/mqtt/auth")
|
||||||
public ResponseEntity mqttAuth(@RequestParam String clientid, @RequestParam String username, @RequestParam String password) throws Exception {
|
public ResponseEntity mqttAuth(@RequestParam String clientid, @RequestParam String username, @RequestParam String password) throws Exception {
|
||||||
@@ -123,18 +150,17 @@ public class ToolController extends BaseController {
|
|||||||
return toolService.returnUnauthorized(new MqttAuthenticationModel(clientid, username, password), MessageUtils.message("mqtt.unauthorized"));
|
return toolService.returnUnauthorized(new MqttAuthenticationModel(clientid, username, password), MessageUtils.message("mqtt.unauthorized"));
|
||||||
}
|
}
|
||||||
} else if (clientid.startsWith("web") || clientid.startsWith("phone")) {
|
} else if (clientid.startsWith("web") || clientid.startsWith("phone")) {
|
||||||
// web端和移动端认证:token认证
|
// web端和移动端认证:token有效且存在活跃登录态
|
||||||
String token = password;
|
String token = password;
|
||||||
if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) {
|
if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) {
|
||||||
token = token.replace(Constants.TOKEN_PREFIX, "");
|
token = token.replace(Constants.TOKEN_PREFIX, "");
|
||||||
}
|
}
|
||||||
try {
|
if (isValidWebSession(token)) {
|
||||||
Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
|
|
||||||
log.info("-----------移动端/Web端mqtt认证成功,clientId:" + clientid + "---------------");
|
log.info("-----------移动端/Web端mqtt认证成功,clientId:" + clientid + "---------------");
|
||||||
return ResponseEntity.ok().body("ok");
|
return ResponseEntity.ok().body("ok");
|
||||||
} catch (Exception ex) {
|
|
||||||
return toolService.returnUnauthorized(new MqttAuthenticationModel(clientid, username, password), ex.getMessage());
|
|
||||||
}
|
}
|
||||||
|
return toolService.returnUnauthorized(new MqttAuthenticationModel(clientid, username, password),
|
||||||
|
"移动端/Web端mqtt认证失败:登录态已失效或用户不可用");
|
||||||
} else {
|
} else {
|
||||||
// 替换为整合之后的接口
|
// 替换为整合之后的接口
|
||||||
return toolService.clientAuth(clientid, username, password);
|
return toolService.clientAuth(clientid, username, password);
|
||||||
@@ -161,20 +187,18 @@ public class ToolController extends BaseController {
|
|||||||
return ResponseEntity.ok().body(ret);
|
return ResponseEntity.ok().body(ret);
|
||||||
}
|
}
|
||||||
} else if (clientid.startsWith("web") || clientid.startsWith("phone")) {
|
} else if (clientid.startsWith("web") || clientid.startsWith("phone")) {
|
||||||
// web端和移动端认证:token认证
|
// web端和移动端认证:token有效且存在活跃登录态
|
||||||
String token = password;
|
String token = password;
|
||||||
if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) {
|
if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) {
|
||||||
token = token.replace(Constants.TOKEN_PREFIX, "");
|
token = token.replace(Constants.TOKEN_PREFIX, "");
|
||||||
}
|
}
|
||||||
try {
|
if (isValidWebSession(token)) {
|
||||||
Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
|
|
||||||
log.info("-----------移动端/Web端mqtt认证成功,clientId:" + clientid + "---------------");
|
log.info("-----------移动端/Web端mqtt认证成功,clientId:" + clientid + "---------------");
|
||||||
ret.put("result", "allow");
|
ret.put("result", "allow");
|
||||||
return ResponseEntity.ok().body(ret);
|
} else {
|
||||||
} catch (Exception ex) {
|
|
||||||
ret.put("result", "deny");
|
ret.put("result", "deny");
|
||||||
return ResponseEntity.ok().body(ret);
|
|
||||||
}
|
}
|
||||||
|
return ResponseEntity.ok().body(ret);
|
||||||
} else {
|
} else {
|
||||||
// 替换为整合之后的接口
|
// 替换为整合之后的接口
|
||||||
return toolService.clientAuthv5(clientid, username, password);
|
return toolService.clientAuthv5(clientid, username, password);
|
||||||
|
|||||||
Reference in New Issue
Block a user