浏览代码

将 LoginUser 重构到 UserSessionService 模块汇总

YunaiV 4 年之前
父节点
当前提交
ab94fe2d4b
共有 21 个文件被更改,包括 314 次插入359 次删除
  1. 0 7
      pom.xml
  2. 0 30
      ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java
  3. 0 92
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java
  4. 0 1
      ruoyi-admin/src/main/resources/META-INF/spring-devtools.properties
  5. 0 14
      ruoyi-admin/src/main/resources/application.yml
  6. 0 3
      src/main/java/cn/iocoder/dashboard/framework/mybatis/core/dataobject/BaseDO.java
  7. 3 1
      src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/SysAuthController.java
  8. 49 0
      src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/SysUserSessionController.java
  9. 36 0
      src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/vo/session/SysUserSessionPageItemRespVO.java
  10. 11 0
      src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/vo/session/SysUserSessionPageReqVO.java
  11. 3 1
      src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/auth/SysUserSessionMapper.java
  12. 9 0
      src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/auth/SysUserSessionDO.java
  13. 10 1
      src/main/java/cn/iocoder/dashboard/modules/system/service/auth/SysAuthService.java
  14. 0 30
      src/main/java/cn/iocoder/dashboard/modules/system/service/auth/SysTokenService.java
  15. 0 40
      src/main/java/cn/iocoder/dashboard/modules/system/service/auth/SysUserOnlineService.java
  16. 63 0
      src/main/java/cn/iocoder/dashboard/modules/system/service/auth/SysUserSessionService.java
  17. 15 57
      src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysAuthServiceImpl.java
  18. 0 58
      src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysTokenServiceImpl.java
  19. 89 0
      src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysUserSessionServiceImpl.java
  20. 1 0
      src/main/resources/application.yaml
  21. 25 24
      src/main/resources/banner.txt

+ 0 - 7
pom.xml

@@ -53,7 +53,6 @@
         <!-- 工具类相关 -->
         <lombok.version>1.16.14</lombok.version>
         <mapstruct.version>1.4.1.Final</mapstruct.version>
-        <jjwt.version>0.9.1</jjwt.version>
         <hutool.version>5.5.6</hutool.version>
         <easyexcel.verion>2.2.7</easyexcel.verion>
     </properties>
@@ -177,12 +176,6 @@
             <version>${lombok.version}</version>
         </dependency>
 
-        <dependency>
-            <groupId>io.jsonwebtoken</groupId>
-            <artifactId>jjwt</artifactId>
-            <version>${jjwt.version}</version>
-        </dependency>
-
         <dependency>
             <groupId>org.projectlombok</groupId>
             <artifactId>lombok</artifactId>

+ 0 - 30
ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java

@@ -1,30 +0,0 @@
-package com.ruoyi;
-
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
-
-/**
- * 启动程序
- * 
- * @author ruoyi
- */
-@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
-public class RuoYiApplication
-{
-    public static void main(String[] args)
-    {
-        // System.setProperty("spring.devtools.restart.enabled", "false");
-        SpringApplication.run(RuoYiApplication.class, args);
-        System.out.println("(♥◠‿◠)ノ゙  若依启动成功   ლ(´ڡ`ლ)゙  \n" +
-                " .-------.       ____     __        \n" +
-                " |  _ _   \\      \\   \\   /  /    \n" +
-                " | ( ' )  |       \\  _. /  '       \n" +
-                " |(_ o _) /        _( )_ .'         \n" +
-                " | (_,_).' __  ___(_ o _)'          \n" +
-                " |  |\\ \\  |  ||   |(_,_)'         \n" +
-                " |  | \\ `'   /|   `-'  /           \n" +
-                " |  |  \\    /  \\      /           \n" +
-                " ''-'   `'-'    `-..-'              ");
-    }
-}

+ 0 - 92
ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java

