From dbee9fe94ad7937c3a86750d901956885ba332ba Mon Sep 17 00:00:00 2001 From: sdy Date: Tue, 26 May 2026 19:40:28 +0800 Subject: [PATCH] =?UTF-8?q?1.=E6=95=B0=E6=8D=AE=E6=9D=83=E9=99=90=E4=BC=98?= =?UTF-8?q?=E5=8C=96=202.mqtt=E5=A2=9E=E5=8A=A0=E6=9D=83=E9=99=90=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/aspect/DataFilterAspect.java | 66 ++++++++----- .../common/interceptor/DataScopeContext.java | 23 +++++ .../modules/business/service/DockService.java | 4 + .../service/impl/DockServiceImpl.java | 16 +++ .../modules/security/config/ShiroConfig.java | 1 + .../security/controller/LoginController.java | 58 ++++++++++- .../modules/security/dto/MqttAuthDTO.java | 21 ++++ .../modules/security/dto/MqttAuthVO.java | 43 ++++++++ .../modules/security/oauth2/Oauth2Realm.java | 78 ++++++++++----- .../modules/security/user/UserDetail.java | 3 + .../src/main/resources/application-docker.yml | 8 +- prj-deploy/deploy.sh | 6 ++ prj-deploy/docker-compose.yml | 13 ++- .../file/api/conf/application-docker.yml | 8 +- prj-deploy/file/emqx/etc/emqx.conf | 99 +++++++++++++++++++ prj-deploy/file/emqx2/etc/emqx.conf | 99 +++++++++++++++++++ 16 files changed, 486 insertions(+), 60 deletions(-) create mode 100644 admin/src/main/java/com/multictrl/common/interceptor/DataScopeContext.java create mode 100644 admin/src/main/java/com/multictrl/modules/security/dto/MqttAuthDTO.java create mode 100644 admin/src/main/java/com/multictrl/modules/security/dto/MqttAuthVO.java create mode 100644 prj-deploy/file/emqx/etc/emqx.conf create mode 100644 prj-deploy/file/emqx2/etc/emqx.conf diff --git a/admin/src/main/java/com/multictrl/common/aspect/DataFilterAspect.java b/admin/src/main/java/com/multictrl/common/aspect/DataFilterAspect.java index bcd2862..e85cc15 100644 --- a/admin/src/main/java/com/multictrl/common/aspect/DataFilterAspect.java +++ b/admin/src/main/java/com/multictrl/common/aspect/DataFilterAspect.java @@ -7,9 +7,11 @@ import com.multictrl.common.constant.Constant; import com.multictrl.common.exception.ErrorCode; import com.multictrl.common.exception.RenException; import com.multictrl.common.interceptor.DataScope; +import com.multictrl.common.interceptor.DataScopeContext; import com.multictrl.modules.security.user.SecurityUser; import com.multictrl.modules.security.user.UserDetail; import com.multictrl.modules.sys.enums.SuperAdminEnum; +import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @@ -26,6 +28,7 @@ import java.util.Map; * * @author Sdy */ +@Slf4j @Aspect @Component public class DataFilterAspect { @@ -37,28 +40,39 @@ public class DataFilterAspect { @Before("dataFilterCut()") public void dataFilter(JoinPoint point) { - Object params = point.getArgs()[0]; - if (params instanceof Map) { - UserDetail user = SecurityUser.getUser(); - - //如果是超级管理员,则不进行数据过滤 - if (user.getSuperAdmin() == SuperAdminEnum.YES.value()) { - return; - } - - try { - //否则进行数据过滤 - Map map = (Map) params; - String sqlFilter = getSqlFilter(user, point); - map.put(Constant.SQL_FILTER, new DataScope(sqlFilter)); - } catch (Exception ignored) { - - } +// Object params = point.getArgs()[0]; +// if (params != null && params instanceof Map) { + UserDetail user = SecurityUser.getUser(); + //如果是超级管理员,则不进行数据过滤 + if (user.getSuperAdmin() == SuperAdminEnum.YES.value()) { return; } - throw new RenException(ErrorCode.DATA_SCOPE_PARAMS_ERROR); + /* try { + //否则进行数据过滤 + Map map = (Map) params; + String sqlFilter = getSqlFilter(user, point); + // 放入 ThreadLocal + DataScopeContext.setDataScope(new DataScope(sqlFilter)); + + map.put(Constant.SQL_FILTER, new DataScope(sqlFilter)); + } catch (Exception e) { + + }*/ + try { + String sqlFilter = getSqlFilter(user, point); + // 放入 ThreadLocal + DataScopeContext.setDataScope(new DataScope(sqlFilter)); + return; + } catch (Exception e) { + throw new RenException(e.getMessage()); + } + +// return; +// } + +// throw new RenException(ErrorCode.DATA_SCOPE_PARAMS_ERROR); } /** @@ -75,8 +89,8 @@ public class DataFilterAspect { tableAlias += "."; } - StringBuilder sqlFilter = new StringBuilder(); - sqlFilter.append(" ("); + StringBuilder sqlFilter = new StringBuilder(" "); +// sqlFilter.append("("); //部门ID列表 List deptIdList = user.getDeptIdList(); @@ -84,16 +98,20 @@ public class DataFilterAspect { sqlFilter.append(tableAlias).append(dataFilter.deptId()); sqlFilter.append(" in(").append(CollUtil.join(deptIdList, ",")).append(")"); + } else { + throw new RenException("暂无数据权限,无法访问"); } //查询本人数据 - if (CollUtil.isNotEmpty(deptIdList)) { + /* if (CollUtil.isNotEmpty(deptIdList)) { sqlFilter.append(" or "); } - sqlFilter.append(tableAlias).append(dataFilter.userId()).append("=").append(user.getId()); + sqlFilter.append(tableAlias).append(dataFilter.userId()).append("=").append(user.getId());*/ - sqlFilter.append(")"); +// sqlFilter.append(")"); + String sqlFilterString = sqlFilter.toString(); + log.debug("数据过滤SQL:{}", sqlFilterString); - return sqlFilter.toString(); + return sqlFilterString; } } \ No newline at end of file diff --git a/admin/src/main/java/com/multictrl/common/interceptor/DataScopeContext.java b/admin/src/main/java/com/multictrl/common/interceptor/DataScopeContext.java new file mode 100644 index 0000000..902f500 --- /dev/null +++ b/admin/src/main/java/com/multictrl/common/interceptor/DataScopeContext.java @@ -0,0 +1,23 @@ +package com.multictrl.common.interceptor; + +/** + * 数据鉴权 + * + * @author Sdy + * @since 1.0.0 2026/5/26 + */ +public class DataScopeContext { + private static final ThreadLocal DATA_SCOPE_HOLDER = new ThreadLocal<>(); + + public static void setDataScope(DataScope dataScope) { + DATA_SCOPE_HOLDER.set(dataScope); + } + + public static DataScope getDataScope() { + return DATA_SCOPE_HOLDER.get(); + } + + public static void clear() { + DATA_SCOPE_HOLDER.remove(); + } +} diff --git a/admin/src/main/java/com/multictrl/modules/business/service/DockService.java b/admin/src/main/java/com/multictrl/modules/business/service/DockService.java index 086dfea..a5d59fd 100644 --- a/admin/src/main/java/com/multictrl/modules/business/service/DockService.java +++ b/admin/src/main/java/com/multictrl/modules/business/service/DockService.java @@ -7,6 +7,7 @@ import com.multictrl.modules.business.dao.DockDao; import com.multictrl.modules.business.dto.DockDTO; import com.multictrl.modules.business.entity.DockEntity; +import java.util.List; import java.util.Map; /** @@ -23,6 +24,9 @@ public interface DockService extends CrudService { //分页查询 PageData pageList(Map params); + //获取机库列表 + List getUserDockList(Long userId); + //获取dao DockDao getDao(); } \ No newline at end of file diff --git a/admin/src/main/java/com/multictrl/modules/business/service/impl/DockServiceImpl.java b/admin/src/main/java/com/multictrl/modules/business/service/impl/DockServiceImpl.java index b026736..6b62c93 100644 --- a/admin/src/main/java/com/multictrl/modules/business/service/impl/DockServiceImpl.java +++ b/admin/src/main/java/com/multictrl/modules/business/service/impl/DockServiceImpl.java @@ -1,11 +1,13 @@ package com.multictrl.modules.business.service.impl; +import cn.hutool.core.collection.CollUtil; import cn.hutool.json.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import cn.hutool.core.util.StrUtil; import com.multictrl.common.constant.DockMode; import com.multictrl.common.page.PageData; import com.multictrl.common.service.impl.CrudServiceImpl; +import com.multictrl.common.utils.ConvertUtils; import com.multictrl.modules.business.dao.DeviceDicDao; import com.multictrl.modules.business.dao.DockDao; import com.multictrl.modules.business.dao.DockDeviceDao; @@ -15,9 +17,11 @@ import com.multictrl.modules.business.entity.DockDeviceEntity; import com.multictrl.modules.business.entity.DockEntity; import com.multictrl.modules.business.service.DJIBaseService; import com.multictrl.modules.business.service.DockService; +import com.multictrl.modules.security.service.ShiroService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -33,6 +37,7 @@ public class DockServiceImpl extends CrudServiceImpl getWrapper(Map params) { @@ -93,6 +98,17 @@ public class DockServiceImpl extends CrudServiceImpl getUserDockList(Long userId) { + List dataScopeList = shiroService.getDataScopeList(userId); + List list = new ArrayList<>(); + if(CollUtil.isNotEmpty(dataScopeList)){ + list = baseDao.selectList(new QueryWrapper().in("dept_id", dataScopeList)); + } + + return ConvertUtils.sourceToTarget(list, DockDTO.class); + } + @Override public DockDao getDao() { diff --git a/admin/src/main/java/com/multictrl/modules/security/config/ShiroConfig.java b/admin/src/main/java/com/multictrl/modules/security/config/ShiroConfig.java index ebbaa24..71ab1cf 100644 --- a/admin/src/main/java/com/multictrl/modules/security/config/ShiroConfig.java +++ b/admin/src/main/java/com/multictrl/modules/security/config/ShiroConfig.java @@ -73,6 +73,7 @@ public class ShiroConfig { filterMap.put("/captcha", "anon"); filterMap.put("/", "anon"); filterMap.put("/srs/**", "anon"); + filterMap.put("/mqtt/auth", "anon"); filterMap.put("/**", "oauth2"); return filterMap; } diff --git a/admin/src/main/java/com/multictrl/modules/security/controller/LoginController.java b/admin/src/main/java/com/multictrl/modules/security/controller/LoginController.java index 3219616..34ebd86 100644 --- a/admin/src/main/java/com/multictrl/modules/security/controller/LoginController.java +++ b/admin/src/main/java/com/multictrl/modules/security/controller/LoginController.java @@ -6,17 +6,22 @@ import com.multictrl.common.utils.IpUtils; import com.multictrl.common.utils.Result; import com.multictrl.common.validator.AssertUtils; import com.multictrl.common.validator.ValidatorUtils; +import com.multictrl.modules.business.dto.DockDTO; +import com.multictrl.modules.business.service.DockService; import com.multictrl.modules.log.entity.SysLogLoginEntity; import com.multictrl.modules.log.enums.LoginOperationEnum; import com.multictrl.modules.log.enums.LoginStatusEnum; import com.multictrl.modules.log.service.SysLogLoginService; import com.multictrl.modules.security.dto.LoginDTO; +import com.multictrl.modules.security.dto.MqttAuthDTO; +import com.multictrl.modules.security.dto.MqttAuthVO; import com.multictrl.modules.security.password.PasswordUtils; import com.multictrl.modules.security.service.CaptchaService; import com.multictrl.modules.security.service.SysUserTokenService; import com.multictrl.modules.security.user.SecurityUser; import com.multictrl.modules.security.user.UserDetail; import com.multictrl.modules.sys.dto.SysUserDTO; +import com.multictrl.modules.sys.enums.SuperAdminEnum; import com.multictrl.modules.sys.enums.UserStatusEnum; import com.multictrl.modules.sys.service.SysUserService; import io.swagger.v3.oas.annotations.Operation; @@ -25,7 +30,7 @@ import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import lombok.AllArgsConstructor; +import lombok.RequiredArgsConstructor; import org.springframework.http.HttpHeaders; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @@ -33,7 +38,9 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import java.io.IOException; +import java.util.ArrayList; import java.util.Date; +import java.util.List; /** * 登录 @@ -42,12 +49,14 @@ import java.util.Date; */ @RestController @Tag(name = "登录管理") -@AllArgsConstructor +@RequiredArgsConstructor public class LoginController { private final SysUserService sysUserService; private final SysUserTokenService sysUserTokenService; private final CaptchaService captchaService; private final SysLogLoginService sysLogLoginService; + private final DockService dockService; + @GetMapping("captcha") @Operation(summary = "验证码") @@ -142,4 +151,49 @@ public class LoginController { return new Result(); } + @PostMapping("/mqtt/auth") + @Operation(summary = "mqtt认证", hidden = true) + public MqttAuthVO mqttAuth(@RequestBody MqttAuthDTO login) { + if (login.getUsername() == null || login.getPassword() == null) { + return new MqttAuthVO().setResult("deny"); + } + + //给机场配置专属用户名密码 + if (login.getUsername().equals("dock") && login.getPassword().equals("Dock@2023")) { + return new MqttAuthVO().setResult("allow").setIs_superuser(true).setAcl(new ArrayList<>()); + } + + //用户、账号、密码校验 + SysUserDTO user = sysUserService.getByUsername(login.getUsername()); + if (user == null || !PasswordUtils.matches(login.getPassword(), user.getPassword()) + || user.getStatus() == UserStatusEnum.DISABLE.value()) { + return new MqttAuthVO().setResult("deny"); + } + + //分配权限 + if (SuperAdminEnum.YES.value() == user.getSuperAdmin()) { + return new MqttAuthVO().setResult("allow").setIs_superuser(true).setAcl(new ArrayList<>()); + } else { + String topic = "thing/product/%s/#"; + List aclList = new ArrayList<>(); + List dockList = dockService.getUserDockList(user.getId()); + for (DockDTO dto : dockList) { + MqttAuthVO.Acl acl = new MqttAuthVO.Acl(); + acl.setAction("publish"); + acl.setPermission("allow"); + acl.setTopic(topic.formatted(dto.getDockSn())); + aclList.add(acl); + + MqttAuthVO.Acl acl2 = new MqttAuthVO.Acl(); + acl2.setAction("subscribe"); + acl2.setPermission("allow"); + acl2.setTopic(topic.formatted(dto.getDockSn())); + aclList.add(acl2); + } + aclList.add(new MqttAuthVO.Acl().setAction("publish").setPermission("deny").setTopic("#")); + aclList.add(new MqttAuthVO.Acl().setAction("subscribe").setPermission("deny").setTopic("#")); + return new MqttAuthVO().setResult("allow").setIs_superuser(false).setAcl(aclList); + } + } + } diff --git a/admin/src/main/java/com/multictrl/modules/security/dto/MqttAuthDTO.java b/admin/src/main/java/com/multictrl/modules/security/dto/MqttAuthDTO.java new file mode 100644 index 0000000..55ed590 --- /dev/null +++ b/admin/src/main/java/com/multictrl/modules/security/dto/MqttAuthDTO.java @@ -0,0 +1,21 @@ +package com.multictrl.modules.security.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * mqtt认证参数 + * + * @author Sdy + * @since 1.0.0 2026/5/26 + */ +@Data +@Schema(name = "mqtt认证参数") +public class MqttAuthDTO { + + @Schema(description = "用户名") + private String username; + + @Schema(description = "密码") + private String password; +} diff --git a/admin/src/main/java/com/multictrl/modules/security/dto/MqttAuthVO.java b/admin/src/main/java/com/multictrl/modules/security/dto/MqttAuthVO.java new file mode 100644 index 0000000..79af785 --- /dev/null +++ b/admin/src/main/java/com/multictrl/modules/security/dto/MqttAuthVO.java @@ -0,0 +1,43 @@ +package com.multictrl.modules.security.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.List; + +/** + * mqtt认证返回 + * + * @author Sdy + * @since 1.0.0 2026/5/26 + */ +@Data +@Accessors(chain = true) +@Schema(name = "mqtt认证参数") +public class MqttAuthVO { + + @Schema(description = "认证结果 allow允许 deny拒绝") + private String result; + + @Schema(description = "是否是超级用户 true超级用户 false普通用户") + private Boolean is_superuser; + + @Schema(description = "访问控制列表," + + "数组中的每个对象是一条规则,EMQX 会按顺序匹配,一旦匹配到某条规则就停止继续匹配,并使用该规则的 permission 值。") + private List acl; + + @Data + @Schema(name = "acl") + public static class Acl { + + @Schema(description = "主题") + private String topic; + + @Schema(description = "动作 publish subscribe") + private String action; + + @Schema(description = "权限 allow deny") + private String permission; + } +} diff --git a/admin/src/main/java/com/multictrl/modules/security/oauth2/Oauth2Realm.java b/admin/src/main/java/com/multictrl/modules/security/oauth2/Oauth2Realm.java index 2ebc4c6..168c593 100644 --- a/admin/src/main/java/com/multictrl/modules/security/oauth2/Oauth2Realm.java +++ b/admin/src/main/java/com/multictrl/modules/security/oauth2/Oauth2Realm.java @@ -1,7 +1,9 @@ package com.multictrl.modules.security.oauth2; import com.multictrl.common.exception.ErrorCode; +import com.multictrl.common.utils.CacheUtils; import com.multictrl.common.utils.ConvertUtils; +import com.multictrl.common.utils.JsonUtils; import com.multictrl.common.utils.MessageUtils; import com.multictrl.modules.security.entity.SysUserTokenEntity; import com.multictrl.modules.security.service.ShiroService; @@ -15,7 +17,9 @@ import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.springframework.stereotype.Component; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Set; /** @@ -40,8 +44,18 @@ public class Oauth2Realm extends AuthorizingRealm { protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { UserDetail user = (UserDetail) principals.getPrimaryPrincipal(); - //用户权限列表 - Set permsSet = shiroService.getUserPermissions(user); + Set permsSet; + Object object = CacheUtils.get(user.getToken() + "_perms"); + if (object == null) { + //用户权限列表 + permsSet = shiroService.getUserPermissions(user); + Map map = new HashMap<>(); + map.put("perms", JsonUtils.toJsonString(permsSet)); + CacheUtils.set(user.getToken() + "_perms", map); + } else { + String perms = (String) ((Map) object).get("perms"); + permsSet = JsonUtils.parseObject(perms, Set.class); + } SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); info.setStringPermissions(permsSet); @@ -55,30 +69,46 @@ public class Oauth2Realm extends AuthorizingRealm { protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { String accessToken = (String) token.getPrincipal(); - //根据accessToken,查询用户信息 - SysUserTokenEntity tokenEntity = shiroService.getByToken(accessToken); - //token失效 - if (tokenEntity == null || tokenEntity.getExpireDate().getTime() < System.currentTimeMillis()) { - throw new IncorrectCredentialsException(MessageUtils.getMessage(ErrorCode.TOKEN_INVALID)); + UserDetail userDetail; + Object object = CacheUtils.get(accessToken); + if (object == null) { + //根据accessToken,查询用户信息 + SysUserTokenEntity tokenEntity = shiroService.getByToken(accessToken); + + //token失效 + if (tokenEntity == null || tokenEntity.getExpireDate().getTime() < System.currentTimeMillis()) { + throw new IncorrectCredentialsException(MessageUtils.getMessage(ErrorCode.TOKEN_INVALID)); + } + + //查询用户信息 + SysUserEntity userEntity = shiroService.getUser(tokenEntity.getUserId()); + //转换成UserDetail对象 + userDetail = ConvertUtils.sourceToTarget(userEntity, UserDetail.class); + userDetail.setToken(accessToken); + //账号锁定 + if (userDetail.getStatus() == 0) { + throw new LockedAccountException(MessageUtils.getMessage(ErrorCode.ACCOUNT_LOCK)); + } + + //获取用户对应的部门数据权限 + List deptIdList = shiroService.getDataScopeList(userDetail.getId()); + userDetail.setDeptIdList(deptIdList); + + Map map = new HashMap<>(); + map.put("user", userDetail); + map.put("token", tokenEntity); + CacheUtils.set(accessToken, map, 1000 * 60); + } else { + userDetail = (UserDetail) ((Map) object).get("user"); + SysUserTokenEntity tokenEntity = (SysUserTokenEntity) ((Map) object).get("token"); + + //token失效 + if (tokenEntity == null || tokenEntity.getExpireDate().getTime() < System.currentTimeMillis()) { + throw new IncorrectCredentialsException(MessageUtils.getMessage(ErrorCode.TOKEN_INVALID)); + } } - //查询用户信息 - SysUserEntity userEntity = shiroService.getUser(tokenEntity.getUserId()); - - //转换成UserDetail对象 - UserDetail userDetail = ConvertUtils.sourceToTarget(userEntity, UserDetail.class); - - //获取用户对应的部门数据权限 - List deptIdList = shiroService.getDataScopeList(userDetail.getId()); - userDetail.setDeptIdList(deptIdList); - - //账号锁定 - if (userDetail.getStatus() == 0) { - throw new LockedAccountException(MessageUtils.getMessage(ErrorCode.ACCOUNT_LOCK)); - } - - SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(userDetail, accessToken, getName()); - return info; + return new SimpleAuthenticationInfo(userDetail, accessToken, getName()); } } \ No newline at end of file diff --git a/admin/src/main/java/com/multictrl/modules/security/user/UserDetail.java b/admin/src/main/java/com/multictrl/modules/security/user/UserDetail.java index 9caaab5..4fac580 100644 --- a/admin/src/main/java/com/multictrl/modules/security/user/UserDetail.java +++ b/admin/src/main/java/com/multictrl/modules/security/user/UserDetail.java @@ -32,4 +32,7 @@ public class UserDetail implements Serializable { */ private List deptIdList; + //token + private String token; + } \ No newline at end of file diff --git a/admin/src/main/resources/application-docker.yml b/admin/src/main/resources/application-docker.yml index ed42310..33dd006 100644 --- a/admin/src/main/resources/application-docker.yml +++ b/admin/src/main/resources/application-docker.yml @@ -42,15 +42,15 @@ minio: mqtt: client1: url: tcp://${host.ip}:61627 - username: admin - password: Aros2023 + username: dock + password: Dock@2023 subClientId: dj-one-sub${random.int(10)} subTopic: thing/product/#,sys/product/#,thing/device/# pubClientId: dj-one-pub${random.int(10)} client2: url: tcp://${host.ip}:61637 - username: admin - password: Aros2023 + username: dock + password: Dock@2023 subClientId: dj-two-sub${random.int(10)} subTopic: thing/product/# pubClientId: dj-two-pub${random.int(10)} diff --git a/prj-deploy/deploy.sh b/prj-deploy/deploy.sh index 5e32ba2..7065a20 100644 --- a/prj-deploy/deploy.sh +++ b/prj-deploy/deploy.sh @@ -24,7 +24,13 @@ cp -r ./file/nginx/cert/* /root/dj_multictrl_data/nginx/cert/ cp -r ./file/nginx/html/* /root/dj_multictrl_data/nginx/html/ mkdir -p /root/dj_multictrl_data/emqx/logs +mkdir -p /root/dj_multictrl_data/emqx/data +mkdir -p /root/dj_multictrl_data/emqx/etc +cp ./file/emqx/etc/emqx.conf /root/dj_multictrl_data/emqx/etc/emqx.conf mkdir -p /root/dj_multictrl_data/emqx2/logs +mkdir -p /root/dj_multictrl_data/emqx2/data +mkdir -p /root/dj_multictrl_data/emqx2/etc +cp ./file/emqx2/etc/emqx.conf /root/dj_multictrl_data/emqx2/etc/emqx.conf mkdir -p /root/dj_multictrl_data/srs/conf mkdir -p /data/dj_multictrl_data/live_record diff --git a/prj-deploy/docker-compose.yml b/prj-deploy/docker-compose.yml index bb92e59..cdfa6ae 100644 --- a/prj-deploy/docker-compose.yml +++ b/prj-deploy/docker-compose.yml @@ -74,9 +74,15 @@ services: - '61628:8083' environment: - TZ=Asia/Shanghai - - EMQX_ADMIN_PASSWORD=Aros2023 + # 关键变量 1: 固定节点名,避免因容器IP变化导致数据失效 + - EMQX_NODE_NAME=emqx@dj-multictrl-emqx + # 关键变量 2: 正确设置 Dashboard 密码 (针对 5.x 版本) + - EMQX_DASHBOARD__DEFAULT_PASSWORD=Multictrl2023 + #- EMQX_ADMIN_PASSWORD=2023 volumes: - /root/dj_multictrl_data/emqx/logs:/opt/emqx/log + - /root/dj_multictrl_data/emqx/data:/opt/emqx/data + - /root/dj_multictrl_data/emqx/etc/emqx.conf:/opt/emqx/etc/emqx.conf - /etc/localtime:/etc/localtime:ro dj-multictrl-emqx2: @@ -90,9 +96,12 @@ services: - '61638:8083' environment: - TZ=Asia/Shanghai - - EMQX_ADMIN_PASSWORD=Aros2023 + - EMQX_NODE_NAME=emqx@dj-multictrl-emqx2 + - EMQX_DASHBOARD__DEFAULT_PASSWORD=Multictrl2023 volumes: - /root/dj_multictrl_data/emqx2/logs:/opt/emqx/log + - /root/dj_multictrl_data/emqx2/data:/opt/emqx/data + - /root/dj_multictrl_data/emqx2/etc:/opt/emqx/etc - /etc/localtime:/etc/localtime:ro dj-multictrl-srs: diff --git a/prj-deploy/file/api/conf/application-docker.yml b/prj-deploy/file/api/conf/application-docker.yml index f2ab239..43573e5 100644 --- a/prj-deploy/file/api/conf/application-docker.yml +++ b/prj-deploy/file/api/conf/application-docker.yml @@ -42,15 +42,15 @@ minio: mqtt: client1: url: tcp://dj-multictrl-emqx:1883 - username: admin - password: Aros2023 + username: dock + password: Dock@2023 subClientId: dj-one-sub subTopic: thing/product/#,sys/product/# pubClientId: dj-one-pub client2: url: tcp://${host.ip}:61637 - username: admin - password: Aros2023 + username: dock + password: Dock@2023 subClientId: dj-two-sub subTopic: thing/product/# pubClientId: dj-two-pub diff --git a/prj-deploy/file/emqx/etc/emqx.conf b/prj-deploy/file/emqx/etc/emqx.conf new file mode 100644 index 0000000..7684ce7 --- /dev/null +++ b/prj-deploy/file/emqx/etc/emqx.conf @@ -0,0 +1,99 @@ +## NOTE: +## This config file overrides data/configs/cluster.hocon, +## and is merged with environment variables which start with 'EMQX_' prefix. +## +## Config changes made from EMQX dashboard UI, management HTTP API, or CLI +## are stored in data/configs/cluster.hocon. +## To avoid confusion, please do not store the same configs in both files. +## +## See https://www.emqx.io/docs/en/latest/configuration/configuration.html for more details. +## Configuration full example can be found in etc/examples + +node { + name = "emqx@127.0.0.1" + cookie = "emqxsecretcookie" + data_dir = "data" +} + +cluster { + name = emqxcl + discovery_strategy = manual +} + +## EMQX provides support for two primary log handlers: `file` and `console`, with an additional `audit` handler specifically designed to always direct logs to files. +## The system's default log handling behavior can be configured via the environment variable `EMQX_DEFAULT_LOG_HANDLER`, which accepts the following settings: +## +## - `file`: Directs log output exclusively to files. +## - `console`: Channels log output solely to the console. +## +## It's noteworthy that `EMQX_DEFAULT_LOG_HANDLER` is set to `file` when EMQX is initiated via systemd `emqx.service` file. +## In scenarios outside systemd initiation, `console` serves as the default log handler. + +## Read more about configs here: https://www.emqx.io/docs/en/latest/configuration/logs.html + +log { + # file { + # level = warning + # } + # console { + # level = warning + # } +} +dashboard { + listeners { + http { + ## Comment out 'bind' (or set bind=0) to disable listener. + bind = 18083 + } + https { + ## Uncomment to enable + # bind = 18084 + ssl_options { + certfile = "${EMQX_ETC_DIR}/certs/cert.pem" + keyfile = "${EMQX_ETC_DIR}/certs/key.pem" + } + } + } +} + +# 必须禁止匿名访问,是开启认证的必备步骤 +allow_anonymous = false + +# 启用http协议的认证方式 +authentication = [ + { + # 后端类型:http 服务 + backend = "http" + enable = true + mechanism = "password_based" + + # ----- 请求配置 ----- + # 请求方法:POST 或 GET(推荐 POST,更安全) + method = "post" + # 你的认证服务 URL(请替换为实际地址) + url = "http://dj-multictrl-api:8080/api/mqtt/auth" + + # 请求头 + headers { + "Content-Type" = "application/json" + "Accept" = "application/json" + } + + # 请求体模板(支持占位符) + body { + username = "${username}" + password = "${password}" + } + + # ----- 性能与超时 ----- + request_timeout = "5s" # HTTP 请求超时时间 + pool_size = 8 # 连接池大小 + + # ----- 认证结果判断 ----- + # 服务端需返回 JSON 格式,包含 result 字段: + # {"result": "allow"} → 允许连接 + # {"result": "deny"} → 拒绝连接 + # {"result": "ignore"} → 忽略,继续后续认证链 + # 如果返回 HTTP 4xx/5xx 状态码,视为 ignore + } +] diff --git a/prj-deploy/file/emqx2/etc/emqx.conf b/prj-deploy/file/emqx2/etc/emqx.conf new file mode 100644 index 0000000..7684ce7 --- /dev/null +++ b/prj-deploy/file/emqx2/etc/emqx.conf @@ -0,0 +1,99 @@ +## NOTE: +## This config file overrides data/configs/cluster.hocon, +## and is merged with environment variables which start with 'EMQX_' prefix. +## +## Config changes made from EMQX dashboard UI, management HTTP API, or CLI +## are stored in data/configs/cluster.hocon. +## To avoid confusion, please do not store the same configs in both files. +## +## See https://www.emqx.io/docs/en/latest/configuration/configuration.html for more details. +## Configuration full example can be found in etc/examples + +node { + name = "emqx@127.0.0.1" + cookie = "emqxsecretcookie" + data_dir = "data" +} + +cluster { + name = emqxcl + discovery_strategy = manual +} + +## EMQX provides support for two primary log handlers: `file` and `console`, with an additional `audit` handler specifically designed to always direct logs to files. +## The system's default log handling behavior can be configured via the environment variable `EMQX_DEFAULT_LOG_HANDLER`, which accepts the following settings: +## +## - `file`: Directs log output exclusively to files. +## - `console`: Channels log output solely to the console. +## +## It's noteworthy that `EMQX_DEFAULT_LOG_HANDLER` is set to `file` when EMQX is initiated via systemd `emqx.service` file. +## In scenarios outside systemd initiation, `console` serves as the default log handler. + +## Read more about configs here: https://www.emqx.io/docs/en/latest/configuration/logs.html + +log { + # file { + # level = warning + # } + # console { + # level = warning + # } +} +dashboard { + listeners { + http { + ## Comment out 'bind' (or set bind=0) to disable listener. + bind = 18083 + } + https { + ## Uncomment to enable + # bind = 18084 + ssl_options { + certfile = "${EMQX_ETC_DIR}/certs/cert.pem" + keyfile = "${EMQX_ETC_DIR}/certs/key.pem" + } + } + } +} + +# 必须禁止匿名访问,是开启认证的必备步骤 +allow_anonymous = false + +# 启用http协议的认证方式 +authentication = [ + { + # 后端类型:http 服务 + backend = "http" + enable = true + mechanism = "password_based" + + # ----- 请求配置 ----- + # 请求方法:POST 或 GET(推荐 POST,更安全) + method = "post" + # 你的认证服务 URL(请替换为实际地址) + url = "http://dj-multictrl-api:8080/api/mqtt/auth" + + # 请求头 + headers { + "Content-Type" = "application/json" + "Accept" = "application/json" + } + + # 请求体模板(支持占位符) + body { + username = "${username}" + password = "${password}" + } + + # ----- 性能与超时 ----- + request_timeout = "5s" # HTTP 请求超时时间 + pool_size = 8 # 连接池大小 + + # ----- 认证结果判断 ----- + # 服务端需返回 JSON 格式,包含 result 字段: + # {"result": "allow"} → 允许连接 + # {"result": "deny"} → 拒绝连接 + # {"result": "ignore"} → 忽略,继续后续认证链 + # 如果返回 HTTP 4xx/5xx 状态码,视为 ignore + } +]