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 | 46 | <groupId>org.postgresql</groupId> |
| 47 | 47 | <artifactId>postgresql</artifactId> |
| 48 | 48 | </dependency> |
| 49 | + <dependency> | |
| 50 | + <groupId>com.microsoft.sqlserver</groupId> | |
| 51 | + <artifactId>mssql-jdbc</artifactId> | |
| 52 | + </dependency> | |
| 49 | 53 | |
| 50 | 54 | <dependency> |
| 51 | 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 | 3 | import cn.iocoder.yudao.framework.security.core.aop.PreAuthenticatedAspect; |
| 4 | 4 | import cn.iocoder.yudao.framework.security.core.authentication.MultiUserDetailsAuthenticationProvider; |
| 5 | 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 | 7 | import cn.iocoder.yudao.framework.security.core.handler.AccessDeniedHandlerImpl; |
| 8 | 8 | import cn.iocoder.yudao.framework.security.core.handler.AuthenticationEntryPointImpl; |
| 9 | 9 | import cn.iocoder.yudao.framework.security.core.handler.LogoutSuccessHandlerImpl; |
| ... | ... | @@ -86,9 +86,9 @@ public class YudaoSecurityAutoConfiguration { |
| 86 | 86 | * Token 认证过滤器 Bean |
| 87 | 87 | */ |
| 88 | 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 | 2 | |
| 3 | 3 | import cn.hutool.core.util.StrUtil; |
| 4 | 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 | 6 | import cn.iocoder.yudao.framework.web.config.WebProperties; |
| 7 | 7 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; |
| 8 | 8 | import org.springframework.context.annotation.Bean; |
| ... | ... | @@ -55,7 +55,7 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap |
| 55 | 55 | * Token 认证过滤器 Bean |
| 56 | 56 | */ |
| 57 | 57 | @Resource |
| 58 | - private JWTAuthenticationTokenFilter authenticationTokenFilter; | |
| 58 | + private TokenAuthenticationFilter authenticationTokenFilter; | |
| 59 | 59 | |
| 60 | 60 | /** |
| 61 | 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 | 18 | import java.io.IOException; |
| 19 | 19 | |
| 20 | 20 | /** |
| 21 | - * JWT 过滤器,验证 token 的有效性 | |
| 21 | + * Token 过滤器,验证 token 的有效性 | |
| 22 | 22 | * 验证通过后,获得 {@link LoginUser} 信息,并加入到 Spring Security 上下文 |
| 23 | 23 | * |
| 24 | 24 | * @author 芋道源码 |
| 25 | 25 | */ |
| 26 | 26 | @RequiredArgsConstructor |
| 27 | -public class JWTAuthenticationTokenFilter extends OncePerRequestFilter { | |
| 27 | +public class TokenAuthenticationFilter extends OncePerRequestFilter { | |
| 28 | 28 | |
| 29 | 29 | private final SecurityProperties securityProperties; |
| 30 | 30 | |
| ... | ... | @@ -43,7 +43,7 @@ public class JWTAuthenticationTokenFilter extends OncePerRequestFilter { |
| 43 | 43 | LoginUser loginUser = authenticationProvider.verifyTokenAndRefresh(request, token); |
| 44 | 44 | // 模拟 Login 功能,方便日常开发调试 |
| 45 | 45 | if (loginUser == null) { |
| 46 | - loginUser = this.mockLoginUser(request, token); | |
| 46 | + loginUser = mockLoginUser(request, token); | |
| 47 | 47 | } |
| 48 | 48 | // 设置当前用户 |
| 49 | 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 | 27 | if (StrUtil.startWithAny(tableInfo.getName().toLowerCase(), "act_", "flw_", "qrtz_")) { |
| 28 | 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 | 33 | System.out.println(tableInfos.size()); |
| 33 | 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 | import cn.iocoder.yudao.framework.security.core.LoginUser; |
| 9 | 9 | import cn.iocoder.yudao.framework.security.core.authentication.MultiUsernamePasswordAuthenticationToken; |
| 10 | 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 | 11 | import cn.iocoder.yudao.module.member.convert.auth.AuthConvert; |
| 14 | 12 | import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; |
| 15 | 13 | import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper; |
| ... | ... | @@ -88,7 +86,7 @@ public class MemberAuthServiceImpl implements MemberAuthService { |
| 88 | 86 | // 使用手机 + 密码,进行登录。 |
| 89 | 87 | LoginUser loginUser = this.login0(reqVO.getMobile(), reqVO.getPassword()); |
| 90 | 88 | |
| 91 | - // 缓存登录用户到 Redis 中,返回 sessionId 编号 | |
| 89 | + // 缓存登录用户到 Redis 中,返回 Token 令牌 | |
| 92 | 90 | return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_USERNAME, userIp, userAgent); |
| 93 | 91 | } |
| 94 | 92 | |
| ... | ... | @@ -105,7 +103,7 @@ public class MemberAuthServiceImpl implements MemberAuthService { |
| 105 | 103 | // 执行登陆 |
| 106 | 104 | LoginUser loginUser = AuthConvert.INSTANCE.convert(user); |
| 107 | 105 | |
| 108 | - // 缓存登录用户到 Redis 中,返回 sessionId 编号 | |
| 106 | + // 缓存登录用户到 Redis 中,返回 Token 令牌 | |
| 109 | 107 | return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_SMS, userIp, userAgent); |
| 110 | 108 | } |
| 111 | 109 | |
| ... | ... | @@ -127,7 +125,7 @@ public class MemberAuthServiceImpl implements MemberAuthService { |
| 127 | 125 | // 创建 LoginUser 对象 |
| 128 | 126 | LoginUser loginUser = AuthConvert.INSTANCE.convert(user); |
| 129 | 127 | |
| 130 | - // 缓存登录用户到 Redis 中,返回 sessionId 编号 | |
| 128 | + // 缓存登录用户到 Redis 中,返回 Token 令牌 | |
| 131 | 129 | return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_SOCIAL, userIp, userAgent); |
| 132 | 130 | } |
| 133 | 131 | |
| ... | ... | @@ -136,18 +134,18 @@ public class MemberAuthServiceImpl implements MemberAuthService { |
| 136 | 134 | // 使用手机号、手机验证码登录 |
| 137 | 135 | AppAuthSmsLoginReqVO loginReqVO = AppAuthSmsLoginReqVO.builder() |
| 138 | 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 | 141 | socialUserApi.bindSocialUser(AuthConvert.INSTANCE.convert(loginUser.getId(), getUserType().getValue(), reqVO)); |
| 144 | - return sessionId; | |
| 142 | + return token; | |
| 145 | 143 | } |
| 146 | 144 | |
| 147 | 145 | private String createUserSessionAfterLoginSuccess(LoginUser loginUser, LoginLogTypeEnum logType, String userIp, String userAgent) { |
| 148 | 146 | // 插入登陆日志 |
| 149 | 147 | createLoginLog(loginUser.getUsername(), logType, LoginResultEnum.SUCCESS); |
| 150 | - // 缓存登录用户到 Redis 中,返回 sessionId 编号 | |
| 148 | + // 缓存登录用户到 Redis 中,返回 Token 令牌 | |
| 151 | 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 | 18 | * @param loginUser 登录用户 |
| 19 | 19 | * @param userIp 用户 IP |
| 20 | 20 | * @param userAgent 用户 UA |
| 21 | - * @return Session 编号 | |
| 21 | + * @return Token 令牌 | |
| 22 | 22 | */ |
| 23 | 23 | String createUserSession(@NotNull(message = "登录用户不能为空") LoginUser loginUser, String userIp, String userAgent); |
| 24 | 24 | |
| 25 | 25 | /** |
| 26 | 26 | * 刷新在线用户 Session 的更新时间 |
| 27 | 27 | * |
| 28 | - * @param sessionId Session 编号 | |
| 28 | + * @param token Token 令牌 | |
| 29 | 29 | * @param loginUser 登录用户 |
| 30 | 30 | */ |
| 31 | - void refreshUserSession(@NotEmpty(message = "Session编号不能为空") String sessionId, | |
| 31 | + void refreshUserSession(@NotEmpty(message = "Token 令牌不能为空") String token, | |
| 32 | 32 | @NotNull(message = "登录用户不能为空") LoginUser loginUser); |
| 33 | 33 | |
| 34 | 34 | /** |
| 35 | 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 | 45 | * @return 在线用户 |
| 46 | 46 | */ |
| 47 | - LoginUser getLoginUser(String sessionId); | |
| 47 | + LoginUser getLoginUser(String token); | |
| 48 | 48 | |
| 49 | 49 | /** |
| 50 | 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 | 25 | } |
| 26 | 26 | |
| 27 | 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 | 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 | 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 | 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 | 69 | |
| 70 | 70 | @DeleteMapping("/delete") |
| 71 | 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 | 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 | 75 | userSessionService.deleteUserSession(id); |
| 77 | 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 | 3 | import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; |
| 4 | 4 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| 5 | 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 | 7 | import lombok.Builder; |
| 11 | 8 | import lombok.Data; |
| 12 | 9 | import lombok.EqualsAndHashCode; |
| ... | ... | @@ -22,18 +19,21 @@ import java.util.Date; |
| 22 | 19 | * |
| 23 | 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 | 23 | @Data |
| 28 | 24 | @Builder |
| 29 | 25 | @EqualsAndHashCode(callSuper = true) |
| 30 | 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 | 19 | * |
| 20 | 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 | 23 | @KeySequence("system_user_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 |
| 24 | 24 | @Data |
| 25 | 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 | 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 | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 6 | 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 | 8 | import org.apache.ibatis.annotations.Mapper; |
| 9 | 9 | |
| 10 | 10 | import java.util.Collection; |
| ... | ... | @@ -15,13 +15,23 @@ import java.util.List; |
| 15 | 15 | public interface UserSessionMapper extends BaseMapperX<UserSessionDO> { |
| 16 | 16 | |
| 17 | 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 | 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 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine; |
| 4 | 4 | import cn.iocoder.yudao.framework.security.core.LoginUser; |
| 5 | -import me.zhyd.oauth.model.AuthUser; | |
| 6 | 5 | |
| 7 | 6 | import java.time.Duration; |
| 8 | 7 | |
| ... | ... | @@ -20,7 +19,7 @@ public interface RedisKeyConstants { |
| 20 | 19 | STRING, String.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC); |
| 21 | 20 | |
| 22 | 21 | RedisKeyDefine LOGIN_USER = new RedisKeyDefine("登录用户的缓存", |
| 23 | - "login_user:%s", // 参数为 sessionId | |
| 22 | + "login_user:%s", // 参数为 token 令牌 | |
| 24 | 23 | STRING, LoginUser.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC); |
| 25 | 24 | |
| 26 | 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 | @Resource |
| 25 | 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 | 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 | 39 | stringRedisTemplate.opsForValue().set(redisKey, JsonUtils.toJsonString(loginUser), |
| 35 | 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 | 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 | 24 | @Override |
| 25 | 25 | public String execute(String param) throws Exception { |
| 26 | 26 | // 执行过期 |
| 27 | - Long timeoutCount = userSessionService.clearSessionTimeout(); | |
| 27 | + Long timeoutCount = userSessionService.deleteTimeoutSession(); | |
| 28 | 28 | // 返回结果,记录每次的超时数量 |
| 29 | 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 | 104 | // 使用账号密码,进行登录 |
| 105 | 105 | LoginUser loginUser = login0(reqVO.getUsername(), reqVO.getPassword()); |
| 106 | 106 | |
| 107 | - // 缓存登陆用户到 Redis 中,返回 sessionId 编号 | |
| 107 | + // 缓存登陆用户到 Redis 中,返回 Token 令牌 | |
| 108 | 108 | return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_USERNAME, userIp, userAgent); |
| 109 | 109 | } |
| 110 | 110 | |
| ... | ... | @@ -207,7 +207,7 @@ public class AdminAuthServiceImpl implements AdminAuthService { |
| 207 | 207 | // 创建 LoginUser 对象 |
| 208 | 208 | LoginUser loginUser = buildLoginUser(user); |
| 209 | 209 | |
| 210 | - // 缓存登录用户到 Redis 中,返回 sessionId 编号 | |
| 210 | + // 缓存登录用户到 Redis 中,返回 Token 令牌 | |
| 211 | 211 | return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_SOCIAL, userIp, userAgent); |
| 212 | 212 | } |
| 213 | 213 | |
| ... | ... | @@ -219,14 +219,14 @@ public class AdminAuthServiceImpl implements AdminAuthService { |
| 219 | 219 | // 绑定社交用户 |
| 220 | 220 | socialUserService.bindSocialUser(AuthConvert.INSTANCE.convert(loginUser.getId(), getUserType().getValue(), reqVO)); |
| 221 | 221 | |
| 222 | - // 缓存登录用户到 Redis 中,返回 sessionId 编号 | |
| 222 | + // 缓存登录用户到 Redis 中,返回 Token 令牌 | |
| 223 | 223 | return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_SOCIAL, userIp, userAgent); |
| 224 | 224 | } |
| 225 | 225 | |
| 226 | 226 | private String createUserSessionAfterLoginSuccess(LoginUser loginUser, LoginLogTypeEnum logType, String userIp, String userAgent) { |
| 227 | 227 | // 插入登陆日志 |
| 228 | 228 | createLoginLog(loginUser.getUsername(), logType, LoginResultEnum.SUCCESS); |
| 229 | - // 缓存登录用户到 Redis 中,返回 sessionId 编号 | |
| 229 | + // 缓存登录用户到 Redis 中,返回 Token 令牌 | |
| 230 | 230 | return userSessionService.createUserSession(loginUser, userIp, userAgent); |
| 231 | 231 | } |
| 232 | 232 | |
| ... | ... | @@ -240,7 +240,7 @@ public class AdminAuthServiceImpl implements AdminAuthService { |
| 240 | 240 | // 删除 session |
| 241 | 241 | userSessionService.deleteUserSession(token); |
| 242 | 242 | // 记录登出日志 |
| 243 | - this.createLogoutLog(loginUser.getId(), loginUser.getUsername()); | |
| 243 | + createLogoutLog(loginUser.getId(), loginUser.getUsername()); | |
| 244 | 244 | } |
| 245 | 245 | |
| 246 | 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 | 25 | * |
| 26 | 26 | * @return {@link Long } 移出的超时用户数量 |
| 27 | 27 | **/ |
| 28 | - long clearSessionTimeout(); | |
| 28 | + long deleteTimeoutSession(); | |
| 29 | 29 | |
| 30 | 30 | /** |
| 31 | 31 | * 创建在线用户 Session |
| ... | ... | @@ -33,32 +33,39 @@ public interface UserSessionService { |
| 33 | 33 | * @param loginUser 登录用户 |
| 34 | 34 | * @param userIp 用户 IP |
| 35 | 35 | * @param userAgent 用户 UA |
| 36 | - * @return Session 编号 | |
| 36 | + * @return Token 令牌 | |
| 37 | 37 | */ |
| 38 | 38 | String createUserSession(LoginUser loginUser, String userIp, String userAgent); |
| 39 | 39 | |
| 40 | 40 | /** |
| 41 | 41 | * 刷新在线用户 Session 的更新时间 |
| 42 | 42 | * |
| 43 | - * @param sessionId Session 编号 | |
| 43 | + * @param token 令牌 | |
| 44 | 44 | * @param loginUser 登录用户 |
| 45 | 45 | */ |
| 46 | - void refreshUserSession(String sessionId, LoginUser loginUser); | |
| 46 | + void refreshUserSession(String token, LoginUser loginUser); | |
| 47 | 47 | |
| 48 | 48 | /** |
| 49 | 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 | 66 | * @return 在线用户 |
| 60 | 67 | */ |
| 61 | - LoginUser getLoginUser(String sessionId); | |
| 68 | + LoginUser getLoginUser(String token); | |
| 62 | 69 | |
| 63 | 70 | /** |
| 64 | 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 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | 4 | import cn.hutool.core.util.IdUtil; |
| 5 | 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 | 8 | import cn.iocoder.yudao.framework.security.config.SecurityProperties; |
| 7 | 9 | import cn.iocoder.yudao.framework.security.core.LoginUser; |
| 10 | +import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO; | |
| 8 | 11 | import cn.iocoder.yudao.module.system.controller.admin.auth.vo.session.UserSessionPageReqVO; |
| 9 | 12 | import cn.iocoder.yudao.module.system.dal.dataobject.auth.UserSessionDO; |
| 10 | 13 | import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; |
| 11 | 14 | import cn.iocoder.yudao.module.system.dal.mysql.auth.UserSessionMapper; |
| 15 | +import cn.iocoder.yudao.module.system.dal.redis.auth.LoginUserRedisDAO; | |
| 12 | 16 | import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; |
| 13 | 17 | import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum; |
| 14 | 18 | import cn.iocoder.yudao.module.system.service.logger.LoginLogService; |
| 15 | 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 | 20 | import lombok.extern.slf4j.Slf4j; |
| 22 | 21 | import org.springframework.stereotype.Service; |
| 23 | 22 | |
| 24 | 23 | import javax.annotation.Resource; |
| 25 | 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 | 29 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; |
| 30 | 30 | import static cn.iocoder.yudao.framework.common.util.date.DateUtils.addTime; |
| ... | ... | @@ -65,82 +65,99 @@ public class UserSessionServiceImpl implements UserSessionService { |
| 65 | 65 | return userSessionMapper.selectPage(reqVO, userIds); |
| 66 | 66 | } |
| 67 | 67 | |
| 68 | - // TODO @芋艿:优化下该方法 | |
| 69 | 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 | 104 | @Override |
| 103 | 105 | public String createUserSession(LoginUser loginUser, String userIp, String userAgent) { |
| 104 | 106 | // 生成 Session 编号 |
| 105 | - String sessionId = generateSessionId(); | |
| 107 | + String token = generateToken(); | |
| 106 | 108 | // 写入 Redis 缓存 |
| 107 | 109 | loginUser.setUpdateTime(new Date()); |
| 108 | - loginUserRedisDAO.set(sessionId, loginUser); | |
| 110 | + loginUserRedisDAO.set(token, loginUser); | |
| 109 | 111 | // 写入 DB 中 |
| 110 | - UserSessionDO userSession = UserSessionDO.builder().id(sessionId) | |
| 112 | + UserSessionDO userSession = UserSessionDO.builder().token(token) | |
| 111 | 113 | .userId(loginUser.getId()).userType(loginUser.getUserType()) |
| 112 | 114 | .userIp(userIp).userAgent(userAgent).username(loginUser.getUsername()) |
| 113 | 115 | .sessionTimeout(addTime(Duration.ofMillis(getSessionTimeoutMillis()))) |
| 114 | 116 | .build(); |
| 115 | 117 | userSessionMapper.insert(userSession); |
| 116 | - // 返回 Session 编号 | |
| 117 | - return sessionId; | |
| 118 | + // 返回 Token 令牌 | |
| 119 | + return token; | |
| 118 | 120 | } |
| 119 | 121 | |
| 120 | 122 | @Override |
| 121 | - public void refreshUserSession(String sessionId, LoginUser loginUser) { | |
| 123 | + public void refreshUserSession(String token, LoginUser loginUser) { | |
| 122 | 124 | // 写入 Redis 缓存 |
| 123 | 125 | loginUser.setUpdateTime(new Date()); |
| 124 | - loginUserRedisDAO.set(sessionId, loginUser); | |
| 126 | + loginUserRedisDAO.set(token, loginUser); | |
| 125 | 127 | // 更新 DB 中 |
| 126 | - UserSessionDO updateObj = UserSessionDO.builder().id(sessionId).build(); | |
| 128 | + UserSessionDO updateObj = UserSessionDO.builder().build(); | |
| 127 | 129 | updateObj.setUsername(loginUser.getUsername()); |
| 128 | 130 | updateObj.setUpdateTime(new Date()); |
| 129 | 131 | updateObj.setSessionTimeout(addTime(Duration.ofMillis(getSessionTimeoutMillis()))); |
| 130 | - userSessionMapper.updateById(updateObj); | |
| 132 | + userSessionMapper.updateByToken(token, updateObj); | |
| 131 | 133 | } |
| 132 | 134 | |
| 133 | 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 | 150 | // 删除 Redis 缓存 |
| 136 | - loginUserRedisDAO.delete(sessionId); | |
| 151 | + loginUserRedisDAO.delete(session.getToken()); | |
| 137 | 152 | // 删除 DB 记录 |
| 138 | - userSessionMapper.deleteById(sessionId); | |
| 153 | + userSessionMapper.deleteById(id); | |
| 154 | + // 记录退出日志 | |
| 155 | + createLogoutLog(session, LoginLogTypeEnum.LOGOUT_DELETE); | |
| 139 | 156 | } |
| 140 | 157 | |
| 141 | 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 | 163 | @Override |
| ... | ... | @@ -149,11 +166,11 @@ public class UserSessionServiceImpl implements UserSessionService { |
| 149 | 166 | } |
| 150 | 167 | |
| 151 | 168 | /** |
| 152 | - * 生成 Session 编号,目前采用 UUID 算法 | |
| 169 | + * 生成 Token 令牌,目前采用 UUID 算法 | |
| 153 | 170 | * |
| 154 | 171 | * @return Session 编号 |
| 155 | 172 | */ |
| 156 | - private static String generateSessionId() { | |
| 173 | + private static String generateToken() { | |
| 157 | 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 | 134 | AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class); |
| 135 | 135 | String userIp = randomString(); |
| 136 | 136 | String userAgent = randomString(); |
| 137 | + | |
| 137 | 138 | // 调用, 并断言异常 |
| 138 | 139 | assertServiceException(() -> authService.login(reqVO, userIp, userAgent), AUTH_LOGIN_CAPTCHA_NOT_FOUND); |
| 139 | 140 | // 校验调用参数 |
| ... | ... | @@ -148,10 +149,12 @@ public class AuthServiceImplTest extends BaseDbUnitTest { |
| 148 | 149 | // 准备参数 |
| 149 | 150 | String userIp = randomString(); |
| 150 | 151 | String userAgent = randomString(); |
| 151 | - String code = randomString(); | |
| 152 | 152 | AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class); |
| 153 | + | |
| 153 | 154 | // mock 验证码不正确 |
| 155 | + String code = randomString(); | |
| 154 | 156 | when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(code); |
| 157 | + | |
| 155 | 158 | // 调用, 并断言异常 |
| 156 | 159 | assertServiceException(() -> authService.login(reqVO, userIp, userAgent), AUTH_LOGIN_CAPTCHA_CODE_ERROR); |
| 157 | 160 | // 校验调用参数 |
| ... | ... | @@ -172,6 +175,7 @@ public class AuthServiceImplTest extends BaseDbUnitTest { |
| 172 | 175 | // mock 抛出异常 |
| 173 | 176 | when(authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(reqVO.getUsername(), reqVO.getPassword()))) |
| 174 | 177 | .thenThrow(new BadCredentialsException("测试账号或密码不正确")); |
| 178 | + | |
| 175 | 179 | // 调用, 并断言异常 |
| 176 | 180 | assertServiceException(() -> authService.login(reqVO, userIp, userAgent), AUTH_LOGIN_BAD_CREDENTIALS); |
| 177 | 181 | // 校验调用参数 |
| ... | ... | @@ -188,11 +192,13 @@ public class AuthServiceImplTest extends BaseDbUnitTest { |
| 188 | 192 | String userIp = randomString(); |
| 189 | 193 | String userAgent = randomString(); |
| 190 | 194 | AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class); |
| 195 | + | |
| 191 | 196 | // mock 验证码正确 |
| 192 | 197 | when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(reqVO.getCode()); |
| 193 | 198 | // mock 抛出异常 |
| 194 | 199 | when(authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(reqVO.getUsername(), reqVO.getPassword()))) |
| 195 | 200 | .thenThrow(new DisabledException("测试用户被禁用")); |
| 201 | + | |
| 196 | 202 | // 调用, 并断言异常 |
| 197 | 203 | assertServiceException(() -> authService.login(reqVO, userIp, userAgent), AUTH_LOGIN_USER_DISABLED); |
| 198 | 204 | // 校验调用参数 |
| ... | ... | @@ -214,6 +220,7 @@ public class AuthServiceImplTest extends BaseDbUnitTest { |
| 214 | 220 | // mock 抛出异常 |
| 215 | 221 | when(authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(reqVO.getUsername(), reqVO.getPassword()))) |
| 216 | 222 | .thenThrow(new AuthenticationException("测试未知异常") {}); |
| 223 | + | |
| 217 | 224 | // 调用, 并断言异常 |
| 218 | 225 | assertServiceException(() -> authService.login(reqVO, userIp, userAgent), AUTH_LOGIN_FAIL_UNKNOWN); |
| 219 | 226 | // 校验调用参数 |
| ... | ... | @@ -229,27 +236,29 @@ public class AuthServiceImplTest extends BaseDbUnitTest { |
| 229 | 236 | // 准备参数 |
| 230 | 237 | String userIp = randomString(); |
| 231 | 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 | 244 | Long userId = randomLongId(); |
| 233 | 245 | Set<Long> userRoleIds = randomSet(Long.class); |
| 234 | - String sessionId = randomString(); | |
| 235 | - AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class); | |
| 236 | 246 | LoginUser loginUser = randomPojo(LoginUser.class, o -> { |
| 237 | 247 | o.setId(userId); |
| 238 | 248 | o.setRoleIds(userRoleIds); |
| 239 | 249 | }); |
| 240 | - // mock 验证码正确 | |
| 241 | - when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(reqVO.getCode()); | |
| 242 | - // mock authentication | |
| 243 | 250 | when(authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(reqVO.getUsername(), reqVO.getPassword()))) |
| 244 | 251 | .thenReturn(authentication); |
| 245 | 252 | when(authentication.getPrincipal()).thenReturn(loginUser); |
| 246 | 253 | // mock 获得 User 拥有的角色编号数组 |
| 247 | 254 | when(permissionService.getUserRoleIds(userId, singleton(CommonStatusEnum.ENABLE.getStatus()))).thenReturn(userRoleIds); |
| 248 | 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 | 260 | String login = authService.login(reqVO, userIp, userAgent); |
| 252 | - assertEquals(sessionId, login); | |
| 261 | + assertEquals(token, login); | |
| 253 | 262 | // 校验调用参数 |
| 254 | 263 | verify(captchaService, times(1)).deleteCaptchaCode(reqVO.getUuid()); |
| 255 | 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 | 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 | 8 | import cn.iocoder.yudao.framework.security.config.SecurityProperties; |
| 5 | 9 | import cn.iocoder.yudao.framework.security.core.LoginUser; |
| 10 | +import cn.iocoder.yudao.framework.test.core.ut.BaseDbAndRedisUnitTest; | |
| 6 | 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 | 13 | import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; |
| 8 | 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 | 15 | import cn.iocoder.yudao.module.system.dal.redis.auth.LoginUserRedisDAO; |
| 13 | 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 | 22 | import org.junit.jupiter.api.Test; |
| 20 | 23 | import org.springframework.boot.test.mock.mockito.MockBean; |
| 21 | 24 | import org.springframework.context.annotation.Import; |
| 22 | 25 | |
| 23 | 26 | import javax.annotation.Resource; |
| 24 | 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 | 30 | import static cn.hutool.core.util.RandomUtil.randomEle; |
| 31 | 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 | 33 | import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; |
| 34 | 34 | import static java.util.Collections.singletonList; |
| 35 | 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 | 37 | import static org.mockito.ArgumentMatchers.eq; |
| 38 | +import static org.mockito.Mockito.verify; | |
| 38 | 39 | import static org.mockito.Mockito.when; |
| 39 | 40 | |
| 40 | 41 | /** |
| ... | ... | @@ -61,6 +62,11 @@ public class UserSessionServiceImplTest extends BaseDbAndRedisUnitTest { |
| 61 | 62 | @MockBean |
| 62 | 63 | private SecurityProperties securityProperties; |
| 63 | 64 | |
| 65 | + @BeforeEach | |
| 66 | + public void setUp() { | |
| 67 | + when(securityProperties.getSessionTimeout()).thenReturn(Duration.ofDays(1L)); | |
| 68 | + } | |
| 69 | + | |
| 64 | 70 | @Test |
| 65 | 71 | public void testGetUserSessionPage_success() { |
| 66 | 72 | // mock 数据 |
| ... | ... | @@ -78,15 +84,9 @@ public class UserSessionServiceImplTest extends BaseDbAndRedisUnitTest { |
| 78 | 84 | }); |
| 79 | 85 | userSessionMapper.insert(dbSession); |
| 80 | 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 | 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 | 91 | UserSessionPageReqVO reqVO = new UserSessionPageReqVO(); |
| 92 | 92 | reqVO.setUsername(dbUser.getUsername()); |
| ... | ... | @@ -100,35 +100,60 @@ public class UserSessionServiceImplTest extends BaseDbAndRedisUnitTest { |
| 100 | 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 | 137 | @Test |
| 105 | 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 | 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 | 159 | @Test |
| ... | ... | @@ -140,80 +165,86 @@ public class UserSessionServiceImplTest extends BaseDbAndRedisUnitTest { |
| 140 | 165 | o.setUserType(randomEle(UserTypeEnum.values()).getValue()); |
| 141 | 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 | 171 | // 校验 UserSessionDO 记录 |
| 149 | - UserSessionDO userSessionDO = userSessionMapper.selectById(sessionId); | |
| 172 | + UserSessionDO userSessionDO = userSessionMapper.selectOne(UserSessionDO::getToken, token); | |
| 150 | 173 | assertPojoEquals(loginUser, userSessionDO, "id", "updateTime"); |
| 151 | - assertEquals(sessionId, userSessionDO.getId()); | |
| 174 | + assertEquals(token, userSessionDO.getToken()); | |
| 152 | 175 | assertEquals(userIp, userSessionDO.getUserIp()); |
| 153 | 176 | assertEquals(userAgent, userSessionDO.getUserAgent()); |
| 154 | 177 | // 校验 LoginUser 缓存 |
| 155 | - LoginUser redisLoginUser = loginUserRedisDAO.get(sessionId); | |
| 178 | + LoginUser redisLoginUser = loginUserRedisDAO.get(token); | |
| 156 | 179 | assertPojoEquals(loginUser, redisLoginUser, "username", "password"); |
| 157 | 180 | } |
| 158 | 181 | |
| 159 | 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 | 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 | 195 | userSessionMapper.insert(userSession); |
| 180 | 196 | |
| 181 | 197 | // 调用 |
| 182 | - userSessionService.refreshUserSession(sessionId, loginUser); | |
| 198 | + userSessionService.refreshUserSession(token, loginUser); | |
| 183 | 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 | 202 | // 校验 UserSessionDO 记录 |
| 187 | - UserSessionDO updateDO = userSessionMapper.selectById(sessionId); | |
| 203 | + UserSessionDO updateDO = userSessionMapper.selectOne(UserSessionDO::getToken, token); | |
| 188 | 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 | 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 | 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 | 115 | ) COMMENT '字典类型表'; |
| 116 | 116 | |
| 117 | 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 | 120 | `user_id` bigint DEFAULT NULL, |
| 120 | 121 | "user_type" tinyint NOT NULL, |
| 121 | 122 | `username` varchar(50) NOT NULL DEFAULT '', | ... | ... |
yudao-server/pom.xml
| ... | ... | @@ -21,7 +21,6 @@ |
| 21 | 21 | <url>https://github.com/YunaiV/ruoyi-vue-pro</url> |
| 22 | 22 | |
| 23 | 23 | <dependencies> |
| 24 | - <!-- TODO 芋艿:多模块 --> | |
| 25 | 24 | <dependency> |
| 26 | 25 | <groupId>cn.iocoder.boot</groupId> |
| 27 | 26 | <artifactId>yudao-module-member-biz</artifactId> |
| ... | ... | @@ -43,11 +42,11 @@ |
| 43 | 42 | <version>${revision}</version> |
| 44 | 43 | </dependency> |
| 45 | 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 | 50 | <!-- <dependency>--> |
| 52 | 51 | <!-- <groupId>cn.iocoder.boot</groupId>--> |
| 53 | 52 | <!-- <artifactId>yudao-module-bpm-biz-activiti</artifactId>--> | ... | ... |
yudao-server/src/main/resources/application-local.yaml
| ... | ... | @@ -49,6 +49,9 @@ spring: |
| 49 | 49 | # url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例 |
| 50 | 50 | username: root |
| 51 | 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 | 55 | slave: # 模拟从库,可根据自己需要修改 |
| 53 | 56 | name: ruoyi-vue-pro |
| 54 | 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 | 59 | # url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例 |
| 57 | 60 | username: root |
| 58 | 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 | 66 | # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 |
| 61 | 67 | redis: | ... | ... |