@@ -1,92 +0,0 @@
-package com.ruoyi.web.controller.monitor;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-import com.ruoyi.common.annotation.Log;
-import com.ruoyi.common.constant.Constants;
-import com.ruoyi.common.core.controller.BaseController;
-import com.ruoyi.common.core.domain.AjaxResult;
-import com.ruoyi.common.core.domain.model.LoginUser;
-import com.ruoyi.common.core.page.TableDataInfo;
-import com.ruoyi.common.core.redis.RedisCache;
-import com.ruoyi.common.enums.BusinessType;
-import com.ruoyi.common.utils.StringUtils;
-import com.ruoyi.system.domain.SysUserOnline;
-import com.ruoyi.system.service.ISysUserOnlineService;
-
-/**
- * 在线用户监控
- * 
- * @author ruoyi
- */
-@RestController
-@RequestMapping("/monitor/online")
-public class SysUserOnlineController extends BaseController
-{
-    @Autowired
-    private ISysUserOnlineService userOnlineService;
-
-    @Autowired
-    private RedisCache redisCache;
-
-    @PreAuthorize("@ss.hasPermi('monitor:online:list')")
-    @GetMapping("/list")
-    public TableDataInfo list(String ipaddr, String userName)
-    {
-        Collection<String> keys = redisCache.keys(Constants.LOGIN_TOKEN_KEY + "*");
-        List<SysUserOnline> userOnlineList = new ArrayList<SysUserOnline>();
-        for (String key : keys)
-        {
-            LoginUser user = redisCache.getCacheObject(key);
-            if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName))
-            {
-                if (StringUtils.equals(ipaddr, user.getIpaddr()) && StringUtils.equals(userName, user.getUsername()))
-                {
-                    userOnlineList.add(userOnlineService.selectOnlineByInfo(ipaddr, userName, user));
-                }
-            }
-            else if (StringUtils.isNotEmpty(ipaddr))
-            {
-                if (StringUtils.equals(ipaddr, user.getIpaddr()))
-                {
-                    userOnlineList.add(userOnlineService.selectOnlineByIpaddr(ipaddr, user));
-                }
-            }
-            else if (StringUtils.isNotEmpty(userName) && StringUtils.isNotNull(user.getUser()))
-            {
-                if (StringUtils.equals(userName, user.getUsername()))
-                {
-                    userOnlineList.add(userOnlineService.selectOnlineByUserName(userName, user));
-                }
-            }
-            else
-            {
-                userOnlineList.add(userOnlineService.loginUserToUserOnline(user));
-            }
-        }
-        Collections.reverse(userOnlineList);
-        userOnlineList.removeAll(Collections.singleton(null));
-        return getDataTable(userOnlineList);
-    }
-
-    /**
-     * 强退用户
-     */
-    @PreAuthorize("@ss.hasPermi('monitor:online:forceLogout')")
-    @Log(title = "在线用户", businessType = BusinessType.FORCE)
-    @DeleteMapping("/{tokenId}")
-    public AjaxResult forceLogout(@PathVariable String tokenId)
-    {
-        redisCache.deleteObject(Constants.LOGIN_TOKEN_KEY + tokenId);
-        return AjaxResult.success();
-    }
-}

+ 0 - 1
ruoyi-admin/src/main/resources/META-INF/spring-devtools.properties

@@ -1 +0,0 @@
-restart.include.json=/com.alibaba.fastjson.*.jar

+ 0 - 14
ruoyi-admin/src/main/resources/application.yml

@@ -34,20 +34,6 @@ logging:
     com.ruoyi: debug
     org.springframework: warn
 
-# Spring配置
-spring:
-  # 资源信息
-  messages:
-    # 国际化资源文件路径
-    basename: i18n/messages
-  profiles:
-    active: druid
-  # 服务模块
-  devtools:
-    restart:
-      # 热部署开关
-      enabled: true
-
 # 防止XSS攻击
 xss:
   # 过滤开关

+ 0 - 3
src/main/java/cn/iocoder/dashboard/framework/mybatis/core/dataobject/BaseDO.java

@@ -34,7 +34,4 @@ public class BaseDO implements Serializable {
     @TableLogic
     private Integer deleted;
 
-//    /** 备注 */ TODO 思考下,怎么解决
-//    private String remark;
-
 }

+ 3 - 1
src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/SysAuthController.java

@@ -28,6 +28,8 @@ import java.util.List;
 import static cn.iocoder.dashboard.common.pojo.CommonResult.success;
 import static cn.iocoder.dashboard.framework.security.core.util.SecurityUtils.getLoginUserId;
 import static cn.iocoder.dashboard.framework.security.core.util.SecurityUtils.getLoginUserRoleIds;
+import static cn.iocoder.dashboard.util.servlet.ServletUtils.getClientIP;
+import static cn.iocoder.dashboard.util.servlet.ServletUtils.getUserAgent;
 
 @Api("认证 API")
 @RestController
