Commit 8606f5c6053b0947213f4e4ed336b4cbc17b2502
1 parent
0f21b55e
优化 UserSession 的实现,将 id 变成自增,额外增加 token 字段
Showing
24 changed files
with
322 additions
and
235 deletions
sql/dm/README.md
0 → 100644
| 1 | +暂未适配国产 DM 数据库,如果你有需要,可以微信联系 wangwenbin-server 一起建设。 |
yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml
| @@ -46,6 +46,10 @@ | @@ -46,6 +46,10 @@ | ||
| 46 | <groupId>org.postgresql</groupId> | 46 | <groupId>org.postgresql</groupId> |
| 47 | <artifactId>postgresql</artifactId> | 47 | <artifactId>postgresql</artifactId> |
| 48 | </dependency> | 48 | </dependency> |
| 49 | + <dependency> | ||
| 50 | + <groupId>com.microsoft.sqlserver</groupId> | ||
| 51 | + <artifactId>mssql-jdbc</artifactId> | ||
| 52 | + </dependency> | ||
| 49 | 53 | ||
| 50 | <dependency> | 54 | <dependency> |
| 51 | <groupId>com.alibaba</groupId> | 55 | <groupId>com.alibaba</groupId> |
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoSecurityAutoConfiguration.java
| @@ -3,7 +3,7 @@ package cn.iocoder.yudao.framework.security.config; | @@ -3,7 +3,7 @@ package cn.iocoder.yudao.framework.security.config; | ||
| 3 | import cn.iocoder.yudao.framework.security.core.aop.PreAuthenticatedAspect; | 3 | import cn.iocoder.yudao.framework.security.core.aop.PreAuthenticatedAspect; |
| 4 | import cn.iocoder.yudao.framework.security.core.authentication.MultiUserDetailsAuthenticationProvider; | 4 | import cn.iocoder.yudao.framework.security.core.authentication.MultiUserDetailsAuthenticationProvider; |
| 5 | import cn.iocoder.yudao.framework.security.core.context.TransmittableThreadLocalSecurityContextHolderStrategy; | 5 | import cn.iocoder.yudao.framework.security.core.context.TransmittableThreadLocalSecurityContextHolderStrategy; |
| 6 | -import cn.iocoder.yudao.framework.security.core.filter.JWTAuthenticationTokenFilter; | 6 | +import cn.iocoder.yudao.framework.security.core.filter.TokenAuthenticationFilter; |
| 7 | import cn.iocoder.yudao.framework.security.core.handler.AccessDeniedHandlerImpl; | 7 | import cn.iocoder.yudao.framework.security.core.handler.AccessDeniedHandlerImpl; |
| 8 | import cn.iocoder.yudao.framework.security.core.handler.AuthenticationEntryPointImpl; | 8 | import cn.iocoder.yudao.framework.security.core.handler.AuthenticationEntryPointImpl; |
| 9 | import cn.iocoder.yudao.framework.security.core.handler.LogoutSuccessHandlerImpl; | 9 | import cn.iocoder.yudao.framework.security.core.handler.LogoutSuccessHandlerImpl; |
| @@ -86,9 +86,9 @@ public class YudaoSecurityAutoConfiguration { | @@ -86,9 +86,9 @@ public class YudaoSecurityAutoConfiguration { | ||
| 86 | * Token 认证过滤器 Bean | 86 | * Token 认证过滤器 Bean |
| 87 | */ | 87 | */ |
| 88 | @Bean | 88 | @Bean |
| 89 | - public JWTAuthenticationTokenFilter authenticationTokenFilter(MultiUserDetailsAuthenticationProvider authenticationProvider, | ||
| 90 | - GlobalExceptionHandler globalExceptionHandler) { | ||
| 91 | - return new JWTAuthenticationTokenFilter(securityProperties, authenticationProvider, globalExceptionHandler); | 89 | + public TokenAuthenticationFilter authenticationTokenFilter(MultiUserDetailsAuthenticationProvider authenticationProvider, |
| 90 | + GlobalExceptionHandler globalExceptionHandler) { | ||
| 91 | + return new TokenAuthenticationFilter(securityProperties, authenticationProvider, globalExceptionHandler); | ||
| 92 | } | 92 | } |
| 93 | 93 | ||
| 94 | /** | 94 | /** |
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java
| @@ -2,7 +2,7 @@ package cn.iocoder.yudao.framework.security.config; | @@ -2,7 +2,7 @@ package cn.iocoder.yudao.framework.security.config; | ||
| 2 | 2 | ||
| 3 | import cn.hutool.core.util.StrUtil; | 3 | import cn.hutool.core.util.StrUtil; |
| 4 | import cn.iocoder.yudao.framework.security.core.authentication.MultiUserDetailsAuthenticationProvider; | 4 | import cn.iocoder.yudao.framework.security.core.authentication.MultiUserDetailsAuthenticationProvider; |
| 5 | -import cn.iocoder.yudao.framework.security.core.filter.JWTAuthenticationTokenFilter; | 5 | +import cn.iocoder.yudao.framework.security.core.filter.TokenAuthenticationFilter; |
| 6 | import cn.iocoder.yudao.framework.web.config.WebProperties; | 6 | import cn.iocoder.yudao.framework.web.config.WebProperties; |
| 7 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; | 7 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; |
| 8 | import org.springframework.context.annotation.Bean; | 8 | import org.springframework.context.annotation.Bean; |
| @@ -55,7 +55,7 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap | @@ -55,7 +55,7 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap | ||
| 55 | * Token 认证过滤器 Bean | 55 | * Token 认证过滤器 Bean |
| 56 | */ | 56 | */ |
| 57 | @Resource | 57 | @Resource |
| 58 | - private JWTAuthenticationTokenFilter authenticationTokenFilter; | 58 | + private TokenAuthenticationFilter authenticationTokenFilter; |
| 59 | 59 | ||
| 60 | /** | 60 | /** |
| 61 | * 自定义的权限映射 Bean 们 | 61 | * 自定义的权限映射 Bean 们 |
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/filter/JWTAuthenticationTokenFilter.java renamed to yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/filter/TokenAuthenticationFilter.java
| @@ -18,13 +18,13 @@ import javax.servlet.http.HttpServletResponse; | @@ -18,13 +18,13 @@ import javax.servlet.http.HttpServletResponse; | ||
| 18 | import java.io.IOException; | 18 | import java.io.IOException; |
| 19 | 19 | ||
| 20 | /** | 20 | /** |
| 21 | - * JWT 过滤器,验证 token 的有效性 | 21 | + * Token 过滤器,验证 token 的有效性 |
| 22 | * 验证通过后,获得 {@link LoginUser} 信息,并加入到 Spring Security 上下文 | 22 | * 验证通过后,获得 {@link LoginUser} 信息,并加入到 Spring Security 上下文 |
| 23 | * | 23 | * |
| 24 | * @author 芋道源码 | 24 | * @author 芋道源码 |
| 25 | */ | 25 | */ |
| 26 | @RequiredArgsConstructor | 26 | @RequiredArgsConstructor |
| 27 | -public class JWTAuthenticationTokenFilter extends OncePerRequestFilter { | 27 | +public class TokenAuthenticationFilter extends OncePerRequestFilter { |
| 28 | 28 | ||
| 29 | private final SecurityProperties securityProperties; | 29 | private final SecurityProperties securityProperties; |
| 30 | 30 | ||
| @@ -43,7 +43,7 @@ public class JWTAuthenticationTokenFilter extends OncePerRequestFilter { | @@ -43,7 +43,7 @@ public class JWTAuthenticationTokenFilter extends OncePerRequestFilter { | ||
| 43 | LoginUser loginUser = authenticationProvider.verifyTokenAndRefresh(request, token); | 43 | LoginUser loginUser = authenticationProvider.verifyTokenAndRefresh(request, token); |
| 44 | // 模拟 Login 功能,方便日常开发调试 | 44 | // 模拟 Login 功能,方便日常开发调试 |
| 45 | if (loginUser == null) { | 45 | if (loginUser == null) { |
| 46 | - loginUser = this.mockLoginUser(request, token); | 46 | + loginUser = mockLoginUser(request, token); |
| 47 | } | 47 | } |
| 48 | // 设置当前用户 | 48 | // 设置当前用户 |
| 49 | if (loginUser != null) { | 49 | if (loginUser != null) { |
yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/DefaultDatabaseQueryTest.java
| @@ -27,7 +27,8 @@ public class DefaultDatabaseQueryTest { | @@ -27,7 +27,8 @@ public class DefaultDatabaseQueryTest { | ||
| 27 | if (StrUtil.startWithAny(tableInfo.getName().toLowerCase(), "act_", "flw_", "qrtz_")) { | 27 | if (StrUtil.startWithAny(tableInfo.getName().toLowerCase(), "act_", "flw_", "qrtz_")) { |
| 28 | continue; | 28 | continue; |
| 29 | } | 29 | } |
| 30 | - System.out.println(String.format("CREATE SEQUENCE %s_seq MINVALUE 0;", tableInfo.getName())); | 30 | +// System.out.println(String.format("CREATE SEQUENCE %s_seq MINVALUE 0;", tableInfo.getName())); |
| 31 | + System.out.println(String.format("DELETE FROM %s WHERE deleted = '1';", tableInfo.getName())); | ||
| 31 | } | 32 | } |
| 32 | System.out.println(tableInfos.size()); | 33 | System.out.println(tableInfos.size()); |
| 33 | System.out.println(System.currentTimeMillis() - time); | 34 | System.out.println(System.currentTimeMillis() - time); |
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthServiceImpl.java
| @@ -8,8 +8,6 @@ import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; | @@ -8,8 +8,6 @@ import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; | ||
| 8 | import cn.iocoder.yudao.framework.security.core.LoginUser; | 8 | import cn.iocoder.yudao.framework.security.core.LoginUser; |
| 9 | import cn.iocoder.yudao.framework.security.core.authentication.MultiUsernamePasswordAuthenticationToken; | 9 | import cn.iocoder.yudao.framework.security.core.authentication.MultiUsernamePasswordAuthenticationToken; |
| 10 | import cn.iocoder.yudao.module.member.controller.app.auth.vo.*; | 10 | import cn.iocoder.yudao.module.member.controller.app.auth.vo.*; |
| 11 | -import cn.iocoder.yudao.module.member.controller.app.social.vo.AppSocialUserBindReqVO; | ||
| 12 | -import cn.iocoder.yudao.module.member.controller.app.social.vo.AppSocialUserUnbindReqVO; | ||
| 13 | import cn.iocoder.yudao.module.member.convert.auth.AuthConvert; | 11 | import cn.iocoder.yudao.module.member.convert.auth.AuthConvert; |
| 14 | import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; | 12 | import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; |
| 15 | import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper; | 13 | import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper; |
| @@ -88,7 +86,7 @@ public class MemberAuthServiceImpl implements MemberAuthService { | @@ -88,7 +86,7 @@ public class MemberAuthServiceImpl implements MemberAuthService { | ||
| 88 | // 使用手机 + 密码,进行登录。 | 86 | // 使用手机 + 密码,进行登录。 |
| 89 | LoginUser loginUser = this.login0(reqVO.getMobile(), reqVO.getPassword()); | 87 | LoginUser loginUser = this.login0(reqVO.getMobile(), reqVO.getPassword()); |
| 90 | 88 | ||
| 91 | - // 缓存登录用户到 Redis 中,返回 sessionId 编号 | 89 | + // 缓存登录用户到 Redis 中,返回 Token 令牌 |
| 92 | return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_USERNAME, userIp, userAgent); | 90 | return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_USERNAME, userIp, userAgent); |
| 93 | } | 91 | } |
| 94 | 92 | ||
| @@ -105,7 +103,7 @@ public class MemberAuthServiceImpl implements MemberAuthService { | @@ -105,7 +103,7 @@ public class MemberAuthServiceImpl implements MemberAuthService { | ||
| 105 | // 执行登陆 | 103 | // 执行登陆 |
| 106 | LoginUser loginUser = AuthConvert.INSTANCE.convert(user); | 104 | LoginUser loginUser = AuthConvert.INSTANCE.convert(user); |
| 107 | 105 | ||
| 108 | - // 缓存登录用户到 Redis 中,返回 sessionId 编号 | 106 | + // 缓存登录用户到 Redis 中,返回 Token 令牌 |
| 109 | return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_SMS, userIp, userAgent); | 107 | return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_SMS, userIp, userAgent); |
| 110 | } | 108 | } |
| 111 | 109 | ||
| @@ -127,7 +125,7 @@ public class MemberAuthServiceImpl implements MemberAuthService { | @@ -127,7 +125,7 @@ public class MemberAuthServiceImpl implements MemberAuthService { | ||
| 127 | // 创建 LoginUser 对象 | 125 | // 创建 LoginUser 对象 |
| 128 | LoginUser loginUser = AuthConvert.INSTANCE.convert(user); | 126 | LoginUser loginUser = AuthConvert.INSTANCE.convert(user); |
| 129 | 127 | ||
| 130 | - // 缓存登录用户到 Redis 中,返回 sessionId 编号 | 128 | + // 缓存登录用户到 Redis 中,返回 Token 令牌 |
| 131 | return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_SOCIAL, userIp, userAgent); | 129 | return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_SOCIAL, userIp, userAgent); |
| 132 | } | 130 | } |
| 133 | 131 | ||
| @@ -136,18 +134,18 @@ public class MemberAuthServiceImpl implements MemberAuthService { | @@ -136,18 +134,18 @@ public class MemberAuthServiceImpl implements MemberAuthService { | ||
| 136 | // 使用手机号、手机验证码登录 | 134 | // 使用手机号、手机验证码登录 |
| 137 | AppAuthSmsLoginReqVO loginReqVO = AppAuthSmsLoginReqVO.builder() | 135 | AppAuthSmsLoginReqVO loginReqVO = AppAuthSmsLoginReqVO.builder() |
| 138 | .mobile(reqVO.getMobile()).code(reqVO.getSmsCode()).build(); | 136 | .mobile(reqVO.getMobile()).code(reqVO.getSmsCode()).build(); |
| 139 | - String sessionId = this.smsLogin(loginReqVO, userIp, userAgent); | ||
| 140 | - LoginUser loginUser = userSessionApi.getLoginUser(sessionId); | 137 | + String token = this.smsLogin(loginReqVO, userIp, userAgent); |
| 138 | + LoginUser loginUser = userSessionApi.getLoginUser(token); | ||
| 141 | 139 | ||
| 142 | // 绑定社交用户 | 140 | // 绑定社交用户 |
| 143 | socialUserApi.bindSocialUser(AuthConvert.INSTANCE.convert(loginUser.getId(), getUserType().getValue(), reqVO)); | 141 | socialUserApi.bindSocialUser(AuthConvert.INSTANCE.convert(loginUser.getId(), getUserType().getValue(), reqVO)); |
| 144 | - return sessionId; | 142 | + return token; |
| 145 | } | 143 | } |
| 146 | 144 | ||
| 147 | private String createUserSessionAfterLoginSuccess(LoginUser loginUser, LoginLogTypeEnum logType, String userIp, String userAgent) { | 145 | private String createUserSessionAfterLoginSuccess(LoginUser loginUser, LoginLogTypeEnum logType, String userIp, String userAgent) { |
| 148 | // 插入登陆日志 | 146 | // 插入登陆日志 |
| 149 | createLoginLog(loginUser.getUsername(), logType, LoginResultEnum.SUCCESS); | 147 | createLoginLog(loginUser.getUsername(), logType, LoginResultEnum.SUCCESS); |
| 150 | - // 缓存登录用户到 Redis 中,返回 sessionId 编号 | 148 | + // 缓存登录用户到 Redis 中,返回 Token 令牌 |
| 151 | return userSessionApi.createUserSession(loginUser, userIp, userAgent); | 149 | return userSessionApi.createUserSession(loginUser, userIp, userAgent); |
| 152 | } | 150 | } |
| 153 | 151 |
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/UserSessionApi.java
| @@ -18,33 +18,33 @@ public interface UserSessionApi { | @@ -18,33 +18,33 @@ public interface UserSessionApi { | ||
| 18 | * @param loginUser 登录用户 | 18 | * @param loginUser 登录用户 |
| 19 | * @param userIp 用户 IP | 19 | * @param userIp 用户 IP |
| 20 | * @param userAgent 用户 UA | 20 | * @param userAgent 用户 UA |
| 21 | - * @return Session 编号 | 21 | + * @return Token 令牌 |
| 22 | */ | 22 | */ |
| 23 | String createUserSession(@NotNull(message = "登录用户不能为空") LoginUser loginUser, String userIp, String userAgent); | 23 | String createUserSession(@NotNull(message = "登录用户不能为空") LoginUser loginUser, String userIp, String userAgent); |
| 24 | 24 | ||
| 25 | /** | 25 | /** |
| 26 | * 刷新在线用户 Session 的更新时间 | 26 | * 刷新在线用户 Session 的更新时间 |
| 27 | * | 27 | * |
| 28 | - * @param sessionId Session 编号 | 28 | + * @param token Token 令牌 |
| 29 | * @param loginUser 登录用户 | 29 | * @param loginUser 登录用户 |
| 30 | */ | 30 | */ |
| 31 | - void refreshUserSession(@NotEmpty(message = "Session编号不能为空") String sessionId, | 31 | + void refreshUserSession(@NotEmpty(message = "Token 令牌不能为空") String token, |
| 32 | @NotNull(message = "登录用户不能为空") LoginUser loginUser); | 32 | @NotNull(message = "登录用户不能为空") LoginUser loginUser); |
| 33 | 33 | ||
| 34 | /** | 34 | /** |
| 35 | * 删除在线用户 Session | 35 | * 删除在线用户 Session |
| 36 | * | 36 | * |
| 37 | - * @param sessionId Session 编号 | 37 | + * @param token Token 令牌 |
| 38 | */ | 38 | */ |
| 39 | - void deleteUserSession(String sessionId); | 39 | + void deleteUserSession(String token); |
| 40 | 40 | ||
| 41 | /** | 41 | /** |
| 42 | - * 获得 Session 编号对应的在线用户 | 42 | + * 获得 Token 令牌对应的在线用户 |
| 43 | * | 43 | * |
| 44 | - * @param sessionId Session 编号 | 44 | + * @param token Token 令牌 |
| 45 | * @return 在线用户 | 45 | * @return 在线用户 |
| 46 | */ | 46 | */ |
| 47 | - LoginUser getLoginUser(String sessionId); | 47 | + LoginUser getLoginUser(String token); |
| 48 | 48 | ||
| 49 | /** | 49 | /** |
| 50 | * 获得 Session 超时时间,单位:毫秒 | 50 | * 获得 Session 超时时间,单位:毫秒 |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/auth/UserSessionApiImpl.java
| @@ -25,18 +25,18 @@ public class UserSessionApiImpl implements UserSessionApi { | @@ -25,18 +25,18 @@ public class UserSessionApiImpl implements UserSessionApi { | ||
| 25 | } | 25 | } |
| 26 | 26 | ||
| 27 | @Override | 27 | @Override |
| 28 | - public void refreshUserSession(String sessionId, LoginUser loginUser) { | ||
| 29 | - userSessionService.refreshUserSession(sessionId, loginUser); | 28 | + public void refreshUserSession(String token, LoginUser loginUser) { |
| 29 | + userSessionService.refreshUserSession(token, loginUser); | ||
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | @Override | 32 | @Override |
| 33 | - public void deleteUserSession(String sessionId) { | ||
| 34 | - userSessionService.deleteUserSession(sessionId); | 33 | + public void deleteUserSession(String token) { |
| 34 | + userSessionService.deleteUserSession(token); | ||
| 35 | } | 35 | } |
| 36 | 36 | ||
| 37 | @Override | 37 | @Override |
| 38 | - public LoginUser getLoginUser(String sessionId) { | ||
| 39 | - return userSessionService.getLoginUser(sessionId); | 38 | + public LoginUser getLoginUser(String token) { |
| 39 | + return userSessionService.getLoginUser(token); | ||
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | @Override | 42 | @Override |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/UserSessionController.java
| @@ -69,10 +69,9 @@ public class UserSessionController { | @@ -69,10 +69,9 @@ public class UserSessionController { | ||
| 69 | 69 | ||
| 70 | @DeleteMapping("/delete") | 70 | @DeleteMapping("/delete") |
| 71 | @ApiOperation("删除 Session") | 71 | @ApiOperation("删除 Session") |
| 72 | - @ApiImplicitParam(name = "id", value = "Session 编号", required = true, dataTypeClass = String.class, | ||
| 73 | - example = "fe50b9f6-d177-44b1-8da9-72ea34f63db7") | 72 | + @ApiImplicitParam(name = "id", value = "Session 编号", required = true, dataTypeClass = Long.class, example = "1024") |
| 74 | @PreAuthorize("@ss.hasPermission('system:user-session:delete')") | 73 | @PreAuthorize("@ss.hasPermission('system:user-session:delete')") |
| 75 | - public CommonResult<Boolean> deleteUserSession(@RequestParam("id") String id) { | 74 | + public CommonResult<Boolean> deleteUserSession(@RequestParam("id") Long id) { |
| 76 | userSessionService.deleteUserSession(id); | 75 | userSessionService.deleteUserSession(id); |
| 77 | return success(true); | 76 | return success(true); |
| 78 | } | 77 | } |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/UserSessionDO.java
| @@ -3,10 +3,7 @@ package cn.iocoder.yudao.module.system.dal.dataobject.auth; | @@ -3,10 +3,7 @@ package cn.iocoder.yudao.module.system.dal.dataobject.auth; | ||
| 3 | import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | 3 | import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; |
| 4 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; | 4 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| 5 | import cn.iocoder.yudao.framework.security.core.LoginUser; | 5 | import cn.iocoder.yudao.framework.security.core.LoginUser; |
| 6 | -import com.baomidou.mybatisplus.annotation.IdType; | ||
| 7 | -import com.baomidou.mybatisplus.annotation.KeySequence; | ||
| 8 | -import com.baomidou.mybatisplus.annotation.TableId; | ||
| 9 | -import com.baomidou.mybatisplus.annotation.TableName; | 6 | +import com.baomidou.mybatisplus.annotation.*; |
| 10 | import lombok.Builder; | 7 | import lombok.Builder; |
| 11 | import lombok.Data; | 8 | import lombok.Data; |
| 12 | import lombok.EqualsAndHashCode; | 9 | import lombok.EqualsAndHashCode; |
| @@ -22,18 +19,21 @@ import java.util.Date; | @@ -22,18 +19,21 @@ import java.util.Date; | ||
| 22 | * | 19 | * |
| 23 | * @author 芋道源码 | 20 | * @author 芋道源码 |
| 24 | */ | 21 | */ |
| 25 | -@TableName(value = "system_user_session", autoResultMap = true) | ||
| 26 | -@KeySequence("system_user_session_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 | 22 | +@TableName(value = "system_user_session") |
| 27 | @Data | 23 | @Data |
| 28 | @Builder | 24 | @Builder |
| 29 | @EqualsAndHashCode(callSuper = true) | 25 | @EqualsAndHashCode(callSuper = true) |
| 30 | public class UserSessionDO extends BaseDO { | 26 | public class UserSessionDO extends BaseDO { |
| 31 | 27 | ||
| 32 | /** | 28 | /** |
| 33 | - * 会话编号, 即 sessionId | 29 | + * 会话编号 |
| 34 | */ | 30 | */ |
| 35 | - @TableId(type = IdType.INPUT) | ||
| 36 | - private String id; | 31 | + private Long id; |
| 32 | + /** | ||
| 33 | + * 令牌 | ||
| 34 | + */ | ||
| 35 | + private String token; | ||
| 36 | + | ||
| 37 | /** | 37 | /** |
| 38 | * 用户编号 | 38 | * 用户编号 |
| 39 | * | 39 | * |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/user/AdminUserDO.java
| @@ -19,7 +19,7 @@ import java.util.Set; | @@ -19,7 +19,7 @@ import java.util.Set; | ||
| 19 | * | 19 | * |
| 20 | * @author 芋道源码 | 20 | * @author 芋道源码 |
| 21 | */ | 21 | */ |
| 22 | -@TableName(value = "system_user", autoResultMap = true) | 22 | +@TableName(value = "system_users", autoResultMap = true) // 由于 SQL Server 的 system_user 是关键字,所以使用 system_users |
| 23 | @KeySequence("system_user_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 | 23 | @KeySequence("system_user_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 |
| 24 | @Data | 24 | @Data |
| 25 | @EqualsAndHashCode(callSuper = true) | 25 | @EqualsAndHashCode(callSuper = true) |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/UserSessionMapper.java
| 1 | package cn.iocoder.yudao.module.system.dal.mysql.auth; | 1 | package cn.iocoder.yudao.module.system.dal.mysql.auth; |
| 2 | 2 | ||
| 3 | -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.session.UserSessionPageReqVO; | ||
| 4 | -import cn.iocoder.yudao.module.system.dal.dataobject.auth.UserSessionDO; | ||
| 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 6 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | 4 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| 7 | -import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX; | 5 | +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| 6 | +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.session.UserSessionPageReqVO; | ||
| 7 | +import cn.iocoder.yudao.module.system.dal.dataobject.auth.UserSessionDO; | ||
| 8 | import org.apache.ibatis.annotations.Mapper; | 8 | import org.apache.ibatis.annotations.Mapper; |
| 9 | 9 | ||
| 10 | import java.util.Collection; | 10 | import java.util.Collection; |
| @@ -15,13 +15,23 @@ import java.util.List; | @@ -15,13 +15,23 @@ import java.util.List; | ||
| 15 | public interface UserSessionMapper extends BaseMapperX<UserSessionDO> { | 15 | public interface UserSessionMapper extends BaseMapperX<UserSessionDO> { |
| 16 | 16 | ||
| 17 | default PageResult<UserSessionDO> selectPage(UserSessionPageReqVO reqVO, Collection<Long> userIds) { | 17 | default PageResult<UserSessionDO> selectPage(UserSessionPageReqVO reqVO, Collection<Long> userIds) { |
| 18 | - return selectPage(reqVO, new QueryWrapperX<UserSessionDO>() | ||
| 19 | - .inIfPresent("user_id", userIds) | ||
| 20 | - .likeIfPresent("user_ip", reqVO.getUserIp())); | 18 | + return selectPage(reqVO, new LambdaQueryWrapperX<UserSessionDO>() |
| 19 | + .inIfPresent(UserSessionDO::getUserId, userIds) | ||
| 20 | + .likeIfPresent(UserSessionDO::getUserIp, reqVO.getUserIp())); | ||
| 21 | } | 21 | } |
| 22 | 22 | ||
| 23 | default List<UserSessionDO> selectListBySessionTimoutLt() { | 23 | default List<UserSessionDO> selectListBySessionTimoutLt() { |
| 24 | - return selectList(new QueryWrapperX<UserSessionDO>().lt("session_timeout",new Date())); | 24 | + return selectList(new LambdaQueryWrapperX<UserSessionDO>() |
| 25 | + .lt(UserSessionDO::getSessionTimeout, new Date())); | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | + default void updateByToken(String token, UserSessionDO updateObj) { | ||
| 29 | + update(updateObj, new LambdaQueryWrapperX<UserSessionDO>() | ||
| 30 | + .eq(UserSessionDO::getToken, token)); | ||
| 31 | + } | ||
| 32 | + | ||
| 33 | + default void deleteByToken(String token) { | ||
| 34 | + delete(new LambdaQueryWrapperX<UserSessionDO>().eq(UserSessionDO::getToken, token)); | ||
| 25 | } | 35 | } |
| 26 | 36 | ||
| 27 | } | 37 | } |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/RedisKeyConstants.java
| @@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.system.dal.redis; | @@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.system.dal.redis; | ||
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine; | 3 | import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine; |
| 4 | import cn.iocoder.yudao.framework.security.core.LoginUser; | 4 | import cn.iocoder.yudao.framework.security.core.LoginUser; |
| 5 | -import me.zhyd.oauth.model.AuthUser; | ||
| 6 | 5 | ||
| 7 | import java.time.Duration; | 6 | import java.time.Duration; |
| 8 | 7 | ||
| @@ -20,7 +19,7 @@ public interface RedisKeyConstants { | @@ -20,7 +19,7 @@ public interface RedisKeyConstants { | ||
| 20 | STRING, String.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC); | 19 | STRING, String.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC); |
| 21 | 20 | ||
| 22 | RedisKeyDefine LOGIN_USER = new RedisKeyDefine("登录用户的缓存", | 21 | RedisKeyDefine LOGIN_USER = new RedisKeyDefine("登录用户的缓存", |
| 23 | - "login_user:%s", // 参数为 sessionId | 22 | + "login_user:%s", // 参数为 token 令牌 |
| 24 | STRING, LoginUser.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC); | 23 | STRING, LoginUser.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC); |
| 25 | 24 | ||
| 26 | RedisKeyDefine SOCIAL_AUTH_STATE = new RedisKeyDefine("社交登陆的 state", // 注意,它是被 JustAuth 的 justauth.type.prefix 使用到 | 25 | RedisKeyDefine SOCIAL_AUTH_STATE = new RedisKeyDefine("社交登陆的 state", // 注意,它是被 JustAuth 的 justauth.type.prefix 使用到 |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/auth/LoginUserRedisDAO.java
| @@ -24,24 +24,29 @@ public class LoginUserRedisDAO { | @@ -24,24 +24,29 @@ public class LoginUserRedisDAO { | ||
| 24 | @Resource | 24 | @Resource |
| 25 | private SecurityProperties securityProperties; | 25 | private SecurityProperties securityProperties; |
| 26 | 26 | ||
| 27 | - public LoginUser get(String sessionId) { | ||
| 28 | - String redisKey = formatKey(sessionId); | 27 | + public LoginUser get(String token) { |
| 28 | + String redisKey = formatKey(token); | ||
| 29 | return JsonUtils.parseObject(stringRedisTemplate.opsForValue().get(redisKey), LoginUser.class); | 29 | return JsonUtils.parseObject(stringRedisTemplate.opsForValue().get(redisKey), LoginUser.class); |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | - public void set(String sessionId, LoginUser loginUser) { | ||
| 33 | - String redisKey = formatKey(sessionId); | 32 | + public Boolean exists(String token) { |
| 33 | + String redisKey = formatKey(token); | ||
| 34 | + return stringRedisTemplate.hasKey(redisKey); | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + public void set(String token, LoginUser loginUser) { | ||
| 38 | + String redisKey = formatKey(token); | ||
| 34 | stringRedisTemplate.opsForValue().set(redisKey, JsonUtils.toJsonString(loginUser), | 39 | stringRedisTemplate.opsForValue().set(redisKey, JsonUtils.toJsonString(loginUser), |
| 35 | securityProperties.getSessionTimeout()); | 40 | securityProperties.getSessionTimeout()); |
| 36 | } | 41 | } |
| 37 | 42 | ||
| 38 | - public void delete(String sessionId) { | ||
| 39 | - String redisKey = formatKey(sessionId); | 43 | + public void delete(String token) { |
| 44 | + String redisKey = formatKey(token); | ||
| 40 | stringRedisTemplate.delete(redisKey); | 45 | stringRedisTemplate.delete(redisKey); |
| 41 | } | 46 | } |
| 42 | 47 | ||
| 43 | - private static String formatKey(String sessionId) { | ||
| 44 | - return LOGIN_USER.formatKey(sessionId); | 48 | + private static String formatKey(String token) { |
| 49 | + return LOGIN_USER.formatKey(token); | ||
| 45 | } | 50 | } |
| 46 | 51 | ||
| 47 | } | 52 | } |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/job/auth/UserSessionTimeoutJob.java
| @@ -24,7 +24,7 @@ public class UserSessionTimeoutJob implements JobHandler { | @@ -24,7 +24,7 @@ public class UserSessionTimeoutJob implements JobHandler { | ||
| 24 | @Override | 24 | @Override |
| 25 | public String execute(String param) throws Exception { | 25 | public String execute(String param) throws Exception { |
| 26 | // 执行过期 | 26 | // 执行过期 |
| 27 | - Long timeoutCount = userSessionService.clearSessionTimeout(); | 27 | + Long timeoutCount = userSessionService.deleteTimeoutSession(); |
| 28 | // 返回结果,记录每次的超时数量 | 28 | // 返回结果,记录每次的超时数量 |
| 29 | return String.format("移除在线会话数量为 %s 个", timeoutCount); | 29 | return String.format("移除在线会话数量为 %s 个", timeoutCount); |
| 30 | } | 30 | } |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java
| @@ -104,7 +104,7 @@ public class AdminAuthServiceImpl implements AdminAuthService { | @@ -104,7 +104,7 @@ public class AdminAuthServiceImpl implements AdminAuthService { | ||
| 104 | // 使用账号密码,进行登录 | 104 | // 使用账号密码,进行登录 |
| 105 | LoginUser loginUser = login0(reqVO.getUsername(), reqVO.getPassword()); | 105 | LoginUser loginUser = login0(reqVO.getUsername(), reqVO.getPassword()); |
| 106 | 106 | ||
| 107 | - // 缓存登陆用户到 Redis 中,返回 sessionId 编号 | 107 | + // 缓存登陆用户到 Redis 中,返回 Token 令牌 |
| 108 | return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_USERNAME, userIp, userAgent); | 108 | return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_USERNAME, userIp, userAgent); |
| 109 | } | 109 | } |
| 110 | 110 | ||
| @@ -207,7 +207,7 @@ public class AdminAuthServiceImpl implements AdminAuthService { | @@ -207,7 +207,7 @@ public class AdminAuthServiceImpl implements AdminAuthService { | ||
| 207 | // 创建 LoginUser 对象 | 207 | // 创建 LoginUser 对象 |
| 208 | LoginUser loginUser = buildLoginUser(user); | 208 | LoginUser loginUser = buildLoginUser(user); |
| 209 | 209 | ||
| 210 | - // 缓存登录用户到 Redis 中,返回 sessionId 编号 | 210 | + // 缓存登录用户到 Redis 中,返回 Token 令牌 |
| 211 | return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_SOCIAL, userIp, userAgent); | 211 | return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_SOCIAL, userIp, userAgent); |
| 212 | } | 212 | } |
| 213 | 213 | ||
| @@ -219,14 +219,14 @@ public class AdminAuthServiceImpl implements AdminAuthService { | @@ -219,14 +219,14 @@ public class AdminAuthServiceImpl implements AdminAuthService { | ||
| 219 | // 绑定社交用户 | 219 | // 绑定社交用户 |
| 220 | socialUserService.bindSocialUser(AuthConvert.INSTANCE.convert(loginUser.getId(), getUserType().getValue(), reqVO)); | 220 | socialUserService.bindSocialUser(AuthConvert.INSTANCE.convert(loginUser.getId(), getUserType().getValue(), reqVO)); |
| 221 | 221 | ||
| 222 | - // 缓存登录用户到 Redis 中,返回 sessionId 编号 | 222 | + // 缓存登录用户到 Redis 中,返回 Token 令牌 |
| 223 | return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_SOCIAL, userIp, userAgent); | 223 | return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_SOCIAL, userIp, userAgent); |
| 224 | } | 224 | } |
| 225 | 225 | ||
| 226 | private String createUserSessionAfterLoginSuccess(LoginUser loginUser, LoginLogTypeEnum logType, String userIp, String userAgent) { | 226 | private String createUserSessionAfterLoginSuccess(LoginUser loginUser, LoginLogTypeEnum logType, String userIp, String userAgent) { |
| 227 | // 插入登陆日志 | 227 | // 插入登陆日志 |
| 228 | createLoginLog(loginUser.getUsername(), logType, LoginResultEnum.SUCCESS); | 228 | createLoginLog(loginUser.getUsername(), logType, LoginResultEnum.SUCCESS); |
| 229 | - // 缓存登录用户到 Redis 中,返回 sessionId 编号 | 229 | + // 缓存登录用户到 Redis 中,返回 Token 令牌 |
| 230 | return userSessionService.createUserSession(loginUser, userIp, userAgent); | 230 | return userSessionService.createUserSession(loginUser, userIp, userAgent); |
| 231 | } | 231 | } |
| 232 | 232 | ||
| @@ -240,7 +240,7 @@ public class AdminAuthServiceImpl implements AdminAuthService { | @@ -240,7 +240,7 @@ public class AdminAuthServiceImpl implements AdminAuthService { | ||
| 240 | // 删除 session | 240 | // 删除 session |
| 241 | userSessionService.deleteUserSession(token); | 241 | userSessionService.deleteUserSession(token); |
| 242 | // 记录登出日志 | 242 | // 记录登出日志 |
| 243 | - this.createLogoutLog(loginUser.getId(), loginUser.getUsername()); | 243 | + createLogoutLog(loginUser.getId(), loginUser.getUsername()); |
| 244 | } | 244 | } |
| 245 | 245 | ||
| 246 | @Override | 246 | @Override |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/UserSessionService.java
| @@ -25,7 +25,7 @@ public interface UserSessionService { | @@ -25,7 +25,7 @@ public interface UserSessionService { | ||
| 25 | * | 25 | * |
| 26 | * @return {@link Long } 移出的超时用户数量 | 26 | * @return {@link Long } 移出的超时用户数量 |
| 27 | **/ | 27 | **/ |
| 28 | - long clearSessionTimeout(); | 28 | + long deleteTimeoutSession(); |
| 29 | 29 | ||
| 30 | /** | 30 | /** |
| 31 | * 创建在线用户 Session | 31 | * 创建在线用户 Session |
| @@ -33,32 +33,39 @@ public interface UserSessionService { | @@ -33,32 +33,39 @@ public interface UserSessionService { | ||
| 33 | * @param loginUser 登录用户 | 33 | * @param loginUser 登录用户 |
| 34 | * @param userIp 用户 IP | 34 | * @param userIp 用户 IP |
| 35 | * @param userAgent 用户 UA | 35 | * @param userAgent 用户 UA |
| 36 | - * @return Session 编号 | 36 | + * @return Token 令牌 |
| 37 | */ | 37 | */ |
| 38 | String createUserSession(LoginUser loginUser, String userIp, String userAgent); | 38 | String createUserSession(LoginUser loginUser, String userIp, String userAgent); |
| 39 | 39 | ||
| 40 | /** | 40 | /** |
| 41 | * 刷新在线用户 Session 的更新时间 | 41 | * 刷新在线用户 Session 的更新时间 |
| 42 | * | 42 | * |
| 43 | - * @param sessionId Session 编号 | 43 | + * @param token 令牌 |
| 44 | * @param loginUser 登录用户 | 44 | * @param loginUser 登录用户 |
| 45 | */ | 45 | */ |
| 46 | - void refreshUserSession(String sessionId, LoginUser loginUser); | 46 | + void refreshUserSession(String token, LoginUser loginUser); |
| 47 | 47 | ||
| 48 | /** | 48 | /** |
| 49 | * 删除在线用户 Session | 49 | * 删除在线用户 Session |
| 50 | * | 50 | * |
| 51 | - * @param sessionId Session 编号 | 51 | + * @param token token 令牌 |
| 52 | */ | 52 | */ |
| 53 | - void deleteUserSession(String sessionId); | 53 | + void deleteUserSession(String token); |
| 54 | 54 | ||
| 55 | /** | 55 | /** |
| 56 | - * 获得 Session 编号对应的在线用户 | 56 | + * 删除在线用户 Session |
| 57 | + * | ||
| 58 | + * @param id 编号 | ||
| 59 | + */ | ||
| 60 | + void deleteUserSession(Long id); | ||
| 61 | + | ||
| 62 | + /** | ||
| 63 | + * 获得 Token 对应的在线用户 | ||
| 57 | * | 64 | * |
| 58 | - * @param sessionId Session 编号 | 65 | + * @param token 令牌 |
| 59 | * @return 在线用户 | 66 | * @return 在线用户 |
| 60 | */ | 67 | */ |
| 61 | - LoginUser getLoginUser(String sessionId); | 68 | + LoginUser getLoginUser(String token); |
| 62 | 69 | ||
| 63 | /** | 70 | /** |
| 64 | * 获得 Session 超时时间,单位:毫秒 | 71 | * 获得 Session 超时时间,单位:毫秒 |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/UserSessionServiceImpl.java
| @@ -3,28 +3,28 @@ package cn.iocoder.yudao.module.system.service.auth; | @@ -3,28 +3,28 @@ package cn.iocoder.yudao.module.system.service.auth; | ||
| 3 | import cn.hutool.core.collection.CollUtil; | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | import cn.hutool.core.util.IdUtil; | 4 | import cn.hutool.core.util.IdUtil; |
| 5 | import cn.hutool.core.util.StrUtil; | 5 | import cn.hutool.core.util.StrUtil; |
| 6 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 7 | +import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils; | ||
| 6 | import cn.iocoder.yudao.framework.security.config.SecurityProperties; | 8 | import cn.iocoder.yudao.framework.security.config.SecurityProperties; |
| 7 | import cn.iocoder.yudao.framework.security.core.LoginUser; | 9 | import cn.iocoder.yudao.framework.security.core.LoginUser; |
| 10 | +import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO; | ||
| 8 | import cn.iocoder.yudao.module.system.controller.admin.auth.vo.session.UserSessionPageReqVO; | 11 | import cn.iocoder.yudao.module.system.controller.admin.auth.vo.session.UserSessionPageReqVO; |
| 9 | import cn.iocoder.yudao.module.system.dal.dataobject.auth.UserSessionDO; | 12 | import cn.iocoder.yudao.module.system.dal.dataobject.auth.UserSessionDO; |
| 10 | import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; | 13 | import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; |
| 11 | import cn.iocoder.yudao.module.system.dal.mysql.auth.UserSessionMapper; | 14 | import cn.iocoder.yudao.module.system.dal.mysql.auth.UserSessionMapper; |
| 15 | +import cn.iocoder.yudao.module.system.dal.redis.auth.LoginUserRedisDAO; | ||
| 12 | import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; | 16 | import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; |
| 13 | import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum; | 17 | import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum; |
| 14 | import cn.iocoder.yudao.module.system.service.logger.LoginLogService; | 18 | import cn.iocoder.yudao.module.system.service.logger.LoginLogService; |
| 15 | import cn.iocoder.yudao.module.system.service.user.AdminUserService; | 19 | import cn.iocoder.yudao.module.system.service.user.AdminUserService; |
| 16 | -import cn.iocoder.yudao.module.system.dal.redis.auth.LoginUserRedisDAO; | ||
| 17 | -import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO; | ||
| 18 | -import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 19 | -import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils; | ||
| 20 | -import com.google.common.collect.Lists; | ||
| 21 | import lombok.extern.slf4j.Slf4j; | 20 | import lombok.extern.slf4j.Slf4j; |
| 22 | import org.springframework.stereotype.Service; | 21 | import org.springframework.stereotype.Service; |
| 23 | 22 | ||
| 24 | import javax.annotation.Resource; | 23 | import javax.annotation.Resource; |
| 25 | import java.time.Duration; | 24 | import java.time.Duration; |
| 26 | -import java.util.*; | ||
| 27 | -import java.util.stream.Collectors; | 25 | +import java.util.Collection; |
| 26 | +import java.util.Date; | ||
| 27 | +import java.util.List; | ||
| 28 | 28 | ||
| 29 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; | 29 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; |
| 30 | import static cn.iocoder.yudao.framework.common.util.date.DateUtils.addTime; | 30 | import static cn.iocoder.yudao.framework.common.util.date.DateUtils.addTime; |
| @@ -65,82 +65,99 @@ public class UserSessionServiceImpl implements UserSessionService { | @@ -65,82 +65,99 @@ public class UserSessionServiceImpl implements UserSessionService { | ||
| 65 | return userSessionMapper.selectPage(reqVO, userIds); | 65 | return userSessionMapper.selectPage(reqVO, userIds); |
| 66 | } | 66 | } |
| 67 | 67 | ||
| 68 | - // TODO @芋艿:优化下该方法 | ||
| 69 | @Override | 68 | @Override |
| 70 | - public long clearSessionTimeout() { | ||
| 71 | - // 获取db里已经超时的用户列表 | ||
| 72 | - List<UserSessionDO> sessionTimeoutDOS = userSessionMapper.selectListBySessionTimoutLt(); | ||
| 73 | - Map<String, UserSessionDO> timeoutSessionDOMap = sessionTimeoutDOS | ||
| 74 | - .stream() | ||
| 75 | - .filter(sessionDO -> loginUserRedisDAO.get(sessionDO.getId()) == null) | ||
| 76 | - .collect(Collectors.toMap(UserSessionDO::getId, o -> o)); | ||
| 77 | - // 确认已经超时,按批次移出在线用户列表 | ||
| 78 | - if (CollUtil.isNotEmpty(timeoutSessionDOMap)) { | ||
| 79 | - Lists.partition(new ArrayList<>(timeoutSessionDOMap.keySet()), 100) | ||
| 80 | - .forEach(userSessionMapper::deleteBatchIds); | ||
| 81 | - // 记录用户超时退出日志 | ||
| 82 | - createTimeoutLogoutLog(timeoutSessionDOMap.values()); | 69 | + public long deleteTimeoutSession() { |
| 70 | + // 获取 db 里已经超时的用户列表 | ||
| 71 | + List<UserSessionDO> timeoutSessions = userSessionMapper.selectListBySessionTimoutLt(); | ||
| 72 | + if (CollUtil.isEmpty(timeoutSessions)) { | ||
| 73 | + return 0L; | ||
| 83 | } | 74 | } |
| 84 | - return timeoutSessionDOMap.size(); | ||
| 85 | - } | ||
| 86 | 75 | ||
| 87 | - private void createTimeoutLogoutLog(Collection<UserSessionDO> timeoutSessionDOS) { | ||
| 88 | - for (UserSessionDO timeoutSessionDO : timeoutSessionDOS) { | ||
| 89 | - LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO(); | ||
| 90 | - reqDTO.setLogType(LoginLogTypeEnum.LOGOUT_TIMEOUT.getType()); | ||
| 91 | - reqDTO.setTraceId(TracerUtils.getTraceId()); | ||
| 92 | - reqDTO.setUserId(timeoutSessionDO.getUserId()); | ||
| 93 | - reqDTO.setUserType(timeoutSessionDO.getUserType()); | ||
| 94 | - reqDTO.setUsername(timeoutSessionDO.getUsername()); | ||
| 95 | - reqDTO.setUserAgent(timeoutSessionDO.getUserAgent()); | ||
| 96 | - reqDTO.setUserIp(timeoutSessionDO.getUserIp()); | ||
| 97 | - reqDTO.setResult(LoginResultEnum.SUCCESS.getResult()); | ||
| 98 | - loginLogService.createLoginLog(reqDTO); | 76 | + // 由于过期的用户一般不多,所以顺序遍历,进行清理 |
| 77 | + int count = 0; | ||
| 78 | + for (UserSessionDO session : timeoutSessions) { | ||
| 79 | + // 基于 Redis 二次判断,同时也保证 Redis Key 的立即过期,避免延迟导致浪费内存空间 | ||
| 80 | + if (loginUserRedisDAO.exists(session.getToken())) { | ||
| 81 | + continue; | ||
| 82 | + } | ||
| 83 | + userSessionMapper.deleteById(session.getId()); | ||
| 84 | + // 记录退出日志 | ||
| 85 | + createLogoutLog(session, LoginLogTypeEnum.LOGOUT_TIMEOUT); | ||
| 86 | + count++; | ||
| 99 | } | 87 | } |
| 88 | + return count; | ||
| 89 | + } | ||
| 90 | + | ||
| 91 | + private void createLogoutLog(UserSessionDO session, LoginLogTypeEnum type) { | ||
| 92 | + LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO(); | ||
| 93 | + reqDTO.setLogType(type.getType()); | ||
| 94 | + reqDTO.setTraceId(TracerUtils.getTraceId()); | ||
| 95 | + reqDTO.setUserId(session.getUserId()); | ||
| 96 | + reqDTO.setUserType(session.getUserType()); | ||
| 97 | + reqDTO.setUsername(session.getUsername()); | ||
| 98 | + reqDTO.setUserAgent(session.getUserAgent()); | ||
| 99 | + reqDTO.setUserIp(session.getUserIp()); | ||
| 100 | + reqDTO.setResult(LoginResultEnum.SUCCESS.getResult()); | ||
| 101 | + loginLogService.createLoginLog(reqDTO); | ||
| 100 | } | 102 | } |
| 101 | 103 | ||
| 102 | @Override | 104 | @Override |
| 103 | public String createUserSession(LoginUser loginUser, String userIp, String userAgent) { | 105 | public String createUserSession(LoginUser loginUser, String userIp, String userAgent) { |
| 104 | // 生成 Session 编号 | 106 | // 生成 Session 编号 |
| 105 | - String sessionId = generateSessionId(); | 107 | + String token = generateToken(); |
| 106 | // 写入 Redis 缓存 | 108 | // 写入 Redis 缓存 |
| 107 | loginUser.setUpdateTime(new Date()); | 109 | loginUser.setUpdateTime(new Date()); |
| 108 | - loginUserRedisDAO.set(sessionId, loginUser); | 110 | + loginUserRedisDAO.set(token, loginUser); |
| 109 | // 写入 DB 中 | 111 | // 写入 DB 中 |
| 110 | - UserSessionDO userSession = UserSessionDO.builder().id(sessionId) | 112 | + UserSessionDO userSession = UserSessionDO.builder().token(token) |
| 111 | .userId(loginUser.getId()).userType(loginUser.getUserType()) | 113 | .userId(loginUser.getId()).userType(loginUser.getUserType()) |
| 112 | .userIp(userIp).userAgent(userAgent).username(loginUser.getUsername()) | 114 | .userIp(userIp).userAgent(userAgent).username(loginUser.getUsername()) |
| 113 | .sessionTimeout(addTime(Duration.ofMillis(getSessionTimeoutMillis()))) | 115 | .sessionTimeout(addTime(Duration.ofMillis(getSessionTimeoutMillis()))) |
| 114 | .build(); | 116 | .build(); |
| 115 | userSessionMapper.insert(userSession); | 117 | userSessionMapper.insert(userSession); |
| 116 | - // 返回 Session 编号 | ||
| 117 | - return sessionId; | 118 | + // 返回 Token 令牌 |
| 119 | + return token; | ||
| 118 | } | 120 | } |
| 119 | 121 | ||
| 120 | @Override | 122 | @Override |
| 121 | - public void refreshUserSession(String sessionId, LoginUser loginUser) { | 123 | + public void refreshUserSession(String token, LoginUser loginUser) { |
| 122 | // 写入 Redis 缓存 | 124 | // 写入 Redis 缓存 |
| 123 | loginUser.setUpdateTime(new Date()); | 125 | loginUser.setUpdateTime(new Date()); |
| 124 | - loginUserRedisDAO.set(sessionId, loginUser); | 126 | + loginUserRedisDAO.set(token, loginUser); |
| 125 | // 更新 DB 中 | 127 | // 更新 DB 中 |
| 126 | - UserSessionDO updateObj = UserSessionDO.builder().id(sessionId).build(); | 128 | + UserSessionDO updateObj = UserSessionDO.builder().build(); |
| 127 | updateObj.setUsername(loginUser.getUsername()); | 129 | updateObj.setUsername(loginUser.getUsername()); |
| 128 | updateObj.setUpdateTime(new Date()); | 130 | updateObj.setUpdateTime(new Date()); |
| 129 | updateObj.setSessionTimeout(addTime(Duration.ofMillis(getSessionTimeoutMillis()))); | 131 | updateObj.setSessionTimeout(addTime(Duration.ofMillis(getSessionTimeoutMillis()))); |
| 130 | - userSessionMapper.updateById(updateObj); | 132 | + userSessionMapper.updateByToken(token, updateObj); |
| 131 | } | 133 | } |
| 132 | 134 | ||
| 133 | @Override | 135 | @Override |
| 134 | - public void deleteUserSession(String sessionId) { | 136 | + public void deleteUserSession(String token) { |
| 137 | + // 删除 Redis 缓存 | ||
| 138 | + loginUserRedisDAO.delete(token); | ||
| 139 | + // 删除 DB 记录 | ||
| 140 | + userSessionMapper.deleteByToken(token); | ||
| 141 | + // 无需记录日志,因为退出那已经记录 | ||
| 142 | + } | ||
| 143 | + | ||
| 144 | + @Override | ||
| 145 | + public void deleteUserSession(Long id) { | ||
| 146 | + UserSessionDO session = userSessionMapper.selectById(id); | ||
| 147 | + if (session == null) { | ||
| 148 | + return; | ||
| 149 | + } | ||
| 135 | // 删除 Redis 缓存 | 150 | // 删除 Redis 缓存 |
| 136 | - loginUserRedisDAO.delete(sessionId); | 151 | + loginUserRedisDAO.delete(session.getToken()); |
| 137 | // 删除 DB 记录 | 152 | // 删除 DB 记录 |
| 138 | - userSessionMapper.deleteById(sessionId); | 153 | + userSessionMapper.deleteById(id); |
| 154 | + // 记录退出日志 | ||
| 155 | + createLogoutLog(session, LoginLogTypeEnum.LOGOUT_DELETE); | ||
| 139 | } | 156 | } |
| 140 | 157 | ||
| 141 | @Override | 158 | @Override |
| 142 | - public LoginUser getLoginUser(String sessionId) { | ||
| 143 | - return loginUserRedisDAO.get(sessionId); | 159 | + public LoginUser getLoginUser(String token) { |
| 160 | + return loginUserRedisDAO.get(token); | ||
| 144 | } | 161 | } |
| 145 | 162 | ||
| 146 | @Override | 163 | @Override |
| @@ -149,11 +166,11 @@ public class UserSessionServiceImpl implements UserSessionService { | @@ -149,11 +166,11 @@ public class UserSessionServiceImpl implements UserSessionService { | ||
| 149 | } | 166 | } |
| 150 | 167 | ||
| 151 | /** | 168 | /** |
| 152 | - * 生成 Session 编号,目前采用 UUID 算法 | 169 | + * 生成 Token 令牌,目前采用 UUID 算法 |
| 153 | * | 170 | * |
| 154 | * @return Session 编号 | 171 | * @return Session 编号 |
| 155 | */ | 172 | */ |
| 156 | - private static String generateSessionId() { | 173 | + private static String generateToken() { |
| 157 | return IdUtil.fastSimpleUUID(); | 174 | return IdUtil.fastSimpleUUID(); |
| 158 | } | 175 | } |
| 159 | 176 |
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AuthServiceImplTest.java
| @@ -134,6 +134,7 @@ public class AuthServiceImplTest extends BaseDbUnitTest { | @@ -134,6 +134,7 @@ public class AuthServiceImplTest extends BaseDbUnitTest { | ||
| 134 | AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class); | 134 | AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class); |
| 135 | String userIp = randomString(); | 135 | String userIp = randomString(); |
| 136 | String userAgent = randomString(); | 136 | String userAgent = randomString(); |
| 137 | + | ||
| 137 | // 调用, 并断言异常 | 138 | // 调用, 并断言异常 |
| 138 | assertServiceException(() -> authService.login(reqVO, userIp, userAgent), AUTH_LOGIN_CAPTCHA_NOT_FOUND); | 139 | assertServiceException(() -> authService.login(reqVO, userIp, userAgent), AUTH_LOGIN_CAPTCHA_NOT_FOUND); |
| 139 | // 校验调用参数 | 140 | // 校验调用参数 |
| @@ -148,10 +149,12 @@ public class AuthServiceImplTest extends BaseDbUnitTest { | @@ -148,10 +149,12 @@ public class AuthServiceImplTest extends BaseDbUnitTest { | ||
| 148 | // 准备参数 | 149 | // 准备参数 |
| 149 | String userIp = randomString(); | 150 | String userIp = randomString(); |
| 150 | String userAgent = randomString(); | 151 | String userAgent = randomString(); |
| 151 | - String code = randomString(); | ||
| 152 | AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class); | 152 | AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class); |
| 153 | + | ||
| 153 | // mock 验证码不正确 | 154 | // mock 验证码不正确 |
| 155 | + String code = randomString(); | ||
| 154 | when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(code); | 156 | when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(code); |
| 157 | + | ||
| 155 | // 调用, 并断言异常 | 158 | // 调用, 并断言异常 |
| 156 | assertServiceException(() -> authService.login(reqVO, userIp, userAgent), AUTH_LOGIN_CAPTCHA_CODE_ERROR); | 159 | assertServiceException(() -> authService.login(reqVO, userIp, userAgent), AUTH_LOGIN_CAPTCHA_CODE_ERROR); |
| 157 | // 校验调用参数 | 160 | // 校验调用参数 |
| @@ -172,6 +175,7 @@ public class AuthServiceImplTest extends BaseDbUnitTest { | @@ -172,6 +175,7 @@ public class AuthServiceImplTest extends BaseDbUnitTest { | ||
| 172 | // mock 抛出异常 | 175 | // mock 抛出异常 |
| 173 | when(authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(reqVO.getUsername(), reqVO.getPassword()))) | 176 | when(authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(reqVO.getUsername(), reqVO.getPassword()))) |
| 174 | .thenThrow(new BadCredentialsException("测试账号或密码不正确")); | 177 | .thenThrow(new BadCredentialsException("测试账号或密码不正确")); |
| 178 | + | ||
| 175 | // 调用, 并断言异常 | 179 | // 调用, 并断言异常 |
| 176 | assertServiceException(() -> authService.login(reqVO, userIp, userAgent), AUTH_LOGIN_BAD_CREDENTIALS); | 180 | assertServiceException(() -> authService.login(reqVO, userIp, userAgent), AUTH_LOGIN_BAD_CREDENTIALS); |
| 177 | // 校验调用参数 | 181 | // 校验调用参数 |
| @@ -188,11 +192,13 @@ public class AuthServiceImplTest extends BaseDbUnitTest { | @@ -188,11 +192,13 @@ public class AuthServiceImplTest extends BaseDbUnitTest { | ||
| 188 | String userIp = randomString(); | 192 | String userIp = randomString(); |
| 189 | String userAgent = randomString(); | 193 | String userAgent = randomString(); |
| 190 | AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class); | 194 | AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class); |
| 195 | + | ||
| 191 | // mock 验证码正确 | 196 | // mock 验证码正确 |
| 192 | when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(reqVO.getCode()); | 197 | when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(reqVO.getCode()); |
| 193 | // mock 抛出异常 | 198 | // mock 抛出异常 |
| 194 | when(authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(reqVO.getUsername(), reqVO.getPassword()))) | 199 | when(authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(reqVO.getUsername(), reqVO.getPassword()))) |
| 195 | .thenThrow(new DisabledException("测试用户被禁用")); | 200 | .thenThrow(new DisabledException("测试用户被禁用")); |
| 201 | + | ||
| 196 | // 调用, 并断言异常 | 202 | // 调用, 并断言异常 |
| 197 | assertServiceException(() -> authService.login(reqVO, userIp, userAgent), AUTH_LOGIN_USER_DISABLED); | 203 | assertServiceException(() -> authService.login(reqVO, userIp, userAgent), AUTH_LOGIN_USER_DISABLED); |
| 198 | // 校验调用参数 | 204 | // 校验调用参数 |
| @@ -214,6 +220,7 @@ public class AuthServiceImplTest extends BaseDbUnitTest { | @@ -214,6 +220,7 @@ public class AuthServiceImplTest extends BaseDbUnitTest { | ||
| 214 | // mock 抛出异常 | 220 | // mock 抛出异常 |
| 215 | when(authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(reqVO.getUsername(), reqVO.getPassword()))) | 221 | when(authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(reqVO.getUsername(), reqVO.getPassword()))) |
| 216 | .thenThrow(new AuthenticationException("测试未知异常") {}); | 222 | .thenThrow(new AuthenticationException("测试未知异常") {}); |
| 223 | + | ||
| 217 | // 调用, 并断言异常 | 224 | // 调用, 并断言异常 |
| 218 | assertServiceException(() -> authService.login(reqVO, userIp, userAgent), AUTH_LOGIN_FAIL_UNKNOWN); | 225 | assertServiceException(() -> authService.login(reqVO, userIp, userAgent), AUTH_LOGIN_FAIL_UNKNOWN); |
| 219 | // 校验调用参数 | 226 | // 校验调用参数 |
| @@ -229,27 +236,29 @@ public class AuthServiceImplTest extends BaseDbUnitTest { | @@ -229,27 +236,29 @@ public class AuthServiceImplTest extends BaseDbUnitTest { | ||
| 229 | // 准备参数 | 236 | // 准备参数 |
| 230 | String userIp = randomString(); | 237 | String userIp = randomString(); |
| 231 | String userAgent = randomString(); | 238 | String userAgent = randomString(); |
| 239 | + AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class); | ||
| 240 | + | ||
| 241 | + // mock 验证码正确 | ||
| 242 | + when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(reqVO.getCode()); | ||
| 243 | + // mock authentication | ||
| 232 | Long userId = randomLongId(); | 244 | Long userId = randomLongId(); |
| 233 | Set<Long> userRoleIds = randomSet(Long.class); | 245 | Set<Long> userRoleIds = randomSet(Long.class); |
| 234 | - String sessionId = randomString(); | ||
| 235 | - AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class); | ||
| 236 | LoginUser loginUser = randomPojo(LoginUser.class, o -> { | 246 | LoginUser loginUser = randomPojo(LoginUser.class, o -> { |
| 237 | o.setId(userId); | 247 | o.setId(userId); |
| 238 | o.setRoleIds(userRoleIds); | 248 | o.setRoleIds(userRoleIds); |
| 239 | }); | 249 | }); |
| 240 | - // mock 验证码正确 | ||
| 241 | - when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(reqVO.getCode()); | ||
| 242 | - // mock authentication | ||
| 243 | when(authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(reqVO.getUsername(), reqVO.getPassword()))) | 250 | when(authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(reqVO.getUsername(), reqVO.getPassword()))) |
| 244 | .thenReturn(authentication); | 251 | .thenReturn(authentication); |
| 245 | when(authentication.getPrincipal()).thenReturn(loginUser); | 252 | when(authentication.getPrincipal()).thenReturn(loginUser); |
| 246 | // mock 获得 User 拥有的角色编号数组 | 253 | // mock 获得 User 拥有的角色编号数组 |
| 247 | when(permissionService.getUserRoleIds(userId, singleton(CommonStatusEnum.ENABLE.getStatus()))).thenReturn(userRoleIds); | 254 | when(permissionService.getUserRoleIds(userId, singleton(CommonStatusEnum.ENABLE.getStatus()))).thenReturn(userRoleIds); |
| 248 | // mock 缓存登录用户到 Redis | 255 | // mock 缓存登录用户到 Redis |
| 249 | - when(userSessionService.createUserSession(loginUser, userIp, userAgent)).thenReturn(sessionId); | 256 | + String token = randomString(); |
| 257 | + when(userSessionService.createUserSession(loginUser, userIp, userAgent)).thenReturn(token); | ||
| 258 | + | ||
| 250 | // 调用, 并断言异常 | 259 | // 调用, 并断言异常 |
| 251 | String login = authService.login(reqVO, userIp, userAgent); | 260 | String login = authService.login(reqVO, userIp, userAgent); |
| 252 | - assertEquals(sessionId, login); | 261 | + assertEquals(token, login); |
| 253 | // 校验调用参数 | 262 | // 校验调用参数 |
| 254 | verify(captchaService, times(1)).deleteCaptchaCode(reqVO.getUuid()); | 263 | verify(captchaService, times(1)).deleteCaptchaCode(reqVO.getUuid()); |
| 255 | verify(loginLogService, times(1)).createLoginLog( | 264 | verify(loginLogService, times(1)).createLoginLog( |
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/UserSessionServiceImplTest.java
| 1 | package cn.iocoder.yudao.module.system.service.auth; | 1 | package cn.iocoder.yudao.module.system.service.auth; |
| 2 | 2 | ||
| 3 | -import cn.hutool.core.date.DateUtil; | 3 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 4 | +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | ||
| 5 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 6 | +import cn.iocoder.yudao.framework.common.util.date.DateUtils; | ||
| 7 | +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; | ||
| 4 | import cn.iocoder.yudao.framework.security.config.SecurityProperties; | 8 | import cn.iocoder.yudao.framework.security.config.SecurityProperties; |
| 5 | import cn.iocoder.yudao.framework.security.core.LoginUser; | 9 | import cn.iocoder.yudao.framework.security.core.LoginUser; |
| 10 | +import cn.iocoder.yudao.framework.test.core.ut.BaseDbAndRedisUnitTest; | ||
| 6 | import cn.iocoder.yudao.module.system.controller.admin.auth.vo.session.UserSessionPageReqVO; | 11 | import cn.iocoder.yudao.module.system.controller.admin.auth.vo.session.UserSessionPageReqVO; |
| 12 | +import cn.iocoder.yudao.module.system.dal.dataobject.auth.UserSessionDO; | ||
| 7 | import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; | 13 | import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; |
| 8 | import cn.iocoder.yudao.module.system.dal.mysql.auth.UserSessionMapper; | 14 | import cn.iocoder.yudao.module.system.dal.mysql.auth.UserSessionMapper; |
| 9 | -import cn.iocoder.yudao.module.system.service.logger.LoginLogService; | ||
| 10 | -import cn.iocoder.yudao.module.system.service.user.AdminUserService; | ||
| 11 | -import cn.iocoder.yudao.module.system.dal.dataobject.auth.UserSessionDO; | ||
| 12 | import cn.iocoder.yudao.module.system.dal.redis.auth.LoginUserRedisDAO; | 15 | import cn.iocoder.yudao.module.system.dal.redis.auth.LoginUserRedisDAO; |
| 13 | import cn.iocoder.yudao.module.system.enums.common.SexEnum; | 16 | import cn.iocoder.yudao.module.system.enums.common.SexEnum; |
| 14 | -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||
| 15 | -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | ||
| 16 | -import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 17 | -import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; | ||
| 18 | -import cn.iocoder.yudao.framework.test.core.ut.BaseDbAndRedisUnitTest; | 17 | +import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; |
| 18 | +import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum; | ||
| 19 | +import cn.iocoder.yudao.module.system.service.logger.LoginLogService; | ||
| 20 | +import cn.iocoder.yudao.module.system.service.user.AdminUserService; | ||
| 21 | +import org.junit.jupiter.api.BeforeEach; | ||
| 19 | import org.junit.jupiter.api.Test; | 22 | import org.junit.jupiter.api.Test; |
| 20 | import org.springframework.boot.test.mock.mockito.MockBean; | 23 | import org.springframework.boot.test.mock.mockito.MockBean; |
| 21 | import org.springframework.context.annotation.Import; | 24 | import org.springframework.context.annotation.Import; |
| 22 | 25 | ||
| 23 | import javax.annotation.Resource; | 26 | import javax.annotation.Resource; |
| 24 | import java.time.Duration; | 27 | import java.time.Duration; |
| 25 | -import java.util.Date; | ||
| 26 | -import java.util.List; | ||
| 27 | -import java.util.stream.Collectors; | ||
| 28 | -import java.util.stream.Stream; | 28 | +import java.util.Calendar; |
| 29 | 29 | ||
| 30 | import static cn.hutool.core.util.RandomUtil.randomEle; | 30 | import static cn.hutool.core.util.RandomUtil.randomEle; |
| 31 | import static cn.iocoder.yudao.framework.common.util.date.DateUtils.addTime; | 31 | import static cn.iocoder.yudao.framework.common.util.date.DateUtils.addTime; |
| @@ -33,8 +33,9 @@ import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEq | @@ -33,8 +33,9 @@ import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEq | ||
| 33 | import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | 33 | import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; |
| 34 | import static java.util.Collections.singletonList; | 34 | import static java.util.Collections.singletonList; |
| 35 | import static org.junit.jupiter.api.Assertions.*; | 35 | import static org.junit.jupiter.api.Assertions.*; |
| 36 | -import static org.junit.jupiter.api.Assertions.assertNull; | 36 | +import static org.mockito.ArgumentMatchers.argThat; |
| 37 | import static org.mockito.ArgumentMatchers.eq; | 37 | import static org.mockito.ArgumentMatchers.eq; |
| 38 | +import static org.mockito.Mockito.verify; | ||
| 38 | import static org.mockito.Mockito.when; | 39 | import static org.mockito.Mockito.when; |
| 39 | 40 | ||
| 40 | /** | 41 | /** |
| @@ -61,6 +62,11 @@ public class UserSessionServiceImplTest extends BaseDbAndRedisUnitTest { | @@ -61,6 +62,11 @@ public class UserSessionServiceImplTest extends BaseDbAndRedisUnitTest { | ||
| 61 | @MockBean | 62 | @MockBean |
| 62 | private SecurityProperties securityProperties; | 63 | private SecurityProperties securityProperties; |
| 63 | 64 | ||
| 65 | + @BeforeEach | ||
| 66 | + public void setUp() { | ||
| 67 | + when(securityProperties.getSessionTimeout()).thenReturn(Duration.ofDays(1L)); | ||
| 68 | + } | ||
| 69 | + | ||
| 64 | @Test | 70 | @Test |
| 65 | public void testGetUserSessionPage_success() { | 71 | public void testGetUserSessionPage_success() { |
| 66 | // mock 数据 | 72 | // mock 数据 |
| @@ -78,15 +84,9 @@ public class UserSessionServiceImplTest extends BaseDbAndRedisUnitTest { | @@ -78,15 +84,9 @@ public class UserSessionServiceImplTest extends BaseDbAndRedisUnitTest { | ||
| 78 | }); | 84 | }); |
| 79 | userSessionMapper.insert(dbSession); | 85 | userSessionMapper.insert(dbSession); |
| 80 | // 测试 username 不匹配 | 86 | // 测试 username 不匹配 |
| 81 | - userSessionMapper.insert(ObjectUtils.cloneIgnoreId(dbSession, o -> { | ||
| 82 | - o.setId(randomString()); | ||
| 83 | - o.setUserId(123456L); | ||
| 84 | - })); | 87 | + userSessionMapper.insert(ObjectUtils.cloneIgnoreId(dbSession, o -> o.setUserId(123456L))); |
| 85 | // 测试 userIp 不匹配 | 88 | // 测试 userIp 不匹配 |
| 86 | - userSessionMapper.insert(ObjectUtils.cloneIgnoreId(dbSession, o -> { | ||
| 87 | - o.setId(randomString()); | ||
| 88 | - o.setUserIp("testUserIp"); | ||
| 89 | - })); | 89 | + userSessionMapper.insert(ObjectUtils.cloneIgnoreId(dbSession, o -> o.setUserIp("testUserIp"))); |
| 90 | // 准备参数 | 90 | // 准备参数 |
| 91 | UserSessionPageReqVO reqVO = new UserSessionPageReqVO(); | 91 | UserSessionPageReqVO reqVO = new UserSessionPageReqVO(); |
| 92 | reqVO.setUsername(dbUser.getUsername()); | 92 | reqVO.setUsername(dbUser.getUsername()); |
| @@ -100,35 +100,60 @@ public class UserSessionServiceImplTest extends BaseDbAndRedisUnitTest { | @@ -100,35 +100,60 @@ public class UserSessionServiceImplTest extends BaseDbAndRedisUnitTest { | ||
| 100 | assertPojoEquals(dbSession, pageResult.getList().get(0)); | 100 | assertPojoEquals(dbSession, pageResult.getList().get(0)); |
| 101 | } | 101 | } |
| 102 | 102 | ||
| 103 | - // TODO 芋艿:单测写的有问题 | 103 | + @Test |
| 104 | + public void testClearSessionTimeout_none() { | ||
| 105 | + // mock db 数据 | ||
| 106 | + UserSessionDO userSession = randomPojo(UserSessionDO.class, o -> { | ||
| 107 | + o.setUserType(randomEle(UserTypeEnum.values()).getValue()); | ||
| 108 | + o.setSessionTimeout(addTime(Duration.ofDays(1))); | ||
| 109 | + }); | ||
| 110 | + userSessionMapper.insert(userSession); | ||
| 111 | + | ||
| 112 | + // 调用 | ||
| 113 | + long count = userSessionService.deleteTimeoutSession(); | ||
| 114 | + // 断言 | ||
| 115 | + assertEquals(0, count); | ||
| 116 | + assertPojoEquals(userSession, userSessionMapper.selectById(userSession.getId())); // 未删除 | ||
| 117 | + } | ||
| 118 | + | ||
| 119 | + @Test // Redis 还存在的情况 | ||
| 120 | + public void testClearSessionTimeout_exists() { | ||
| 121 | + // mock db 数据 | ||
| 122 | + UserSessionDO userSession = randomPojo(UserSessionDO.class, o -> { | ||
| 123 | + o.setUserType(randomEle(UserTypeEnum.values()).getValue()); | ||
| 124 | + o.setSessionTimeout(DateUtils.addDate(Calendar.DAY_OF_YEAR, -1)); | ||
| 125 | + }); | ||
| 126 | + userSessionMapper.insert(userSession); | ||
| 127 | + // mock redis 数据 | ||
| 128 | + loginUserRedisDAO.set(userSession.getToken(), new LoginUser()); | ||
| 129 | + | ||
| 130 | + // 调用 | ||
| 131 | + long count = userSessionService.deleteTimeoutSession(); | ||
| 132 | + // 断言 | ||
| 133 | + assertEquals(0, count); | ||
| 134 | + assertPojoEquals(userSession, userSessionMapper.selectById(userSession.getId())); // 未删除 | ||
| 135 | + } | ||
| 136 | + | ||
| 104 | @Test | 137 | @Test |
| 105 | public void testClearSessionTimeout_success() { | 138 | public void testClearSessionTimeout_success() { |
| 106 | - // 准备超时数据 120 条, 在线用户 1 条 | ||
| 107 | - int expectedTimeoutCount = 120, expectedTotal = 1; | ||
| 108 | - | ||
| 109 | - // 准备数据 | ||
| 110 | - List<UserSessionDO> prepareData = Stream | ||
| 111 | - .iterate(0, i -> i) | ||
| 112 | - .limit(expectedTimeoutCount) | ||
| 113 | - .map(i -> randomPojo(UserSessionDO.class, o -> { | ||
| 114 | - o.setUserType(randomEle(UserTypeEnum.values()).getValue()); | ||
| 115 | - o.setSessionTimeout(DateUtil.offsetSecond(new Date(), -1)); | ||
| 116 | - })) | ||
| 117 | - .collect(Collectors.toList()); | ||
| 118 | - UserSessionDO sessionDO = randomPojo(UserSessionDO.class, o -> { | 139 | + // mock db 数据 |
| 140 | + UserSessionDO userSession = randomPojo(UserSessionDO.class, o -> { | ||
| 119 | o.setUserType(randomEle(UserTypeEnum.values()).getValue()); | 141 | o.setUserType(randomEle(UserTypeEnum.values()).getValue()); |
| 120 | - o.setSessionTimeout(DateUtil.offsetMinute(new Date(), 30)); | 142 | + o.setSessionTimeout(DateUtils.addDate(Calendar.DAY_OF_YEAR, -1)); |
| 121 | }); | 143 | }); |
| 122 | - prepareData.add(sessionDO); | ||
| 123 | - prepareData.forEach(userSessionMapper::insert); | 144 | + userSessionMapper.insert(userSession); |
| 124 | 145 | ||
| 125 | // 清空超时数据 | 146 | // 清空超时数据 |
| 126 | - long actualTimeoutCount = userSessionService.clearSessionTimeout(); | 147 | + long count = userSessionService.deleteTimeoutSession(); |
| 127 | // 校验 | 148 | // 校验 |
| 128 | - assertEquals(expectedTimeoutCount, actualTimeoutCount); | ||
| 129 | - List<UserSessionDO> userSessionDOS = userSessionMapper.selectList(); | ||
| 130 | - assertEquals(expectedTotal, userSessionDOS.size()); | ||
| 131 | - assertPojoEquals(sessionDO, userSessionDOS.get(0), "updateTime"); | 149 | + assertEquals(1, count); |
| 150 | + assertNull(userSessionMapper.selectById(userSession.getId())); // 已删除 | ||
| 151 | + verify(loginLogService).createLoginLog(argThat(loginLog -> { | ||
| 152 | + assertPojoEquals(userSession, loginLog); | ||
| 153 | + assertEquals(LoginLogTypeEnum.LOGOUT_TIMEOUT.getType(), loginLog.getLogType()); | ||
| 154 | + assertEquals(LoginResultEnum.SUCCESS.getResult(), loginLog.getResult()); | ||
| 155 | + return true; | ||
| 156 | + })); | ||
| 132 | } | 157 | } |
| 133 | 158 | ||
| 134 | @Test | 159 | @Test |
| @@ -140,80 +165,86 @@ public class UserSessionServiceImplTest extends BaseDbAndRedisUnitTest { | @@ -140,80 +165,86 @@ public class UserSessionServiceImplTest extends BaseDbAndRedisUnitTest { | ||
| 140 | o.setUserType(randomEle(UserTypeEnum.values()).getValue()); | 165 | o.setUserType(randomEle(UserTypeEnum.values()).getValue()); |
| 141 | o.setTenantId(0L); // 租户设置为 0,因为暂未启用多租户组件 | 166 | o.setTenantId(0L); // 租户设置为 0,因为暂未启用多租户组件 |
| 142 | }); | 167 | }); |
| 143 | - // mock 方法 | ||
| 144 | - when(securityProperties.getSessionTimeout()).thenReturn(Duration.ofDays(1)); | ||
| 145 | 168 | ||
| 146 | // 调用 | 169 | // 调用 |
| 147 | - String sessionId = userSessionService.createUserSession(loginUser, userIp, userAgent); | 170 | + String token = userSessionService.createUserSession(loginUser, userIp, userAgent); |
| 148 | // 校验 UserSessionDO 记录 | 171 | // 校验 UserSessionDO 记录 |
| 149 | - UserSessionDO userSessionDO = userSessionMapper.selectById(sessionId); | 172 | + UserSessionDO userSessionDO = userSessionMapper.selectOne(UserSessionDO::getToken, token); |
| 150 | assertPojoEquals(loginUser, userSessionDO, "id", "updateTime"); | 173 | assertPojoEquals(loginUser, userSessionDO, "id", "updateTime"); |
| 151 | - assertEquals(sessionId, userSessionDO.getId()); | 174 | + assertEquals(token, userSessionDO.getToken()); |
| 152 | assertEquals(userIp, userSessionDO.getUserIp()); | 175 | assertEquals(userIp, userSessionDO.getUserIp()); |
| 153 | assertEquals(userAgent, userSessionDO.getUserAgent()); | 176 | assertEquals(userAgent, userSessionDO.getUserAgent()); |
| 154 | // 校验 LoginUser 缓存 | 177 | // 校验 LoginUser 缓存 |
| 155 | - LoginUser redisLoginUser = loginUserRedisDAO.get(sessionId); | 178 | + LoginUser redisLoginUser = loginUserRedisDAO.get(token); |
| 156 | assertPojoEquals(loginUser, redisLoginUser, "username", "password"); | 179 | assertPojoEquals(loginUser, redisLoginUser, "username", "password"); |
| 157 | } | 180 | } |
| 158 | 181 | ||
| 159 | @Test | 182 | @Test |
| 160 | - public void testCreateRefreshUserSession_success() { | 183 | + public void testCreateRefreshUserSession() { |
| 161 | // 准备参数 | 184 | // 准备参数 |
| 162 | - String sessionId = randomString(); | ||
| 163 | - String userIp = randomString(); | ||
| 164 | - String userAgent = randomString(); | ||
| 165 | - long timeLong = randomLongId(); | ||
| 166 | - String userName = randomString(); | ||
| 167 | - Date date = randomDate(); | 185 | + String token = randomString(); |
| 186 | + | ||
| 187 | + // mock redis 数据 | ||
| 168 | LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setUserType(randomEle(UserTypeEnum.values()).getValue())); | 188 | LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setUserType(randomEle(UserTypeEnum.values()).getValue())); |
| 169 | - // mock 方法 | ||
| 170 | - when(securityProperties.getSessionTimeout()).thenReturn(Duration.ofDays(1)); | ||
| 171 | - // mock 数据 | ||
| 172 | - loginUser.setUpdateTime(date); | ||
| 173 | - loginUserRedisDAO.set(sessionId, loginUser); | ||
| 174 | - UserSessionDO userSession = UserSessionDO.builder().id(sessionId) | ||
| 175 | - .userId(loginUser.getId()).userType(loginUser.getUserType()) | ||
| 176 | - .userIp(userIp).userAgent(userAgent).username(userName) | ||
| 177 | - .sessionTimeout(addTime(Duration.ofMillis(timeLong))) | ||
| 178 | - .build(); | 189 | + loginUserRedisDAO.set(token, loginUser); |
| 190 | + // mock db 数据 | ||
| 191 | + UserSessionDO userSession = randomPojo(UserSessionDO.class, o -> { | ||
| 192 | + o.setUserType(randomEle(UserTypeEnum.values()).getValue()); | ||
| 193 | + o.setToken(token); | ||
| 194 | + }); | ||
| 179 | userSessionMapper.insert(userSession); | 195 | userSessionMapper.insert(userSession); |
| 180 | 196 | ||
| 181 | // 调用 | 197 | // 调用 |
| 182 | - userSessionService.refreshUserSession(sessionId, loginUser); | 198 | + userSessionService.refreshUserSession(token, loginUser); |
| 183 | // 校验 LoginUser 缓存 | 199 | // 校验 LoginUser 缓存 |
| 184 | - LoginUser redisLoginUser = loginUserRedisDAO.get(sessionId); | ||
| 185 | - assertNotEquals(redisLoginUser.getUpdateTime(), date); | 200 | + LoginUser redisLoginUser = loginUserRedisDAO.get(token); |
| 201 | + assertPojoEquals(redisLoginUser, loginUser, "username", "password"); | ||
| 186 | // 校验 UserSessionDO 记录 | 202 | // 校验 UserSessionDO 记录 |
| 187 | - UserSessionDO updateDO = userSessionMapper.selectById(sessionId); | 203 | + UserSessionDO updateDO = userSessionMapper.selectOne(UserSessionDO::getToken, token); |
| 188 | assertEquals(updateDO.getUsername(), loginUser.getUsername()); | 204 | assertEquals(updateDO.getUsername(), loginUser.getUsername()); |
| 189 | - assertNotEquals(updateDO.getUpdateTime(), userSession.getUpdateTime()); | ||
| 190 | - assertNotEquals(updateDO.getSessionTimeout(), addTime(Duration.ofMillis(timeLong))); | 205 | + assertNotNull(userSession.getUpdateTime()); |
| 206 | + assertNotNull(userSession.getSessionTimeout()); | ||
| 191 | } | 207 | } |
| 192 | 208 | ||
| 193 | @Test | 209 | @Test |
| 194 | - public void testDeleteUserSession_success() { | 210 | + public void testDeleteUserSession_Token() { |
| 195 | // 准备参数 | 211 | // 准备参数 |
| 196 | - String sessionId = randomString(); | ||
| 197 | - String userIp = randomString(); | ||
| 198 | - String userAgent = randomString(); | ||
| 199 | - Long timeLong = randomLongId(); | ||
| 200 | - LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setUserType(randomEle(UserTypeEnum.values()).getValue())); | ||
| 201 | - // mock 存入 Redis | ||
| 202 | - when(securityProperties.getSessionTimeout()).thenReturn(Duration.ofDays(1)); | ||
| 203 | - // mock 数据 | ||
| 204 | - loginUserRedisDAO.set(sessionId, loginUser); | ||
| 205 | - UserSessionDO userSession = UserSessionDO.builder().id(sessionId) | ||
| 206 | - .userId(loginUser.getId()).userType(loginUser.getUserType()) | ||
| 207 | - .userIp(userIp).userAgent(userAgent).username(loginUser.getUsername()) | ||
| 208 | - .sessionTimeout(addTime(Duration.ofMillis(timeLong))) | ||
| 209 | - .build(); | 212 | + String token = randomString(); |
| 213 | + | ||
| 214 | + // mock redis 数据 | ||
| 215 | + loginUserRedisDAO.set(token, new LoginUser()); | ||
| 216 | + // mock db 数据 | ||
| 217 | + UserSessionDO userSession = randomPojo(UserSessionDO.class, o -> { | ||
| 218 | + o.setUserType(randomEle(UserTypeEnum.values()).getValue()); | ||
| 219 | + o.setToken(token); | ||
| 220 | + }); | ||
| 210 | userSessionMapper.insert(userSession); | 221 | userSessionMapper.insert(userSession); |
| 211 | 222 | ||
| 212 | // 调用 | 223 | // 调用 |
| 213 | - userSessionService.deleteUserSession(sessionId); | 224 | + userSessionService.deleteUserSession(token); |
| 225 | + // 校验数据不存在了 | ||
| 226 | + assertNull(loginUserRedisDAO.get(token)); | ||
| 227 | + assertNull(userSessionMapper.selectOne(UserSessionDO::getToken, token)); | ||
| 228 | + } | ||
| 229 | + | ||
| 230 | + @Test | ||
| 231 | + public void testDeleteUserSession_Id() { | ||
| 232 | + // mock db 数据 | ||
| 233 | + UserSessionDO userSession = randomPojo(UserSessionDO.class, o -> { | ||
| 234 | + o.setUserType(randomEle(UserTypeEnum.values()).getValue()); | ||
| 235 | + }); | ||
| 236 | + userSessionMapper.insert(userSession); | ||
| 237 | + // mock redis 数据 | ||
| 238 | + loginUserRedisDAO.set(userSession.getToken(), new LoginUser()); | ||
| 239 | + | ||
| 240 | + // 准备参数 | ||
| 241 | + Long id = userSession.getId(); | ||
| 242 | + | ||
| 243 | + // 调用 | ||
| 244 | + userSessionService.deleteUserSession(id); | ||
| 214 | // 校验数据不存在了 | 245 | // 校验数据不存在了 |
| 215 | - assertNull(loginUserRedisDAO.get(sessionId)); | ||
| 216 | - assertNull(userSessionMapper.selectById(sessionId)); | 246 | + assertNull(loginUserRedisDAO.get(userSession.getToken())); |
| 247 | + assertNull(userSessionMapper.selectById(id)); | ||
| 217 | } | 248 | } |
| 218 | 249 | ||
| 219 | } | 250 | } |
yudao-module-system/yudao-module-system-biz/src/test/resources/sql/create_tables.sql
| @@ -115,7 +115,8 @@ CREATE TABLE IF NOT EXISTS "system_dict_type" ( | @@ -115,7 +115,8 @@ CREATE TABLE IF NOT EXISTS "system_dict_type" ( | ||
| 115 | ) COMMENT '字典类型表'; | 115 | ) COMMENT '字典类型表'; |
| 116 | 116 | ||
| 117 | CREATE TABLE IF NOT EXISTS `system_user_session` ( | 117 | CREATE TABLE IF NOT EXISTS `system_user_session` ( |
| 118 | - `id` varchar(32) NOT NULL, | 118 | + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, |
| 119 | + `token` varchar(32) NOT NULL, | ||
| 119 | `user_id` bigint DEFAULT NULL, | 120 | `user_id` bigint DEFAULT NULL, |
| 120 | "user_type" tinyint NOT NULL, | 121 | "user_type" tinyint NOT NULL, |
| 121 | `username` varchar(50) NOT NULL DEFAULT '', | 122 | `username` varchar(50) NOT NULL DEFAULT '', |
yudao-server/pom.xml
| @@ -21,7 +21,6 @@ | @@ -21,7 +21,6 @@ | ||
| 21 | <url>https://github.com/YunaiV/ruoyi-vue-pro</url> | 21 | <url>https://github.com/YunaiV/ruoyi-vue-pro</url> |
| 22 | 22 | ||
| 23 | <dependencies> | 23 | <dependencies> |
| 24 | - <!-- TODO 芋艿:多模块 --> | ||
| 25 | <dependency> | 24 | <dependency> |
| 26 | <groupId>cn.iocoder.boot</groupId> | 25 | <groupId>cn.iocoder.boot</groupId> |
| 27 | <artifactId>yudao-module-member-biz</artifactId> | 26 | <artifactId>yudao-module-member-biz</artifactId> |
| @@ -43,11 +42,11 @@ | @@ -43,11 +42,11 @@ | ||
| 43 | <version>${revision}</version> | 42 | <version>${revision}</version> |
| 44 | </dependency> | 43 | </dependency> |
| 45 | <!-- 默认引入 yudao-module-bpm-biz-flowable 实现,可以替换为 yudao-module-bpm-biz-activiti 实现--> | 44 | <!-- 默认引入 yudao-module-bpm-biz-flowable 实现,可以替换为 yudao-module-bpm-biz-activiti 实现--> |
| 46 | - <dependency> | ||
| 47 | - <groupId>cn.iocoder.boot</groupId> | ||
| 48 | - <artifactId>yudao-module-bpm-biz-flowable</artifactId> | ||
| 49 | - <version>${revision}</version> | ||
| 50 | - </dependency> | 45 | +<!-- <dependency>--> |
| 46 | +<!-- <groupId>cn.iocoder.boot</groupId>--> | ||
| 47 | +<!-- <artifactId>yudao-module-bpm-biz-flowable</artifactId>--> | ||
| 48 | +<!-- <version>${revision}</version>--> | ||
| 49 | +<!-- </dependency>--> | ||
| 51 | <!-- <dependency>--> | 50 | <!-- <dependency>--> |
| 52 | <!-- <groupId>cn.iocoder.boot</groupId>--> | 51 | <!-- <groupId>cn.iocoder.boot</groupId>--> |
| 53 | <!-- <artifactId>yudao-module-bpm-biz-activiti</artifactId>--> | 52 | <!-- <artifactId>yudao-module-bpm-biz-activiti</artifactId>--> |
yudao-server/src/main/resources/application-local.yaml
| @@ -49,6 +49,9 @@ spring: | @@ -49,6 +49,9 @@ spring: | ||
| 49 | # url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例 | 49 | # url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例 |
| 50 | username: root | 50 | username: root |
| 51 | password: 123456 | 51 | password: 123456 |
| 52 | +# url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=${spring.datasource.dynamic.datasource.master.name} # SQLServer 连接的示例 | ||
| 53 | +# username: sa | ||
| 54 | +# password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W | ||
| 52 | slave: # 模拟从库,可根据自己需要修改 | 55 | slave: # 模拟从库,可根据自己需要修改 |
| 53 | name: ruoyi-vue-pro | 56 | name: ruoyi-vue-pro |
| 54 | url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.slave.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT # MySQL 连接的示例 | 57 | url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.slave.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT # MySQL 连接的示例 |
| @@ -56,6 +59,9 @@ spring: | @@ -56,6 +59,9 @@ spring: | ||
| 56 | # url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例 | 59 | # url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例 |
| 57 | username: root | 60 | username: root |
| 58 | password: 123456 | 61 | password: 123456 |
| 62 | +# url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=${spring.datasource.dynamic.datasource.slave.name} # SQLServer 连接的示例 | ||
| 63 | +# username: sa | ||
| 64 | +# password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W | ||
| 59 | 65 | ||
| 60 | # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 | 66 | # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 |
| 61 | redis: | 67 | redis: |