mirror of
				https://github.com/YunaiV/ruoyi-vue-pro.git
				synced 2025-10-31 18:49:06 +08:00 
			
		
		
		
	将 LoginUser 重构到 UserSessionService 模块汇总
This commit is contained in:
		
							
								
								
									
										7
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										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> | ||||
|  | ||||
| @ -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" + | ||||
|                 " ''-'   `'-'    `-..-'              "); | ||||
|     } | ||||
| } | ||||
| @ -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(); | ||||
|     } | ||||
| } | ||||
| @ -1 +0,0 @@ | ||||
| restart.include.json=/com.alibaba.fastjson.*.jar | ||||
| @ -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: | ||||
|   # 过滤开关 | ||||
|  | ||||
| @ -34,7 +34,4 @@ public class BaseDO implements Serializable { | ||||
|     @TableLogic | ||||
|     private Integer deleted; | ||||
|  | ||||
| //    /** 备注 */ TODO 思考下,怎么解决 | ||||
| //    private String remark; | ||||
|  | ||||
| } | ||||
|  | ||||
| @ -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()); | ||||
|     } | ||||
|  | ||||
| @ -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); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -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; | ||||
|  | ||||
| } | ||||
| @ -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; | ||||
|  | ||||
| } | ||||
|  | ||||
| @ -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> { | ||||
| } | ||||
| @ -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 { | ||||
|  | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| } | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| } | ||||
| @ -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); | ||||
|  | ||||
| } | ||||
| @ -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); | ||||
|  | ||||
| } | ||||
| @ -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); | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| @ -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()); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -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(); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -41,6 +41,7 @@ spring: | ||||
|  | ||||
| # 芋道配置项,设置当前项目所有自定义的配置 | ||||
| yudao: | ||||
|   version: 1.0.0 | ||||
|   web: | ||||
|     api-prefix: /api | ||||
|     controller-package: cn.iocoder.dashboard | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| Application Version: ${ruoyi.version} | ||||
| 芋道源码 http://www.iocoder.cn | ||||
| Application Version: ${yudao.version} | ||||
| Spring Boot Version: ${spring-boot.version} | ||||
| //////////////////////////////////////////////////////////////////// | ||||
| //                          _ooOoo_                               // | ||||
		Reference in New Issue
	
	Block a user
	 YunaiV
					YunaiV