@@ -47,7 +49,7 @@ public class SysAuthController {
     @PostMapping("/login")
     @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
     public CommonResult<SysAuthLoginRespVO> login(@RequestBody @Valid SysAuthLoginReqVO reqVO) {
-        String token = authService.login(reqVO.getUsername(), reqVO.getPassword(), reqVO.getUuid(), reqVO.getCode());
+        String token = authService.login(reqVO, getClientIP(), getUserAgent());
         // 返回结果
         return success(SysAuthLoginRespVO.builder().token(token).build());
     }

+ 49 - 0
src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/SysUserSessionController.java

@@ -0,0 +1,49 @@
+package cn.iocoder.dashboard.modules.system.controller.auth;
+
+import cn.iocoder.dashboard.common.pojo.CommonResult;
+import cn.iocoder.dashboard.common.pojo.PageResult;
+import cn.iocoder.dashboard.modules.system.controller.auth.vo.session.SysUserSessionPageItemRespVO;
+import cn.iocoder.dashboard.modules.system.controller.auth.vo.session.SysUserSessionPageReqVO;
+import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.auth.SysUserSessionDO;
+import cn.iocoder.dashboard.modules.system.service.auth.SysUserSessionService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+
+import static cn.iocoder.dashboard.common.pojo.CommonResult.success;
+
+@Api("用户 Session API")
+@RestController
+@RequestMapping("/user-session")
+public class SysUserSessionController {
+
+    @Resource
+    private SysUserSessionService userSessionService;
+
+    @ApiOperation("获得 Session 分页列表")
+    @PreAuthorize("@ss.hasPermission('system:user-session:page')")
+    @GetMapping("/page")
+    public CommonResult<PageResult<SysUserSessionPageItemRespVO>> getUserSessionPage(@Validated SysUserSessionPageReqVO reqVO) {
+        // 获得 Session 分页
+        PageResult<SysUserSessionDO> sessionPage = userSessionService.getUserSessionPage(reqVO);
+
+        //
+        return null;
+    }
+
+    @ApiOperation("删除 Session")
+    @PreAuthorize("@ss.hasPermission('system:user-session:delete')")
+    @DeleteMapping("/delete")
+    @ApiImplicitParam(name = "id", value = "Session 编号", required = true, dataTypeClass = String.class,
+            example = "fe50b9f6-d177-44b1-8da9-72ea34f63db7")
+    public CommonResult<Boolean> delete(@RequestParam("id") String id) {
+        userSessionService.deleteUserSession(id);
+        return success(true);
+    }
+
+}

+ 36 - 0
src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/vo/session/SysUserSessionPageItemRespVO.java

@@ -0,0 +1,36 @@
+package cn.iocoder.dashboard.modules.system.controller.auth.vo.session;
+
+import cn.iocoder.dashboard.common.pojo.PageParam;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+@ApiModel(value = "用户在线 Session Response VO", description = "相比用户基本信息来说,会多部门、用户账号等信息")
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@EqualsAndHashCode(callSuper = true)
+public class SysUserSessionPageItemRespVO extends PageParam {
+
+    @ApiModelProperty(value = "Session 编号", required = true, example = "fe50b9f6-d177-44b1-8da9-72ea34f63db7")
+    private String id;
+
+    @ApiModelProperty(value = "用户 IP", required = true, example = "127.0.0.1")
+    private String userIp;
+
+    @ApiModelProperty(value = "浏览器 UserAgent", required = true, example = "Mozilla/5.0")
+    private String userAgent;
+
+    @ApiModelProperty(value = "登陆时间", required = true)
+    private String createTime;
+
+    @ApiModelProperty(value = "用户账号", required = true, example = "yudao")
+    private String username;
+
+    @ApiModelProperty(value = "部门名称", example = "研发部")
+    private String deptName;
+
+}

+ 11 - 0
src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/vo/session/SysUserSessionPageReqVO.java

@@ -2,11 +2,22 @@ package cn.iocoder.dashboard.modules.system.controller.auth.vo.session;
 
 import cn.iocoder.dashboard.common.pojo.PageParam;
 import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 
+import javax.validation.constraints.NotEmpty;
+
 @ApiModel("在线用户 Session 分页 Request VO")
 @Data
 @EqualsAndHashCode(callSuper = true)
 public class SysUserSessionPageReqVO extends PageParam {
+
+    @ApiModelProperty(value = "用户 IP", example = "127.0.0.1", notes = "模糊匹配")
+    @NotEmpty(message = "用户 IP 不能为空")
+    private String userIp;
+
+    @ApiModelProperty(value = "用户账号", example = "yudao", notes = "模糊匹配")
+    private String username;
+
 }

+ 3 - 1
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/auth/SysUserOnlineMapper.java → src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/auth/SysUserSessionMapper.java

@@ -2,6 +2,8 @@ package cn.iocoder.dashboard.modules.system.dal.mysql.dao.auth;
 
 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.auth.SysUserSessionDO;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
 
-public interface SysUserOnlineMapper extends BaseMapper<SysUserSessionDO> {
+@Mapper
+public interface SysUserSessionMapper extends BaseMapper<SysUserSessionDO> {
 }

+ 9 - 0
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/auth/SysUserSessionDO.java

@@ -1,17 +1,26 @@
 package cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.auth;
 
 import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO;
+import cn.iocoder.dashboard.framework.security.core.LoginUser;
 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.user.SysUserDO;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Builder;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 
 /**
  * 在线用户表
+ *
+ * 我们已经将 {@link LoginUser} 缓存在 Redis 当中。
+ * 这里额外存储在线用户到 MySQL 中,目的是为了方便管理界面可以灵活查询。
+ * 同时,通过定时轮询 SysUserSessionDO 表,可以主动删除 Redis 的缓存,因为 Redis 的过期删除是延迟的。
+ *
+ * @author 芋道源码
  */
 @TableName(value = "sys_user_session", autoResultMap = true)
 @Data
+@Builder
 @EqualsAndHashCode(callSuper = true)
 public class SysUserSessionDO extends BaseDO {
 

+ 10 - 1
src/main/java/cn/iocoder/dashboard/modules/system/service/auth/SysAuthService.java

@@ -1,6 +1,7 @@
 package cn.iocoder.dashboard.modules.system.service.auth;
 
 import cn.iocoder.dashboard.framework.security.core.service.SecurityAuthFrameworkService;
+import cn.iocoder.dashboard.modules.system.controller.auth.vo.auth.SysAuthLoginReqVO;
 
 /**
  * 认证 Service 接口
@@ -11,6 +12,14 @@ import cn.iocoder.dashboard.framework.security.core.service.SecurityAuthFramewor
  */
 public interface SysAuthService extends SecurityAuthFrameworkService {
 
-    String login(String username, String password, String captchaUUID, String captchaCode);
+    /**
+     * 登陆用户
+     *
+     * @param reqVO 登陆信息
+     * @param userIp 用户 IP
+     * @param userAgent 用户 UA
+     * @return 身份令牌,使用 JWT 方式
+     */
+    String login(SysAuthLoginReqVO reqVO, String userIp, String userAgent);
 
 }

+ 0 - 30
src/main/java/cn/iocoder/dashboard/modules/system/service/auth/SysTokenService.java

@@ -1,30 +0,0 @@
-package cn.iocoder.dashboard.modules.system.service.auth;
-
-import io.jsonwebtoken.Claims;
-
-import java.util.Map;
-
-/**
- * Token Service 接口
- *
- * 提供访问 Token 令牌,目前基于 JWT 实现
- */
-public interface SysTokenService {
-
-    /**
-     * 创建 Token
-     *
-     * @param subject 主体
-     * @return Token 字符串
-     */
-    String createToken(String subject);
-
-    /**
-     * 解析 Token,返回 claims 数据声明
-     *
-     * @param token Token
-     * @return claims
-     */
-    Claims parseToken(String token);
-
-}

+ 0 - 40
src/main/java/cn/iocoder/dashboard/modules/system/service/auth/SysUserOnlineService.java

@@ -1,40 +0,0 @@
-package cn.iocoder.dashboard.modules.system.service.auth;
-
-import cn.iocoder.dashboard.common.pojo.PageResult;
-import cn.iocoder.dashboard.modules.system.controller.auth.vo.session.SysUserSessionPageReqVO;
-import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.auth.SysUserSessionDO;
-
-import java.util.Date;
-
-/**
- * 在线用户 Session Service 接口
- */
-public interface SysUserOnlineService {
-
-    /**
-     * 创建在线用户 Session
-     *
-     * @param sessionId Session 编号
-     * @param userId 用户编号
-     * @param userIp 用户 IP
-     * @param userAgent 用户 UA
-     */
-    void createUserOnline(String sessionId, Long userId, String userIp, String userAgent);
-
-    /**
-     * 更新在线用户 Session 的更新时间
-     *
-     * @param sessionId Session 编号
-     * @param updateTime 更新时间
-     */
-    void updateUserOnlineUpdateTime(String sessionId, Date updateTime);
-
-    /**
-     * 获得在线用户分页列表
-     *
-     * @param reqVO 分页条件
-     * @return 份额与列表
-     */
-    PageResult<SysUserSessionDO> getUserSessionPage(SysUserSessionPageReqVO reqVO);
-
-}

+ 63 - 0
src/main/java/cn/iocoder/dashboard/modules/system/service/auth/SysUserSessionService.java

@@ -0,0 +1,63 @@
+package cn.iocoder.dashboard.modules.system.service.auth;
+
+import cn.iocoder.dashboard.common.pojo.PageResult;
+import cn.iocoder.dashboard.framework.security.core.LoginUser;
+import cn.iocoder.dashboard.modules.system.controller.auth.vo.session.SysUserSessionPageReqVO;
+import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.auth.SysUserSessionDO;
+
+/**
+ * 在线用户 Session Service 接口
+ *
+ * @author 芋道源码
+ */
+public interface SysUserSessionService {
+
+    /**
+     * 创建在线用户 Session
+     *
+     * @param loginUser 登陆用户
+     * @param userIp 用户 IP
+     * @param userAgent 用户 UA
+     * @return Session 编号
+     */
+    String createUserSession(LoginUser loginUser, String userIp, String userAgent);
+
+    /**
+     * 刷新在线用户 Session 的更新时间
+     *
+     * @param sessionId Session 编号
+     * @param loginUser 登陆用户
+     */
+    void refreshUserSession(String sessionId, LoginUser loginUser);
+
+    /**
+     * 删除在线用户 Session
+     *
+     * @param sessionId Session 编号
+     */
+    void deleteUserSession(String sessionId);
+
+    /**
+     * 获得 Session 编号对应的在线用户
+     *
+     * @param sessionId Session 编号
+     * @return 在线用户
+     */
+    LoginUser getLoginUser(String sessionId);
+
+    /**
+     * 获得 Session 超时时间,单位:毫秒
+     *
+     * @return 超时时间
+     */
+    Long getSessionTimeoutMillis();
+
+    /**
+     * 获得在线用户分页列表
+     *
+     * @param reqVO 分页条件
+     * @return 份额与列表
+     */
+    PageResult<SysUserSessionDO> getUserSessionPage(SysUserSessionPageReqVO reqVO);
+
+}

+ 15 - 57
src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysAuthServiceImpl.java

@@ -1,28 +1,22 @@
 package cn.iocoder.dashboard.modules.system.service.auth.impl;
 
-import cn.hutool.core.util.IdUtil;
-import cn.hutool.core.util.StrUtil;
 import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
 import cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil;
-import cn.iocoder.dashboard.framework.security.config.SecurityProperties;
 import cn.iocoder.dashboard.framework.security.core.LoginUser;
 import cn.iocoder.dashboard.framework.tracer.core.util.TracerUtils;
+import cn.iocoder.dashboard.modules.system.controller.auth.vo.auth.SysAuthLoginReqVO;
 import cn.iocoder.dashboard.modules.system.controller.logger.vo.loginlog.SysLoginLogCreateReqVO;
 import cn.iocoder.dashboard.modules.system.convert.auth.SysAuthConvert;
 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.user.SysUserDO;
-import cn.iocoder.dashboard.modules.system.dal.redis.dao.auth.SysLoginUserRedisDAO;
 import cn.iocoder.dashboard.modules.system.enums.logger.SysLoginLogTypeEnum;
 import cn.iocoder.dashboard.modules.system.enums.logger.SysLoginResultEnum;
 import cn.iocoder.dashboard.modules.system.service.auth.SysAuthService;
-import cn.iocoder.dashboard.modules.system.service.auth.SysTokenService;
+import cn.iocoder.dashboard.modules.system.service.auth.SysUserSessionService;
 import cn.iocoder.dashboard.modules.system.service.common.SysCaptchaService;
 import cn.iocoder.dashboard.modules.system.service.logger.SysLoginLogService;
 import cn.iocoder.dashboard.modules.system.service.permission.SysPermissionService;
 import cn.iocoder.dashboard.modules.system.service.user.SysUserService;
-import cn.iocoder.dashboard.util.date.DateUtils;
 import cn.iocoder.dashboard.util.servlet.ServletUtils;
-import io.jsonwebtoken.Claims;
-import io.jsonwebtoken.JwtException;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.authentication.BadCredentialsException;
@@ -37,7 +31,6 @@ import org.springframework.util.Assert;
 
 import javax.annotation.Resource;
 import java.util.Collections;
-import java.util.Date;
 import java.util.Set;
 
 import static cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil.exception;
@@ -52,11 +45,6 @@ import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;
 @Slf4j
 public class SysAuthServiceImpl implements SysAuthService {
 
-    @Resource
-    private SecurityProperties securityProperties;
-
-    @Resource
-    private SysTokenService tokenService;
     @Resource
     private AuthenticationManager authenticationManager;
     @Resource
@@ -67,9 +55,8 @@ public class SysAuthServiceImpl implements SysAuthService {
     private SysCaptchaService captchaService;
     @Resource
     private SysLoginLogService loginLogService;
-
     @Resource
-    private SysLoginUserRedisDAO loginUserRedisDAO;
+    private SysUserSessionService userSessionService;
 
     @Override
     public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
@@ -91,27 +78,21 @@ public class SysAuthServiceImpl implements SysAuthService {
         }
         // 创建 LoginUser 对象
         LoginUser loginUser = SysAuthConvert.INSTANCE.convert(user);
-        loginUser.setUpdateTime(new Date());
-        loginUser.setRoleIds(this.getUserRoleIds(loginUser.getId()));
+        loginUser.setRoleIds(this.getUserRoleIds(loginUser.getId())); // 获取用户角色列表
         return loginUser;
     }
 
     @Override
-    public String login(String username, String password, String captchaUUID, String captchaCode) {
+    public String login(SysAuthLoginReqVO reqVO, String userIp, String userAgent) {
         // 判断验证码是否正确
-        this.verifyCaptcha(username, captchaUUID, captchaCode);
+        this.verifyCaptcha(reqVO.getUsername(), reqVO.getUuid(), reqVO.getCode());
 
         // 使用账号密码,进行登陆。
-        LoginUser loginUser = this.login0(username, password);
-        // 缓存登陆用户到 Redis 中
-        String sessionId = IdUtil.fastSimpleUUID();
-        loginUser.setUpdateTime(new Date());
-        loginUser.setRoleIds(this.getUserRoleIds(loginUser.getId()));
-        loginUserRedisDAO.set(sessionId, loginUser);
+        LoginUser loginUser = this.login0(reqVO.getUsername(), reqVO.getPassword());
+        loginUser.setRoleIds(this.getUserRoleIds(loginUser.getId())); // 获取用户角色列表
 
-        // 创建 Token
-        // 我们在返回给前端的 JWT 中,使用 sessionId 作为 subject 主体,标识当前 User 用户
-        return tokenService.createToken(sessionId);
+        // 缓存登陆用户到 Redis 中,返回 sessionId 编号
+        return userSessionService.createUserSession(loginUser, userIp, userAgent);
     }
 
     private void verifyCaptcha(String username, String captchaUUID, String captchaCode) {
@@ -182,42 +163,20 @@ public class SysAuthServiceImpl implements SysAuthService {
 
     @Override
     public LoginUser verifyTokenAndRefresh(String token) {
-        // 验证 token 的有效性
-        String sessionId = this.verifyToken(token);
         // 获得 LoginUser
-        LoginUser loginUser = loginUserRedisDAO.get(sessionId);
+        LoginUser loginUser = userSessionService.getLoginUser(token);
         if (loginUser == null) {
             return null;
         }
         // 刷新 LoginUser 缓存
-        this.refreshLoginUserCache(sessionId, loginUser);
+        this.refreshLoginUserCache(token, loginUser);
         return loginUser;
     }
 
-    private String verifyToken(String token) {
-        Claims claims;
-        try {
-            claims = tokenService.parseToken(token);
-        } catch (JwtException jwtException) {
-            log.warn("[verifyToken][token({}) 解析发生异常]", token);
-            return null;
-        }
-        // token 已经过期
-        if (DateUtils.isExpired(claims.getExpiration())) {
-            return null;
-        }
-        // 判断 sessionId 是否存在
-        String sessionId = claims.getSubject();
-        if (StrUtil.isBlank(sessionId)) {
-            return null;
-        }
-        return sessionId;
-    }
-
-    private void refreshLoginUserCache(String sessionId, LoginUser loginUser) {
+    private void refreshLoginUserCache(String token, LoginUser loginUser) {
         // 每 1/3 的 Session 超时时间,刷新 LoginUser 缓存
         if (System.currentTimeMillis() - loginUser.getUpdateTime().getTime() <
-                securityProperties.getSessionTimeout().toMillis() / 3) {
+                userSessionService.getSessionTimeoutMillis() / 3) {
             return;
         }
 
@@ -229,9 +188,8 @@ public class SysAuthServiceImpl implements SysAuthService {
 
         // 刷新 LoginUser 缓存
         loginUser.setDeptId(user.getDeptId());
-        loginUser.setUpdateTime(new Date());
         loginUser.setRoleIds(this.getUserRoleIds(loginUser.getId()));
-        loginUserRedisDAO.set(sessionId, loginUser);
+        userSessionService.refreshUserSession(token, loginUser);
     }
 
 }

+ 0 - 58
src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysTokenServiceImpl.java

@@ -1,58 +0,0 @@
-package cn.iocoder.dashboard.modules.system.service.auth.impl;
-
-import cn.iocoder.dashboard.framework.security.config.SecurityProperties;
-import cn.iocoder.dashboard.modules.system.service.auth.SysTokenService;
-import cn.iocoder.dashboard.util.date.DateUtils;
-import io.jsonwebtoken.Claims;
-import io.jsonwebtoken.Jwts;
-import io.jsonwebtoken.SignatureAlgorithm;
-import org.springframework.stereotype.Service;
-
-import javax.annotation.Resource;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Token Service 实现类
- *
- * @author 芋道源码
- */
-@Service
-public class SysTokenServiceImpl implements SysTokenService {
-
-    @Resource
-    private SecurityProperties securityProperties;
-
-    @Override
-    public String createToken(String subject) {
-        return Jwts.builder()
-                .signWith(SignatureAlgorithm.HS512, securityProperties.getTokenSecret())
-                .setExpiration(DateUtils.addTime(securityProperties.getTokenTimeout()))
-                .setSubject(subject)
-                .compact();
-    }
-
-    @Override
-    public Claims parseToken(String token) {
-        return Jwts.parser()
-                .setSigningKey(securityProperties.getTokenSecret())
-                .parseClaimsJws(token)
-                .getBody();
-    }
-
-    public static void main(String[] args) {
-        String secret = "abcdefghijklmnopqrstuvwxyz";
-        Map<String, Object> map = new HashMap<>();
-        map.put("key1", "value1");
-        System.out.println(Jwts.builder()
-                .signWith(SignatureAlgorithm.HS512, secret)
-                .setClaims(map)
-                .compact());
-
-        System.out.println(Jwts.parser()
-                .setSigningKey(secret)
-                .parseClaimsJws("qyJhbGciOiJIUzUxMiJ9.eyJrZXkxIjoidmFsdWUxIn0.AHWncLRBlJkqrKaoWHZmMgbqYIT7rfLs8KCp9LuC0mdNfnx1xEMm1N9bgcD-0lc5sjySqsKiWzqJ3rpoyUSh0g")
-                .getBody());
-    }
-
-}

+ 89 - 0
src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysUserSessionServiceImpl.java

@@ -0,0 +1,89 @@
+package cn.iocoder.dashboard.modules.system.service.auth.impl;
+
+import cn.hutool.core.util.IdUtil;
+import cn.iocoder.dashboard.common.pojo.PageResult;
+import cn.iocoder.dashboard.framework.security.config.SecurityProperties;
+import cn.iocoder.dashboard.framework.security.core.LoginUser;
+import cn.iocoder.dashboard.modules.system.controller.auth.vo.session.SysUserSessionPageReqVO;
+import cn.iocoder.dashboard.modules.system.dal.mysql.dao.auth.SysUserSessionMapper;
+import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.auth.SysUserSessionDO;
+import cn.iocoder.dashboard.modules.system.dal.redis.dao.auth.SysLoginUserRedisDAO;
+import cn.iocoder.dashboard.modules.system.service.auth.SysUserSessionService;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.Date;
+
+/**
+ * 在线用户 Session Service 实现类
+ *
+ * @author 芋道源码
+ */
+@Service
+public class SysUserSessionServiceImpl implements SysUserSessionService {
+
+    @Resource
+    private SecurityProperties securityProperties;
+
+    @Resource
+    private SysLoginUserRedisDAO loginUserRedisDAO;
+
+    @Resource
+    private SysUserSessionMapper userSessionMapper;
+
+    @Override
+    public String createUserSession(LoginUser loginUser, String userIp, String userAgent) {
+        // 生成 Session 编号
+        String sessionId = generateSessionId();
+        // 写入 Redis 缓存
+        loginUser.setUpdateTime(new Date());
+        loginUserRedisDAO.set(sessionId, loginUser);
+        // 写入 DB 中
+        SysUserSessionDO userSession = SysUserSessionDO.builder().userId(loginUser.getId())
+                .userIp(userIp).userAgent(userAgent).build();
+        userSessionMapper.insert(userSession);
+        // 返回 Session 编号
+        return sessionId;
+    }
+
+    @Override
+    public void refreshUserSession(String sessionId, LoginUser loginUser) {
+        // 写入 Redis 缓存
+        loginUser.setUpdateTime(new Date());
+        loginUserRedisDAO.set(sessionId, loginUser);
+        // 更新 DB 中
+        SysUserSessionDO updateObj = SysUserSessionDO.builder().id(sessionId).build();
+        updateObj.setUpdateTime(new Date());
+        userSessionMapper.updateById(updateObj);
+    }
+
+    @Override
+    public void deleteUserSession(String sessionId) {
+
+    }
+
+    @Override
+    public LoginUser getLoginUser(String sessionId) {
+        return loginUserRedisDAO.get(sessionId);
+    }
+
+    @Override
+    public Long getSessionTimeoutMillis() {
+        return securityProperties.getSessionTimeout().toMillis();
+    }
+
+    @Override
+    public PageResult<SysUserSessionDO> getUserSessionPage(SysUserSessionPageReqVO reqVO) {
+        return null;
+    }
+
+    /**
+     * 生成 Session 编号,目前采用 UUID 算法
+     *
+     * @return Session 编号
+     */
+    private static String generateSessionId() {
+        return IdUtil.fastSimpleUUID();
+    }
+
+}

+ 1 - 0
src/main/resources/application.yaml

@@ -41,6 +41,7 @@ spring:
 
 # 芋道配置项,设置当前项目所有自定义的配置
 yudao:
+  version: 1.0.0
   web:
     api-prefix: /api
     controller-package: cn.iocoder.dashboard

+ 25 - 24
ruoyi-admin/src/main/resources/banner.txt → src/main/resources/banner.txt

@@ -1,24 +1,25 @@
-Application Version: ${ruoyi.version}
-Spring Boot Version: ${spring-boot.version}
-////////////////////////////////////////////////////////////////////
-//                          _ooOoo_                               //
-//                         o8888888o                              //
-//                         88" . "88                              //
-//                         (| ^_^ |)                              //
-//                         O\  =  /O                              //
-//                      ____/`---'\____                           //
-//                    .'  \\|     |//  `.                         //
-//                   /  \\|||  :  |||//  \                        //
-//                  /  _||||| -:- |||||-  \                       //
-//                  |   | \\\  -  /// |   |                       //
-//                  | \_|  ''\---/''  |   |                       //
-//                  \  .-\__  `-`  ___/-. /                       //
-//                ___`. .'  /--.--\  `. . ___                     //
-//              ."" '<  `.___\_<|>_/___.'  >'"".                  //
-//            | | :  `- \`.;`\ _ /`;.`/ - ` : | |                 //
-//            \  \ `-.   \_ __\ /__ _/   .-` /  /                 //
-//      ========`-.____`-.___\_____/___.-`____.-'========         //
-//                           `=---='                              //
-//      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^        //
-//             佛祖保佑       永不宕机      永无BUG               //
-////////////////////////////////////////////////////////////////////
+芋道源码 http://www.iocoder.cn
+Application Version: ${yudao.version}
+Spring Boot Version: ${spring-boot.version}
+////////////////////////////////////////////////////////////////////
+//                          _ooOoo_                               //
+//                         o8888888o                              //
+//                         88" . "88                              //
+//                         (| ^_^ |)                              //
+//                         O\  =  /O                              //
+//                      ____/`---'\____                           //
+//                    .'  \\|     |//  `.                         //
+//                   /  \\|||  :  |||//  \                        //
+//                  /  _||||| -:- |||||-  \                       //
+//                  |   | \\\  -  /// |   |                       //
+//                  | \_|  ''\---/''  |   |                       //
+//                  \  .-\__  `-`  ___/-. /                       //
+//                ___`. .'  /--.--\  `. . ___                     //
+//              ."" '<  `.___\_<|>_/___.'  >'"".                  //
+//            | | :  `- \`.;`\ _ /`;.`/ - ` : | |                 //
+//            \  \ `-.   \_ __\ /__ _/   .-` /  /                 //
+//      ========`-.____`-.___\_____/___.-`____.-'========         //
+//                           `=---='                              //
+//      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^        //
+//             佛祖保佑       永不宕机      永无BUG               //
+////////////////////////////////////////////////////////////////////