wanggaokun 1 rok pred
rodič
commit
2c0b965f69

+ 1 - 1
eco-common/common-core/src/main/java/org/eco/common/core/config/ThreadPoolConfig.java

@@ -1,9 +1,9 @@
 package org.eco.common.core.config;
 
-import org.eco.common.core.utils.Threads;
 import jakarta.annotation.PreDestroy;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.concurrent.BasicThreadFactory;
+import org.eco.common.core.utils.Threads;
 import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.context.annotation.Bean;
 

+ 10 - 0
eco-common/common-core/src/main/java/org/eco/common/core/core/domain/dto/UserOnlineDTO.java

@@ -58,5 +58,15 @@ public class UserOnlineDTO implements Serializable {
      * 登录时间
      */
     private Long loginTime;
+    /**
+     * 客户端
+     */
+    private String clientKey;
+
+    /**
+     * 设备类型
+     */
+    private String deviceType;
+
 
 }

+ 5 - 2
eco-common/common-core/src/main/java/org/eco/common/core/utils/ServletUtils.java

@@ -1,12 +1,14 @@
 package org.eco.common.core.utils;
 
 import cn.hutool.extra.servlet.JakartaServletUtil;
-import org.eco.common.core.constant.Constants;
-import org.eco.common.core.core.text.Convert;
 import jakarta.servlet.ServletRequest;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 import jakarta.servlet.http.HttpSession;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import org.eco.common.core.constant.Constants;
+import org.eco.common.core.core.text.Convert;
 import org.springframework.web.context.request.RequestAttributes;
 import org.springframework.web.context.request.RequestContextHolder;
 import org.springframework.web.context.request.ServletRequestAttributes;
