diff --git a/pom.xml b/pom.xml
index 39f97ab43..0f26eb5fc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -53,7 +53,6 @@
1.16.14
1.4.1.Final
- 0.9.1
5.5.6
2.2.7
@@ -177,12 +176,6 @@
${lombok.version}
-
- io.jsonwebtoken
- jjwt
- ${jjwt.version}
-
-
org.projectlombok
lombok
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java b/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java
deleted file mode 100644
index e3c56ee54..000000000
--- a/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java
+++ /dev/null
@@ -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" +
- " ''-' `'-' `-..-' ");
- }
-}
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java
deleted file mode 100644
index 4ca306d70..000000000
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java
+++ /dev/null
@@ -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 keys = redisCache.keys(Constants.LOGIN_TOKEN_KEY + "*");
- List userOnlineList = new ArrayList();
- 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();
- }
-}
diff --git a/ruoyi-admin/src/main/resources/META-INF/spring-devtools.properties b/ruoyi-admin/src/main/resources/META-INF/spring-devtools.properties
deleted file mode 100644
index 2b23f85a3..000000000
--- a/ruoyi-admin/src/main/resources/META-INF/spring-devtools.properties
+++ /dev/null
@@ -1 +0,0 @@
-restart.include.json=/com.alibaba.fastjson.*.jar
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml
index afb292bcf..e2c99b186 100644
--- a/ruoyi-admin/src/main/resources/application.yml
+++ b/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:
# 过滤开关
diff --git a/src/main/java/cn/iocoder/dashboard/framework/mybatis/core/dataobject/BaseDO.java b/src/main/java/cn/iocoder/dashboard/framework/mybatis/core/dataobject/BaseDO.java
index c0fcda207..c65eaf6dd 100644
--- a/src/main/java/cn/iocoder/dashboard/framework/mybatis/core/dataobject/BaseDO.java
+++ b/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;
-
}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/SysAuthController.java b/src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/SysAuthController.java
index 71810f447..026fa2aed 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/SysAuthController.java
+++ b/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 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());
}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/SysUserSessionController.java b/src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/SysUserSessionController.java
new file mode 100644
index 000000000..f366e0d29
--- /dev/null
+++ b/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> getUserSessionPage(@Validated SysUserSessionPageReqVO reqVO) {
+ // 获得 Session 分页
+ PageResult 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 delete(@RequestParam("id") String id) {
+ userSessionService.deleteUserSession(id);
+ return success(true);
+ }
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/vo/session/SysUserSessionPageItemRespVO.java b/src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/vo/session/SysUserSessionPageItemRespVO.java
new file mode 100644
index 000000000..adf1f9459
--- /dev/null
+++ b/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;
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/vo/session/SysUserSessionPageReqVO.java b/src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/vo/session/SysUserSessionPageReqVO.java
index e91198877..2b27b867f 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/vo/session/SysUserSessionPageReqVO.java
+++ b/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;
+
}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/auth/SysUserOnlineMapper.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/auth/SysUserSessionMapper.java
similarity index 61%
rename from src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/auth/SysUserOnlineMapper.java
rename to src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/auth/SysUserSessionMapper.java
index fdc317a39..51aed9dbb 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/auth/SysUserOnlineMapper.java
+++ b/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 {
+@Mapper
+public interface SysUserSessionMapper extends BaseMapper {
}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/auth/SysUserSessionDO.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/auth/SysUserSessionDO.java
index 3964ef9c6..1efda9b85 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/auth/SysUserSessionDO.java
+++ b/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 {
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/SysAuthService.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/SysAuthService.java
index d1c0580e4..04eb6d968 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/SysAuthService.java
+++ b/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);
}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/SysTokenService.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/SysTokenService.java
deleted file mode 100644
index c1f8c8d91..000000000
--- a/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/SysTokenService.java
+++ /dev/null
@@ -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);
-
-}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/SysUserOnlineService.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/SysUserOnlineService.java
deleted file mode 100644
index 62198809a..000000000
--- a/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/SysUserOnlineService.java
+++ /dev/null
@@ -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 getUserSessionPage(SysUserSessionPageReqVO reqVO);
-
-}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/SysUserSessionService.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/SysUserSessionService.java
new file mode 100644
index 000000000..f22152a3a
--- /dev/null
+++ b/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 getUserSessionPage(SysUserSessionPageReqVO reqVO);
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysAuthServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysAuthServiceImpl.java
index ce44b0a1e..e86af8c87 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysAuthServiceImpl.java
+++ b/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);
}
}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysTokenServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysTokenServiceImpl.java
deleted file mode 100644
index 3b3112f60..000000000
--- a/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysTokenServiceImpl.java
+++ /dev/null
@@ -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 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());
- }
-
-}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysUserSessionServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysUserSessionServiceImpl.java
new file mode 100644
index 000000000..6dda624c4
--- /dev/null
+++ b/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 getUserSessionPage(SysUserSessionPageReqVO reqVO) {
+ return null;
+ }
+
+ /**
+ * 生成 Session 编号,目前采用 UUID 算法
+ *
+ * @return Session 编号
+ */
+ private static String generateSessionId() {
+ return IdUtil.fastSimpleUUID();
+ }
+
+}
diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml
index ff060b164..e313d6e47 100644
--- a/src/main/resources/application.yaml
+++ b/src/main/resources/application.yaml
@@ -41,6 +41,7 @@ spring:
# 芋道配置项,设置当前项目所有自定义的配置
yudao:
+ version: 1.0.0
web:
api-prefix: /api
controller-package: cn.iocoder.dashboard
diff --git a/ruoyi-admin/src/main/resources/banner.txt b/src/main/resources/banner.txt
similarity index 95%
rename from ruoyi-admin/src/main/resources/banner.txt
rename to src/main/resources/banner.txt
index 94662592f..d4e3b433b 100644
--- a/ruoyi-admin/src/main/resources/banner.txt
+++ b/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 //
-////////////////////////////////////////////////////////////////////
\ No newline at end of file
+芋道源码 http://www.iocoder.cn
+Application Version: ${yudao.version}
+Spring Boot Version: ${spring-boot.version}
+////////////////////////////////////////////////////////////////////
+// _ooOoo_ //
+// o8888888o //
+// 88" . "88 //
+// (| ^_^ |) //
+// O\ = /O //
+// ____/`---'\____ //
+// .' \\| |// `. //
+// / \\||| : |||// \ //
+// / _||||| -:- |||||- \ //
+// | | \\\ - /// | | //
+// | \_| ''\---/'' | | //
+// \ .-\__ `-` ___/-. / //
+// ___`. .' /--.--\ `. . ___ //
+// ."" '< `.___\_<|>_/___.' >'"". //
+// | | : `- \`.;`\ _ /`;.`/ - ` : | | //
+// \ \ `-. \_ __\ /__ _/ .-` / / //
+// ========`-.____`-.___\_____/___.-`____.-'======== //
+// `=---=' //
+// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //
+// 佛祖保佑 永不宕机 永无BUG //
+////////////////////////////////////////////////////////////////////