parent
d92c6e1f2f
commit
dbee9fe94a
|
|
@ -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,8 +40,8 @@ public class DataFilterAspect {
|
|||
|
||||
@Before("dataFilterCut()")
|
||||
public void dataFilter(JoinPoint point) {
|
||||
Object params = point.getArgs()[0];
|
||||
if (params instanceof Map) {
|
||||
// Object params = point.getArgs()[0];
|
||||
// if (params != null && params instanceof Map) {
|
||||
UserDetail user = SecurityUser.getUser();
|
||||
|
||||
//如果是超级管理员,则不进行数据过滤
|
||||
|
|
@ -46,19 +49,30 @@ public class DataFilterAspect {
|
|||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
/* 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 ignored) {
|
||||
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
||||
}*/
|
||||
try {
|
||||
String sqlFilter = getSqlFilter(user, point);
|
||||
// 放入 ThreadLocal
|
||||
DataScopeContext.setDataScope(new DataScope(sqlFilter));
|
||||
return;
|
||||
} catch (Exception e) {
|
||||
throw new RenException(e.getMessage());
|
||||
}
|
||||
|
||||
throw new RenException(ErrorCode.DATA_SCOPE_PARAMS_ERROR);
|
||||
// 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<Long> 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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<DataScope> 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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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<DockEntity, DockDTO> {
|
|||
//分页查询
|
||||
PageData<DockDTO> pageList(Map<String, Object> params);
|
||||
|
||||
//获取机库列表
|
||||
List<DockDTO> getUserDockList(Long userId);
|
||||
|
||||
//获取dao
|
||||
DockDao getDao();
|
||||
}
|
||||
|
|
@ -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<DockDao, DockEntity, DockDT
|
|||
private final DJIBaseService djiBaseService;
|
||||
private final DockDeviceDao dockDeviceDao;
|
||||
private final DeviceDicDao deviceDicDao;
|
||||
private final ShiroService shiroService;
|
||||
|
||||
@Override
|
||||
public QueryWrapper<DockEntity> getWrapper(Map<String, Object> params) {
|
||||
|
|
@ -93,6 +98,17 @@ public class DockServiceImpl extends CrudServiceImpl<DockDao, DockEntity, DockDT
|
|||
return page;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DockDTO> getUserDockList(Long userId) {
|
||||
List<Long> dataScopeList = shiroService.getDataScopeList(userId);
|
||||
List<DockEntity> list = new ArrayList<>();
|
||||
if(CollUtil.isNotEmpty(dataScopeList)){
|
||||
list = baseDao.selectList(new QueryWrapper<DockEntity>().in("dept_id", dataScopeList));
|
||||
}
|
||||
|
||||
return ConvertUtils.sourceToTarget(list, DockDTO.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DockDao getDao() {
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Object>();
|
||||
}
|
||||
|
||||
@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<MqttAuthVO.Acl> aclList = new ArrayList<>();
|
||||
List<DockDTO> 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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> 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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<String> permsSet;
|
||||
Object object = CacheUtils.get(user.getToken() + "_perms");
|
||||
if (object == null) {
|
||||
//用户权限列表
|
||||
Set<String> permsSet = shiroService.getUserPermissions(user);
|
||||
permsSet = shiroService.getUserPermissions(user);
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("perms", JsonUtils.toJsonString(permsSet));
|
||||
CacheUtils.set(user.getToken() + "_perms", map);
|
||||
} else {
|
||||
String perms = (String) ((Map<String, Object>) object).get("perms");
|
||||
permsSet = JsonUtils.parseObject(perms, Set.class);
|
||||
}
|
||||
|
||||
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
|
||||
info.setStringPermissions(permsSet);
|
||||
|
|
@ -55,8 +69,12 @@ public class Oauth2Realm extends AuthorizingRealm {
|
|||
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
|
||||
String accessToken = (String) token.getPrincipal();
|
||||
|
||||
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));
|
||||
|
|
@ -64,21 +82,33 @@ public class Oauth2Realm extends AuthorizingRealm {
|
|||
|
||||
//查询用户信息
|
||||
SysUserEntity userEntity = shiroService.getUser(tokenEntity.getUserId());
|
||||
|
||||
//转换成UserDetail对象
|
||||
UserDetail userDetail = ConvertUtils.sourceToTarget(userEntity, UserDetail.class);
|
||||
|
||||
//获取用户对应的部门数据权限
|
||||
List<Long> deptIdList = shiroService.getDataScopeList(userDetail.getId());
|
||||
userDetail.setDeptIdList(deptIdList);
|
||||
|
||||
userDetail = ConvertUtils.sourceToTarget(userEntity, UserDetail.class);
|
||||
userDetail.setToken(accessToken);
|
||||
//账号锁定
|
||||
if (userDetail.getStatus() == 0) {
|
||||
throw new LockedAccountException(MessageUtils.getMessage(ErrorCode.ACCOUNT_LOCK));
|
||||
}
|
||||
|
||||
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(userDetail, accessToken, getName());
|
||||
return info;
|
||||
//获取用户对应的部门数据权限
|
||||
List<Long> deptIdList = shiroService.getDataScopeList(userDetail.getId());
|
||||
userDetail.setDeptIdList(deptIdList);
|
||||
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("user", userDetail);
|
||||
map.put("token", tokenEntity);
|
||||
CacheUtils.set(accessToken, map, 1000 * 60);
|
||||
} else {
|
||||
userDetail = (UserDetail) ((Map<String, Object>) object).get("user");
|
||||
SysUserTokenEntity tokenEntity = (SysUserTokenEntity) ((Map<String, Object>) object).get("token");
|
||||
|
||||
//token失效
|
||||
if (tokenEntity == null || tokenEntity.getExpireDate().getTime() < System.currentTimeMillis()) {
|
||||
throw new IncorrectCredentialsException(MessageUtils.getMessage(ErrorCode.TOKEN_INVALID));
|
||||
}
|
||||
}
|
||||
|
||||
return new SimpleAuthenticationInfo(userDetail, accessToken, getName());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -32,4 +32,7 @@ public class UserDetail implements Serializable {
|
|||
*/
|
||||
private List<Long> deptIdList;
|
||||
|
||||
//token
|
||||
private String token;
|
||||
|
||||
}
|
||||
|
|
@ -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)}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
]
|
||||
|
|
@ -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
|
||||
}
|
||||
]
|
||||
Loading…
Reference in New Issue