@@ -24,6 +26,7 @@ import java.util.Map;
  *
  * @author wgk
  */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
 public class ServletUtils extends JakartaServletUtil {
     /**
      * 获取String参数

+ 1 - 0
eco-common/common-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

@@ -3,3 +3,4 @@ org.eco.common.core.config.AsyncConfig
 org.eco.common.core.config.EcoConfig
 org.eco.common.core.config.ThreadPoolConfig
 org.eco.common.core.config.ValidatorConfig
+org.eco.common.core.utils.SpringUtils

+ 31 - 7
eco-common/common-security/src/main/java/org/eco/common/security/config/SecurityConfig.java

@@ -1,13 +1,17 @@
 package org.eco.common.security.config;
 
+import cn.dev33.satoken.exception.NotLoginException;
 import cn.dev33.satoken.interceptor.SaInterceptor;
 import cn.dev33.satoken.router.SaRouter;
 import cn.dev33.satoken.stp.StpUtil;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.eco.common.core.utils.ServletUtils;
 import org.eco.common.core.utils.SpringUtils;
+import org.eco.common.core.utils.StringUtils;
 import org.eco.common.security.config.properties.SecurityProperties;
 import org.eco.common.security.handler.AllUrlHandler;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
+import org.eco.common.security.utils.LoginHelper;
 import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
@@ -32,16 +36,36 @@ public class SecurityConfig implements WebMvcConfigurer {
      */
     @Override
     public void addInterceptors(InterceptorRegistry registry) {
+
         // 注册路由拦截器,自定义验证规则
         registry.addInterceptor(new SaInterceptor(handler -> {
                 AllUrlHandler allUrlHandler = SpringUtils.getBean(AllUrlHandler.class);
                 // 登录验证 -- 排除多个路径
-                    // 检查是否登录 是否有token
-                    //TODO :以后完善多平台登录校验clientID功能
-                    SaRouter
+                SaRouter
                     // 获取所有的
-                    .match(allUrlHandler.getUrls())  // 拦截的 path 列表
-                    .check(StpUtil::checkLogin);
+                    .match(allUrlHandler.getUrls())
+                    // 对未排除的路径进行检查
+                    .check(() -> {
+                        // 检查是否登录 是否有token
+                        StpUtil.checkLogin();
+                        // 检查 header 与 param 里的 clientid 与 token 里的是否一致
+                        String headerCid = ServletUtils.getRequest().getHeader(LoginHelper.CLIENT_KEY);
+                        String paramCid = ServletUtils.getParameter(LoginHelper.CLIENT_KEY);
+                        String clientId = StpUtil.getExtra(LoginHelper.CLIENT_KEY).toString();
+                        if (!StringUtils.equalsAny(clientId, headerCid, paramCid)) {
+                            // token 无效
+                            throw NotLoginException.newInstance(StpUtil.getLoginType(),
+                                "-100", "客户端ID与Token不匹配",
+                                StpUtil.getTokenValue());
+                        }
+
+                        // 有效率影响 用于临时测试
+                        // if (log.isDebugEnabled()) {
+                        //     log.info("剩余有效时间: {}", StpUtil.getTokenTimeout());
+                        //     log.info("临时有效时间: {}", StpUtil.getTokenActivityTimeout());
+                        // }
+
+                    });
             })).addPathPatterns("/**")
             // 排除不需要拦截的路径
             .excludePathPatterns(securityProperties.getExcludes());

+ 4 - 2
eco-common/common-security/src/main/java/org/eco/common/security/listener/UserActionListener.java

@@ -5,6 +5,8 @@ import cn.dev33.satoken.listener.SaTokenListener;
 import cn.dev33.satoken.stp.SaLoginModel;
 import cn.hutool.http.useragent.UserAgent;
 import cn.hutool.http.useragent.UserAgentUtil;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.eco.common.core.constant.CacheConstants;
 import org.eco.common.core.core.domain.dto.UserOnlineDTO;
 import org.eco.common.core.core.domain.model.LoginUser;
@@ -13,8 +15,6 @@ import org.eco.common.core.utils.ServletUtils;
 import org.eco.common.core.utils.ip.AddressUtils;
 import org.eco.common.redis.utils.RedisUtils;
 import org.eco.common.security.utils.LoginHelper;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Component;
 
 import java.time.Duration;
@@ -48,6 +48,8 @@ public class UserActionListener implements SaTokenListener {
             dto.setOs(userAgent.getOs().getName());
             dto.setLoginTime(System.currentTimeMillis());
             dto.setTokenId(tokenValue);
+            dto.setClientKey(user.getClientKey());
+            dto.setDeviceType(loginModel.getDevice());
             dto.setUserName(user.getUsername());
             dto.setDeptName(user.getDeptName());
             if (tokenConfig.getTimeout() == -1) {

+ 32 - 0
eco-modules/system/src/main/java/org/eco/system/controller/monitor/WeSocketController.java

@@ -0,0 +1,32 @@
+package org.eco.system.controller.monitor;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.eco.common.core.core.domain.CommonResult;
+import org.eco.common.websocket.dto.WebSocketMessageDto;
+import org.eco.common.websocket.utils.WebSocketUtils;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @Description: WeSocketController
+ * @Author: GaoKun Wang
+ * @Date: 2024/7/1
+ */
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/demo/websocket")
+@Slf4j
+public class WeSocketController {
+    /**
+     * 发布消息
+     *
+     * @param dto 发送内容
+     */
+    @GetMapping("/send")
+    public CommonResult<Void> send(WebSocketMessageDto dto) throws InterruptedException {
+        WebSocketUtils.publishMessage(dto);
+        return CommonResult.success("操作成功");
+    }
+}

+ 34 - 19
eco-start/src/main/java/org/eco/web/controller/AuthController.java

@@ -3,11 +3,11 @@ package org.eco.web.controller;
 import cn.dev33.satoken.annotation.SaIgnore;
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.ObjectUtil;
-import org.eco.web.domain.vo.LoginTenantVo;
-import org.eco.web.domain.vo.LoginVo;
-import org.eco.web.domain.vo.TenantListVo;
-import org.eco.web.service.IAuthStrategy;
-import org.eco.web.service.SysLoginService;
+import com.mybatisflex.core.query.QueryWrapper;
+import jakarta.annotation.Resource;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.eco.common.core.constant.UserConstants;
 import org.eco.common.core.core.domain.CommonResult;
 import org.eco.common.core.core.domain.model.LoginBody;
@@ -19,16 +19,19 @@ import org.eco.common.core.utils.StringUtils;
 import org.eco.common.core.utils.ValidatorUtils;
 import org.eco.common.encrypt.annotation.ApiEncrypt;
 import org.eco.common.json.utils.JsonUtils;
+import org.eco.common.security.utils.LoginHelper;
+import org.eco.common.websocket.dto.WebSocketMessageDto;
+import org.eco.common.websocket.utils.WebSocketUtils;
 import org.eco.system.domain.SysClient;
 import org.eco.system.domain.bo.SysTenantBo;
 import org.eco.system.domain.vo.SysTenantVo;
 import org.eco.system.service.ISysClientService;
 import org.eco.system.service.ISysTenantService;
-import com.mybatisflex.core.query.QueryWrapper;
-import jakarta.annotation.Resource;
-import jakarta.servlet.http.HttpServletRequest;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
+import org.eco.web.domain.vo.LoginTenantVo;
+import org.eco.web.domain.vo.LoginVo;
+import org.eco.web.domain.vo.TenantListVo;
+import org.eco.web.service.IAuthStrategy;
+import org.eco.web.service.SysLoginService;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PostMapping;
@@ -38,6 +41,8 @@ import org.springframework.web.bind.annotation.RestController;
 
 import java.net.URL;
 import java.util.List;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
 
 import static org.eco.system.domain.table.SysClientTableDef.SYS_CLIENT;
 
@@ -61,6 +66,8 @@ public class AuthController {
     private ISysClientService clientService;
     @Resource
     private ISysTenantService tenantService;
+    @Resource
+    private ScheduledExecutorService scheduledExecutorService;
 
     /**
      * 登录方法
@@ -86,11 +93,19 @@ public class AuthController {
         } else if (!UserConstants.NORMAL.equals(client.getStatus())) {
             return CommonResult.fail(MessageUtils.message("auth.grant.type.blocked"));
         }
-        // 校验租户 TODO 不需要校验租户
-        // loginService.checkTenant(loginBody.getTenantId());
-
+        // 校验租户
+        loginService.checkTenant(loginBody.getTenantId());
+        // 登录
+        LoginVo loginVo = IAuthStrategy.login(body, client, grantType);
+        Long userId = LoginHelper.getUserId();
+        scheduledExecutorService.schedule(() -> {
+            WebSocketMessageDto dto = new WebSocketMessageDto();
+            dto.setMessage("欢迎登录");
+            dto.setSessionKeys(List.of(userId));
+            WebSocketUtils.publishMessage(dto);
+        }, 3, TimeUnit.SECONDS);
         // 登录
-        return CommonResult.success(IAuthStrategy.login(body, client, grantType));
+        return CommonResult.success(loginVo);
     }
 
     /**
@@ -108,9 +123,10 @@ public class AuthController {
     @PostMapping("/register")
     public CommonResult<Void> register(@Validated @RequestBody RegisterBody user) {
         //if (!configService.selectRegisterEnabled(user.getTenantId())) // TODO:注册代码
-        {
-            return CommonResult.fail("当前系统没有开启注册功能!");
-        }
+//        {
+        log.info(user.getUserType());
+        return CommonResult.fail("当前系统没有开启注册功能!");
+//        }
 //        registerService.register(user);
 //        return R.ok();
     }
@@ -134,8 +150,7 @@ public class AuthController {
             host = new URL(request.getRequestURL().toString()).getHost();
         }
         // 根据域名进行筛选
-        List<TenantListVo> list = StreamUtils.filter(voList, vo ->
-            StringUtils.equals(vo.getDomain(), host));
+        List<TenantListVo> list = StreamUtils.filter(voList, vo -> StringUtils.equals(vo.getDomain(), host));
         // 返回对象
         LoginTenantVo vo = new LoginTenantVo();
         vo.setTenantEnabled(true);

+ 27 - 22
eco-start/src/main/java/org/eco/web/service/SysLoginService.java

@@ -4,11 +4,16 @@ import cn.dev33.satoken.exception.NotLoginException;
 import cn.dev33.satoken.stp.StpUtil;
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.util.ObjectUtil;
+import jakarta.annotation.Resource;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.eco.common.core.constant.Constants;
 import org.eco.common.core.constant.GlobalConstants;
+import org.eco.common.core.constant.TenantConstants;
 import org.eco.common.core.core.domain.dto.RoleDTO;
 import org.eco.common.core.core.domain.model.LoginUser;
 import org.eco.common.core.enums.LoginType;
+import org.eco.common.core.enums.TenantStatus;
 import org.eco.common.core.exception.user.UserException;
 import org.eco.common.core.utils.DateUtils;
 import org.eco.common.core.utils.MessageUtils;
@@ -17,19 +22,19 @@ import org.eco.common.core.utils.SpringUtils;
 import org.eco.common.log.event.LogininforEvent;
 import org.eco.common.redis.utils.RedisUtils;
 import org.eco.common.security.utils.LoginHelper;
+import org.eco.common.tenant.exception.TenantException;
 import org.eco.common.tenant.helper.TenantHelper;
 import org.eco.system.domain.bo.SysUserBo;
+import org.eco.system.domain.vo.SysTenantVo;
 import org.eco.system.domain.vo.SysUserVo;
 import org.eco.system.service.ISysPermissionService;
 import org.eco.system.service.ISysTenantService;
 import org.eco.system.service.ISysUserService;
-import jakarta.annotation.Resource;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 
 import java.time.Duration;
+import java.util.Date;
 import java.util.List;
 import java.util.function.Supplier;
 
@@ -98,25 +103,25 @@ public class SysLoginService {
      * @param tenantId 租户编号
      */
     public void checkTenant(Long tenantId) {
-//        if (TenantConstants.DEFAULT_TENANT_ID.equals(tenantId)) {
-//            return;
-//        }
-//        if (ObjectUtil.isNull(tenantId)) {
-//            throw new TenantException("tenant.number.not.blank");
-//        }
-//        SysTenantVo tenant = tenantService.selectById(tenantId);
-//
-//        if (ObjectUtil.isNull(tenant)) {
-//            log.info("登录租户:{} 不存在.", tenantId);
-//            throw new TenantException("tenant.not.exists");
-//        } else if (TenantStatus.DISABLE.getCode().equals(tenant.getStatus())) {
-//            log.info("登录租户:{} 已被停用.", tenantId);
-//            throw new TenantException("tenant.blocked");
-//        } else if (ObjectUtil.isNotNull(tenant.getExpireTime())
-//            && new Date().after(tenant.getExpireTime())) {
-//            log.info("登录租户:{} 已超过有效期.", tenantId);
-//            throw new TenantException("tenant.expired");
-//        }
+        if (TenantConstants.DEFAULT_TENANT_ID.equals(tenantId)) {
+            return;
+        }
+        if (ObjectUtil.isNull(tenantId)) {
+            throw new TenantException("tenant.number.not.blank");
+        }
+        SysTenantVo tenant = tenantService.selectById(tenantId);
+
+        if (ObjectUtil.isNull(tenant)) {
+            log.info("登录租户:{} 不存在.", tenantId);
+            throw new TenantException("tenant.not.exists");
+        } else if (TenantStatus.DISABLE.getCode().equals(tenant.getStatus())) {
+            log.info("登录租户:{} 已被停用.", tenantId);
+            throw new TenantException("tenant.blocked");
+        } else if (ObjectUtil.isNotNull(tenant.getExpireTime())
+            && new Date().after(tenant.getExpireTime())) {
+            log.info("登录租户:{} 已超过有效期.", tenantId);
+            throw new TenantException("tenant.expired");
+        }
     }
 
     /**