Commit 73bf0b6f4f42423d0c513560b86e44d5f7a18e66
1 parent
8737674d
去除 LoginUser 的 updateTime、username、password、status 字段,简化
Showing
10 changed files
with
182 additions
and
199 deletions
yudao-dependencies/pom.xml
| ... | ... | @@ -173,11 +173,6 @@ |
| 173 | 173 | </dependency> |
| 174 | 174 | |
| 175 | 175 | <dependency> |
| 176 | - <groupId>mysql</groupId> | |
| 177 | - <artifactId>mysql-connector-java</artifactId> | |
| 178 | - <version>${mysql.version}</version> | |
| 179 | - </dependency> | |
| 180 | - <dependency> | |
| 181 | 176 | <groupId>com.alibaba</groupId> |
| 182 | 177 | <artifactId>druid-spring-boot-starter</artifactId> |
| 183 | 178 | <version>${druid.version}</version> | ... | ... |
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/LoginUser.java
| 1 | 1 | package cn.iocoder.yudao.framework.security.core; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.map.MapUtil; |
| 4 | -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | |
| 5 | 4 | import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; |
| 6 | 5 | import com.fasterxml.jackson.annotation.JsonIgnore; |
| 7 | 6 | import lombok.Data; |
| 8 | -import org.springframework.security.core.GrantedAuthority; | |
| 9 | -import org.springframework.security.core.userdetails.UserDetails; | |
| 10 | 7 | |
| 11 | -import java.util.*; | |
| 8 | +import java.util.HashMap; | |
| 9 | +import java.util.Map; | |
| 12 | 10 | |
| 13 | 11 | /** |
| 14 | 12 | * 登录用户信息 |
| ... | ... | @@ -16,7 +14,7 @@ import java.util.*; |
| 16 | 14 | * @author 芋道源码 |
| 17 | 15 | */ |
| 18 | 16 | @Data |
| 19 | -public class LoginUser implements UserDetails { | |
| 17 | +public class LoginUser { | |
| 20 | 18 | |
| 21 | 19 | /** |
| 22 | 20 | * 用户编号 |
| ... | ... | @@ -29,23 +27,6 @@ public class LoginUser implements UserDetails { |
| 29 | 27 | */ |
| 30 | 28 | private Integer userType; |
| 31 | 29 | /** |
| 32 | - * 最后更新时间 | |
| 33 | - */ | |
| 34 | - private Date updateTime; | |
| 35 | - | |
| 36 | - /** | |
| 37 | - * 用户名 | |
| 38 | - */ | |
| 39 | - private String username; | |
| 40 | - /** | |
| 41 | - * 密码 | |
| 42 | - */ | |
| 43 | - private String password; | |
| 44 | - /** | |
| 45 | - * 状态 | |
| 46 | - */ | |
| 47 | - private Integer status; | |
| 48 | - /** | |
| 49 | 30 | * 租户编号 |
| 50 | 31 | */ |
| 51 | 32 | private Long tenantId; |
| ... | ... | @@ -59,49 +40,6 @@ public class LoginUser implements UserDetails { |
| 59 | 40 | @JsonIgnore |
| 60 | 41 | private Map<String, Object> context; |
| 61 | 42 | |
| 62 | - @Override | |
| 63 | - @JsonIgnore// 避免序列化 | |
| 64 | - public String getPassword() { | |
| 65 | - return password; | |
| 66 | - } | |
| 67 | - | |
| 68 | - @Override | |
| 69 | - public String getUsername() { | |
| 70 | - return username; | |
| 71 | - } | |
| 72 | - | |
| 73 | - @Override | |
| 74 | - @JsonIgnore// 避免序列化 | |
| 75 | - public boolean isEnabled() { | |
| 76 | - return CommonStatusEnum.ENABLE.getStatus().equals(status); | |
| 77 | - } | |
| 78 | - | |
| 79 | - @Override | |
| 80 | - @JsonIgnore// 避免序列化 | |
| 81 | - public Collection<? extends GrantedAuthority> getAuthorities() { | |
| 82 | - return new HashSet<>(); | |
| 83 | - } | |
| 84 | - | |
| 85 | - @Override | |
| 86 | - @JsonIgnore// 避免序列化 | |
| 87 | - public boolean isAccountNonExpired() { | |
| 88 | - return true; // 返回 true,不依赖 Spring Security 判断 | |
| 89 | - } | |
| 90 | - | |
| 91 | - @Override | |
| 92 | - @JsonIgnore// 避免序列化 | |
| 93 | - public boolean isAccountNonLocked() { | |
| 94 | - return true; // 返回 true,不依赖 Spring Security 判断 | |
| 95 | - } | |
| 96 | - | |
| 97 | - @Override | |
| 98 | - @JsonIgnore// 避免序列化 | |
| 99 | - public boolean isCredentialsNonExpired() { | |
| 100 | - return true; // 返回 true,不依赖 Spring Security 判断 | |
| 101 | - } | |
| 102 | - | |
| 103 | - // ========== 上下文 ========== | |
| 104 | - | |
| 105 | 43 | public void setContext(String key, Object value) { |
| 106 | 44 | if (context == null) { |
| 107 | 45 | context = new HashMap<>(); | ... | ... |
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/authentication/SpringSecurityUser.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.security.core.authentication; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | |
| 4 | +import lombok.AllArgsConstructor; | |
| 5 | +import lombok.Data; | |
| 6 | +import org.springframework.security.core.GrantedAuthority; | |
| 7 | +import org.springframework.security.core.userdetails.UserDetails; | |
| 8 | + | |
| 9 | +import java.util.Collection; | |
| 10 | +import java.util.Collections; | |
| 11 | + | |
| 12 | +/** | |
| 13 | + * 登录用户信息 | |
| 14 | + * | |
| 15 | + * @author 芋道源码 | |
| 16 | + */ | |
| 17 | +@Data | |
| 18 | +@AllArgsConstructor | |
| 19 | +public class SpringSecurityUser implements UserDetails { | |
| 20 | + | |
| 21 | + /** | |
| 22 | + * 用户编号 | |
| 23 | + */ | |
| 24 | + private Long id; | |
| 25 | + | |
| 26 | + /** | |
| 27 | + * 用户名 | |
| 28 | + */ | |
| 29 | + private String username; | |
| 30 | + /** | |
| 31 | + * 密码 | |
| 32 | + */ | |
| 33 | + private String password; | |
| 34 | + /** | |
| 35 | + * 状态 | |
| 36 | + */ | |
| 37 | + private Integer status; | |
| 38 | + /** | |
| 39 | + * 租户编号 | |
| 40 | + */ | |
| 41 | + private Long tenantId; | |
| 42 | + | |
| 43 | + @Override | |
| 44 | + public String getPassword() { | |
| 45 | + return password; | |
| 46 | + } | |
| 47 | + | |
| 48 | + @Override | |
| 49 | + public String getUsername() { | |
| 50 | + return username; | |
| 51 | + } | |
| 52 | + | |
| 53 | + @Override | |
| 54 | + public boolean isEnabled() { | |
| 55 | + return CommonStatusEnum.ENABLE.getStatus().equals(status); | |
| 56 | + } | |
| 57 | + | |
| 58 | + @Override | |
| 59 | + public Collection<? extends GrantedAuthority> getAuthorities() { | |
| 60 | + return Collections.emptyList(); | |
| 61 | + } | |
| 62 | + | |
| 63 | + @Override | |
| 64 | + public boolean isAccountNonExpired() { | |
| 65 | + return true; // 返回 true,不依赖 Spring Security 判断 | |
| 66 | + } | |
| 67 | + | |
| 68 | + @Override | |
| 69 | + public boolean isAccountNonLocked() { | |
| 70 | + return true; // 返回 true,不依赖 Spring Security 判断 | |
| 71 | + } | |
| 72 | + | |
| 73 | + @Override | |
| 74 | + public boolean isCredentialsNonExpired() { | |
| 75 | + return true; // 返回 true,不依赖 Spring Security 判断 | |
| 76 | + } | |
| 77 | + | |
| 78 | +} | ... | ... |
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/util/SecurityFrameworkUtils.java
| ... | ... | @@ -11,6 +11,7 @@ import org.springframework.security.web.authentication.WebAuthenticationDetailsS |
| 11 | 11 | import org.springframework.util.StringUtils; |
| 12 | 12 | |
| 13 | 13 | import javax.servlet.http.HttpServletRequest; |
| 14 | +import java.util.Collections; | |
| 14 | 15 | |
| 15 | 16 | /** |
| 16 | 17 | * 安全服务工具类 |
| ... | ... | @@ -98,7 +99,7 @@ public class SecurityFrameworkUtils { |
| 98 | 99 | private static Authentication buildAuthentication(LoginUser loginUser, HttpServletRequest request) { |
| 99 | 100 | // 创建 UsernamePasswordAuthenticationToken 对象 |
| 100 | 101 | UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken( |
| 101 | - loginUser, null, loginUser.getAuthorities()); | |
| 102 | + loginUser, null, Collections.emptyList()); | |
| 102 | 103 | authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); |
| 103 | 104 | return authenticationToken; |
| 104 | 105 | } | ... | ... |
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/auth/AuthConvert.java
| 1 | 1 | package cn.iocoder.yudao.module.member.convert.auth; |
| 2 | 2 | |
| 3 | -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | |
| 4 | 3 | import cn.iocoder.yudao.framework.security.core.LoginUser; |
| 4 | +import cn.iocoder.yudao.framework.security.core.authentication.SpringSecurityUser; | |
| 5 | 5 | import cn.iocoder.yudao.module.member.controller.app.auth.vo.*; |
| 6 | 6 | import cn.iocoder.yudao.module.member.controller.app.social.vo.AppSocialUserUnbindReqVO; |
| 7 | 7 | import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; |
| ... | ... | @@ -19,13 +19,12 @@ public interface AuthConvert { |
| 19 | 19 | |
| 20 | 20 | AuthConvert INSTANCE = Mappers.getMapper(AuthConvert.class); |
| 21 | 21 | |
| 22 | + LoginUser convert(MemberUserDO bean); | |
| 23 | + | |
| 22 | 24 | @Mapping(source = "mobile", target = "username") |
| 23 | - LoginUser convert0(MemberUserDO bean); | |
| 25 | + SpringSecurityUser convert2(MemberUserDO user); | |
| 24 | 26 | |
| 25 | - default LoginUser convert(MemberUserDO bean) { | |
| 26 | - // 目的,为了设置 UserTypeEnum.MEMBER.getValue() | |
| 27 | - return convert0(bean).setUserType(UserTypeEnum.MEMBER.getValue()); | |
| 28 | - } | |
| 27 | + LoginUser convert(SpringSecurityUser bean); | |
| 29 | 28 | |
| 30 | 29 | SocialUserBindReqDTO convert(Long userId, Integer userType, AppAuthSocialBindLoginReqVO reqVO); |
| 31 | 30 | SocialUserBindReqDTO convert(Long userId, Integer userType, AppAuthSocialQuickLoginReqVO reqVO); | ... | ... |
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthServiceImpl.java
| 1 | 1 | package cn.iocoder.yudao.module.member.service.auth; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.lang.Assert; |
| 4 | -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | |
| 5 | 4 | import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; |
| 6 | 5 | import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils; |
| 7 | 6 | import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; |
| ... | ... | @@ -78,7 +77,7 @@ public class MemberAuthServiceImpl implements MemberAuthService { |
| 78 | 77 | throw new UsernameNotFoundException(mobile); |
| 79 | 78 | } |
| 80 | 79 | // 创建 LoginUser 对象 |
| 81 | - return AuthConvert.INSTANCE.convert(user); | |
| 80 | + return AuthConvert.INSTANCE.convert2(user); | |
| 82 | 81 | } |
| 83 | 82 | |
| 84 | 83 | @Override |
| ... | ... | @@ -87,7 +86,8 @@ public class MemberAuthServiceImpl implements MemberAuthService { |
| 87 | 86 | LoginUser loginUser = login0(reqVO.getMobile(), reqVO.getPassword()); |
| 88 | 87 | |
| 89 | 88 | // 缓存登录用户到 Redis 中,返回 Token 令牌 |
| 90 | - return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_USERNAME, userIp, userAgent); | |
| 89 | + return createUserSessionAfterLoginSuccess(loginUser, reqVO.getMobile(), | |
| 90 | + LoginLogTypeEnum.LOGIN_USERNAME, userIp, userAgent); | |
| 91 | 91 | } |
| 92 | 92 | |
| 93 | 93 | @Override |
| ... | ... | @@ -101,10 +101,11 @@ public class MemberAuthServiceImpl implements MemberAuthService { |
| 101 | 101 | Assert.notNull(user, "获取用户失败,结果为空"); |
| 102 | 102 | |
| 103 | 103 | // 执行登陆 |
| 104 | - LoginUser loginUser = AuthConvert.INSTANCE.convert(user); | |
| 104 | + LoginUser loginUser = buildLoginUser(user); | |
| 105 | 105 | |
| 106 | 106 | // 缓存登录用户到 Redis 中,返回 Token 令牌 |
| 107 | - return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_SMS, userIp, userAgent); | |
| 107 | + return createUserSessionAfterLoginSuccess(loginUser, reqVO.getMobile(), | |
| 108 | + LoginLogTypeEnum.LOGIN_SMS, userIp, userAgent); | |
| 108 | 109 | } |
| 109 | 110 | |
| 110 | 111 | @Override |
| ... | ... | @@ -123,10 +124,11 @@ public class MemberAuthServiceImpl implements MemberAuthService { |
| 123 | 124 | } |
| 124 | 125 | |
| 125 | 126 | // 创建 LoginUser 对象 |
| 126 | - LoginUser loginUser = AuthConvert.INSTANCE.convert(user); | |
| 127 | + LoginUser loginUser = buildLoginUser(user); | |
| 127 | 128 | |
| 128 | 129 | // 缓存登录用户到 Redis 中,返回 Token 令牌 |
| 129 | - return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_SOCIAL, userIp, userAgent); | |
| 130 | + return createUserSessionAfterLoginSuccess(loginUser, null, | |
| 131 | + LoginLogTypeEnum.LOGIN_SOCIAL, userIp, userAgent); | |
| 130 | 132 | } |
| 131 | 133 | |
| 132 | 134 | @Override |
| ... | ... | @@ -142,9 +144,10 @@ public class MemberAuthServiceImpl implements MemberAuthService { |
| 142 | 144 | return token; |
| 143 | 145 | } |
| 144 | 146 | |
| 145 | - private String createUserSessionAfterLoginSuccess(LoginUser loginUser, LoginLogTypeEnum logType, String userIp, String userAgent) { | |
| 147 | + private String createUserSessionAfterLoginSuccess(LoginUser loginUser, String mobile, | |
| 148 | + LoginLogTypeEnum logType, String userIp, String userAgent) { | |
| 146 | 149 | // 插入登陆日志 |
| 147 | - createLoginLog(loginUser.getUsername(), logType, LoginResultEnum.SUCCESS); | |
| 150 | + createLoginLog(loginUser.getId(), mobile, logType, LoginResultEnum.SUCCESS); | |
| 148 | 151 | // 缓存登录用户到 Redis 中,返回 Token 令牌 |
| 149 | 152 | return userSessionApi.createUserSession(loginUser, userIp, userAgent); |
| 150 | 153 | } |
| ... | ... | @@ -164,29 +167,32 @@ public class MemberAuthServiceImpl implements MemberAuthService { |
| 164 | 167 | authentication = authenticationManager.authenticate(new MultiUsernamePasswordAuthenticationToken( |
| 165 | 168 | username, password, getUserType())); |
| 166 | 169 | } catch (BadCredentialsException badCredentialsException) { |
| 167 | - this.createLoginLog(username, logType, LoginResultEnum.BAD_CREDENTIALS); | |
| 170 | + this.createLoginLog(null, username, logType, LoginResultEnum.BAD_CREDENTIALS); | |
| 168 | 171 | throw exception(AUTH_LOGIN_BAD_CREDENTIALS); |
| 169 | 172 | } catch (DisabledException disabledException) { |
| 170 | - this.createLoginLog(username, logType, LoginResultEnum.USER_DISABLED); | |
| 173 | + this.createLoginLog(null, username, logType, LoginResultEnum.USER_DISABLED); | |
| 171 | 174 | throw exception(AUTH_LOGIN_USER_DISABLED); |
| 172 | 175 | } catch (AuthenticationException authenticationException) { |
| 173 | 176 | log.error("[login0][username({}) 发生未知异常]", username, authenticationException); |
| 174 | - this.createLoginLog(username, logType, LoginResultEnum.UNKNOWN_ERROR); | |
| 177 | + this.createLoginLog(null, username, logType, LoginResultEnum.UNKNOWN_ERROR); | |
| 175 | 178 | throw exception(AUTH_LOGIN_FAIL_UNKNOWN); |
| 176 | 179 | } |
| 177 | 180 | Assert.notNull(authentication.getPrincipal(), "Principal 不会为空"); |
| 178 | 181 | return (LoginUser) authentication.getPrincipal(); |
| 179 | 182 | } |
| 180 | 183 | |
| 181 | - private void createLoginLog(String mobile, LoginLogTypeEnum logType, LoginResultEnum loginResult) { | |
| 184 | + private void createLoginLog(Long userId, String mobile, LoginLogTypeEnum logType, LoginResultEnum loginResult) { | |
| 182 | 185 | // 获得用户 |
| 183 | - MemberUserDO user = userService.getUserByMobile(mobile); | |
| 186 | + if (userId == null) { | |
| 187 | + MemberUserDO user = userService.getUserByMobile(mobile); | |
| 188 | + userId = user != null ? user.getId() : null; | |
| 189 | + } | |
| 184 | 190 | // 插入登录日志 |
| 185 | 191 | LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO(); |
| 186 | 192 | reqDTO.setLogType(logType.getType()); |
| 187 | 193 | reqDTO.setTraceId(TracerUtils.getTraceId()); |
| 188 | - if (user != null) { | |
| 189 | - reqDTO.setUserId(user.getId()); | |
| 194 | + if (userId != null) { | |
| 195 | + reqDTO.setUserId(userId); | |
| 190 | 196 | } |
| 191 | 197 | reqDTO.setUserType(getUserType().getValue()); |
| 192 | 198 | reqDTO.setUsername(mobile); |
| ... | ... | @@ -195,39 +201,14 @@ public class MemberAuthServiceImpl implements MemberAuthService { |
| 195 | 201 | reqDTO.setResult(loginResult.getResult()); |
| 196 | 202 | loginLogApi.createLoginLog(reqDTO); |
| 197 | 203 | // 更新最后登录时间 |
| 198 | - if (user != null && Objects.equals(LoginResultEnum.SUCCESS.getResult(), loginResult.getResult())) { | |
| 199 | - userService.updateUserLogin(user.getId(), getClientIP()); | |
| 204 | + if (userId != null && Objects.equals(LoginResultEnum.SUCCESS.getResult(), loginResult.getResult())) { | |
| 205 | + userService.updateUserLogin(userId, getClientIP()); | |
| 200 | 206 | } |
| 201 | 207 | } |
| 202 | 208 | |
| 203 | 209 | @Override |
| 204 | 210 | public LoginUser verifyTokenAndRefresh(String token) { |
| 205 | - // 获得 LoginUser | |
| 206 | - LoginUser loginUser = userSessionApi.getLoginUser(token); | |
| 207 | - if (loginUser == null) { | |
| 208 | - return null; | |
| 209 | - } | |
| 210 | - // 刷新 LoginUser 缓存 | |
| 211 | - this.refreshLoginUserCache(token, loginUser); | |
| 212 | - return loginUser; | |
| 213 | - } | |
| 214 | - | |
| 215 | - private void refreshLoginUserCache(String token, LoginUser loginUser) { | |
| 216 | - // 每 1/3 的 Session 超时时间,刷新 LoginUser 缓存 | |
| 217 | - if (System.currentTimeMillis() - loginUser.getUpdateTime().getTime() < | |
| 218 | - userSessionApi.getSessionTimeoutMillis() / 3) { | |
| 219 | - return; | |
| 220 | - } | |
| 221 | - | |
| 222 | - // 重新加载 UserDO 信息 | |
| 223 | - MemberUserDO user = userService.getUser(loginUser.getId()); | |
| 224 | - if (user == null || CommonStatusEnum.DISABLE.getStatus().equals(user.getStatus())) { | |
| 225 | - // 校验 token 时,用户被禁用的情况下,也认为 token 过期,方便前端跳转到登录界面 | |
| 226 | - throw exception(AUTH_TOKEN_EXPIRED); | |
| 227 | - } | |
| 228 | - | |
| 229 | - // 刷新 LoginUser 缓存 | |
| 230 | - userSessionApi.refreshUserSession(token, loginUser); | |
| 211 | + return userSessionApi.getLoginUser(token); | |
| 231 | 212 | } |
| 232 | 213 | |
| 233 | 214 | @Override |
| ... | ... | @@ -239,10 +220,10 @@ public class MemberAuthServiceImpl implements MemberAuthService { |
| 239 | 220 | } |
| 240 | 221 | |
| 241 | 222 | // 执行登陆 |
| 242 | - this.createLoginLog(user.getMobile(), LoginLogTypeEnum.LOGIN_MOCK, LoginResultEnum.SUCCESS); | |
| 223 | + createLoginLog(userId, user.getMobile(), LoginLogTypeEnum.LOGIN_MOCK, LoginResultEnum.SUCCESS); | |
| 243 | 224 | |
| 244 | 225 | // 创建 LoginUser 对象 |
| 245 | - return AuthConvert.INSTANCE.convert(user); | |
| 226 | + return buildLoginUser(user); | |
| 246 | 227 | } |
| 247 | 228 | |
| 248 | 229 | @Override |
| ... | ... | @@ -255,7 +236,7 @@ public class MemberAuthServiceImpl implements MemberAuthService { |
| 255 | 236 | // 删除 session |
| 256 | 237 | userSessionApi.deleteUserSession(token); |
| 257 | 238 | // 记录登出日志 |
| 258 | - this.createLogoutLog(loginUser.getId(), loginUser.getUsername()); | |
| 239 | + createLogoutLog(loginUser.getId()); | |
| 259 | 240 | } |
| 260 | 241 | |
| 261 | 242 | @Override |
| ... | ... | @@ -321,17 +302,29 @@ public class MemberAuthServiceImpl implements MemberAuthService { |
| 321 | 302 | return user; |
| 322 | 303 | } |
| 323 | 304 | |
| 324 | - private void createLogoutLog(Long userId, String username) { | |
| 305 | + private void createLogoutLog(Long userId) { | |
| 325 | 306 | LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO(); |
| 326 | 307 | reqDTO.setLogType(LoginLogTypeEnum.LOGOUT_SELF.getType()); |
| 327 | 308 | reqDTO.setTraceId(TracerUtils.getTraceId()); |
| 328 | 309 | reqDTO.setUserId(userId); |
| 329 | 310 | reqDTO.setUserType(getUserType().getValue()); |
| 330 | - reqDTO.setUsername(username); | |
| 311 | + reqDTO.setUsername(getMobile(userId)); | |
| 331 | 312 | reqDTO.setUserAgent(ServletUtils.getUserAgent()); |
| 332 | 313 | reqDTO.setUserIp(getClientIP()); |
| 333 | 314 | reqDTO.setResult(LoginResultEnum.SUCCESS.getResult()); |
| 334 | 315 | loginLogApi.createLoginLog(reqDTO); |
| 335 | 316 | } |
| 336 | 317 | |
| 318 | + private LoginUser buildLoginUser(MemberUserDO user) { | |
| 319 | + return AuthConvert.INSTANCE.convert(user).setUserType(getUserType().getValue()); | |
| 320 | + } | |
| 321 | + | |
| 322 | + private String getMobile(Long userId) { | |
| 323 | + if (userId == null) { | |
| 324 | + return null; | |
| 325 | + } | |
| 326 | + MemberUserDO user = userService.getUser(userId); | |
| 327 | + return user != null ? user.getMobile() : null; | |
| 328 | + } | |
| 329 | + | |
| 337 | 330 | } | ... | ... |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java
| 1 | 1 | package cn.iocoder.yudao.module.system.convert.auth; |
| 2 | 2 | |
| 3 | -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | |
| 4 | 3 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| 5 | 4 | import cn.iocoder.yudao.framework.security.core.LoginUser; |
| 6 | -import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO; | |
| 5 | +import cn.iocoder.yudao.framework.security.core.authentication.SpringSecurityUser; | |
| 7 | 6 | import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO; |
| 8 | 7 | import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; |
| 9 | 8 | import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; |
| 10 | -import cn.iocoder.yudao.module.system.api.social.dto.SocialUserUnbindReqDTO; | |
| 11 | 9 | import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.*; |
| 12 | 10 | import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; |
| 13 | 11 | import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; |
| 14 | 12 | import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; |
| 15 | 13 | import cn.iocoder.yudao.module.system.enums.permission.MenuIdEnum; |
| 16 | 14 | import org.mapstruct.Mapper; |
| 17 | -import org.mapstruct.Mapping; | |
| 18 | 15 | import org.mapstruct.factory.Mappers; |
| 19 | 16 | import org.slf4j.LoggerFactory; |
| 20 | 17 | |
| ... | ... | @@ -25,13 +22,11 @@ public interface AuthConvert { |
| 25 | 22 | |
| 26 | 23 | AuthConvert INSTANCE = Mappers.getMapper(AuthConvert.class); |
| 27 | 24 | |
| 28 | - @Mapping(source = "updateTime", target = "updateTime", ignore = true) // 字段相同,但是含义不同,忽略 | |
| 29 | - LoginUser convert0(AdminUserDO bean); | |
| 25 | + LoginUser convert(AdminUserDO bean); | |
| 30 | 26 | |
| 31 | - default LoginUser convert(AdminUserDO bean) { | |
| 32 | - // 目的,为了设置 UserTypeEnum.ADMIN.getValue() | |
| 33 | - return convert0(bean).setUserType(UserTypeEnum.ADMIN.getValue()); | |
| 34 | - } | |
| 27 | + SpringSecurityUser convert2(AdminUserDO user); | |
| 28 | + | |
| 29 | + LoginUser convert(SpringSecurityUser bean); | |
| 35 | 30 | |
| 36 | 31 | default AuthPermissionInfoRespVO convert(AdminUserDO user, List<RoleDO> roleList, List<MenuDO> menuList) { |
| 37 | 32 | return AuthPermissionInfoRespVO.builder() | ... | ... |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java
| 1 | 1 | package cn.iocoder.yudao.module.system.service.auth; |
| 2 | 2 | |
| 3 | -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | |
| 4 | 3 | import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; |
| 5 | 4 | import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils; |
| 6 | 5 | import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; |
| 7 | 6 | import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; |
| 8 | 7 | import cn.iocoder.yudao.framework.security.core.LoginUser; |
| 9 | 8 | import cn.iocoder.yudao.framework.security.core.authentication.MultiUsernamePasswordAuthenticationToken; |
| 9 | +import cn.iocoder.yudao.framework.security.core.authentication.SpringSecurityUser; | |
| 10 | 10 | import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO; |
| 11 | 11 | import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; |
| 12 | 12 | import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.*; |
| ... | ... | @@ -79,7 +79,7 @@ public class AdminAuthServiceImpl implements AdminAuthService { |
| 79 | 79 | throw new UsernameNotFoundException(username); |
| 80 | 80 | } |
| 81 | 81 | // 创建 LoginUser 对象 |
| 82 | - return buildLoginUser(user); | |
| 82 | + return AuthConvert.INSTANCE.convert2(user); | |
| 83 | 83 | } |
| 84 | 84 | |
| 85 | 85 | @Override |
| ... | ... | @@ -89,7 +89,7 @@ public class AdminAuthServiceImpl implements AdminAuthService { |
| 89 | 89 | if (user == null) { |
| 90 | 90 | throw new UsernameNotFoundException(String.valueOf(userId)); |
| 91 | 91 | } |
| 92 | - createLoginLog(user.getUsername(), LoginLogTypeEnum.LOGIN_MOCK, LoginResultEnum.SUCCESS); | |
| 92 | + createLoginLog(userId, user.getUsername(), LoginLogTypeEnum.LOGIN_MOCK, LoginResultEnum.SUCCESS); | |
| 93 | 93 | |
| 94 | 94 | // 创建 LoginUser 对象 |
| 95 | 95 | return buildLoginUser(user); |
| ... | ... | @@ -104,7 +104,8 @@ public class AdminAuthServiceImpl implements AdminAuthService { |
| 104 | 104 | LoginUser loginUser = login0(reqVO.getUsername(), reqVO.getPassword()); |
| 105 | 105 | |
| 106 | 106 | // 缓存登陆用户到 Redis 中,返回 Token 令牌 |
| 107 | - return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_USERNAME, userIp, userAgent); | |
| 107 | + return createUserSessionAfterLoginSuccess(loginUser, reqVO.getUsername(), | |
| 108 | + LoginLogTypeEnum.LOGIN_USERNAME, userIp, userAgent); | |
| 108 | 109 | } |
| 109 | 110 | |
| 110 | 111 | @Override |
| ... | ... | @@ -132,7 +133,8 @@ public class AdminAuthServiceImpl implements AdminAuthService { |
| 132 | 133 | LoginUser loginUser = buildLoginUser(user); |
| 133 | 134 | |
| 134 | 135 | // 缓存登陆用户到 Redis 中,返回 sessionId 编号 |
| 135 | - return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_MOBILE, userIp, userAgent); | |
| 136 | + return createUserSessionAfterLoginSuccess(loginUser, reqVO.getMobile(), | |
| 137 | + LoginLogTypeEnum.LOGIN_MOBILE, userIp, userAgent); | |
| 136 | 138 | } |
| 137 | 139 | |
| 138 | 140 | private void verifyCaptcha(AuthLoginReqVO reqVO) { |
| ... | ... | @@ -147,13 +149,13 @@ public class AdminAuthServiceImpl implements AdminAuthService { |
| 147 | 149 | String code = captchaService.getCaptchaCode(reqVO.getUuid()); |
| 148 | 150 | if (code == null) { |
| 149 | 151 | // 创建登录失败日志(验证码不存在) |
| 150 | - this.createLoginLog(reqVO.getUsername(), logTypeEnum, LoginResultEnum.CAPTCHA_NOT_FOUND); | |
| 152 | + createLoginLog(null, reqVO.getUsername(), logTypeEnum, LoginResultEnum.CAPTCHA_NOT_FOUND); | |
| 151 | 153 | throw exception(AUTH_LOGIN_CAPTCHA_NOT_FOUND); |
| 152 | 154 | } |
| 153 | 155 | // 验证码不正确 |
| 154 | 156 | if (!code.equals(reqVO.getCode())) { |
| 155 | 157 | // 创建登录失败日志(验证码不正确) |
| 156 | - this.createLoginLog(reqVO.getUsername(), logTypeEnum, LoginResultEnum.CAPTCHA_CODE_ERROR); | |
| 158 | + createLoginLog(null, reqVO.getUsername(), logTypeEnum, LoginResultEnum.CAPTCHA_CODE_ERROR); | |
| 157 | 159 | throw exception(AUTH_LOGIN_CAPTCHA_CODE_ERROR); |
| 158 | 160 | } |
| 159 | 161 | // 正确,所以要删除下验证码 |
| ... | ... | @@ -170,29 +172,35 @@ public class AdminAuthServiceImpl implements AdminAuthService { |
| 170 | 172 | authentication = authenticationManager.authenticate(new MultiUsernamePasswordAuthenticationToken( |
| 171 | 173 | username, password, getUserType())); |
| 172 | 174 | } catch (BadCredentialsException badCredentialsException) { |
| 173 | - this.createLoginLog(username, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS); | |
| 175 | + createLoginLog(null, username, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS); | |
| 174 | 176 | throw exception(AUTH_LOGIN_BAD_CREDENTIALS); |
| 175 | 177 | } catch (DisabledException disabledException) { |
| 176 | - this.createLoginLog(username, logTypeEnum, LoginResultEnum.USER_DISABLED); | |
| 178 | + createLoginLog(null, username, logTypeEnum, LoginResultEnum.USER_DISABLED); | |
| 177 | 179 | throw exception(AUTH_LOGIN_USER_DISABLED); |
| 178 | 180 | } catch (AuthenticationException authenticationException) { |
| 179 | 181 | log.error("[login0][username({}) 发生未知异常]", username, authenticationException); |
| 180 | - this.createLoginLog(username, logTypeEnum, LoginResultEnum.UNKNOWN_ERROR); | |
| 182 | + createLoginLog(null, username, logTypeEnum, LoginResultEnum.UNKNOWN_ERROR); | |
| 181 | 183 | throw exception(AUTH_LOGIN_FAIL_UNKNOWN); |
| 182 | 184 | } |
| 183 | 185 | Assert.notNull(authentication.getPrincipal(), "Principal 不会为空"); |
| 184 | - return (LoginUser) authentication.getPrincipal(); | |
| 186 | + // 构建 User 对象 | |
| 187 | + return AuthConvert.INSTANCE.convert((SpringSecurityUser) authentication.getPrincipal()) | |
| 188 | + .setUserType(getUserType().getValue()); | |
| 185 | 189 | } |
| 186 | 190 | |
| 187 | - private void createLoginLog(String username, LoginLogTypeEnum logTypeEnum, LoginResultEnum loginResult) { | |
| 191 | + private void createLoginLog(Long userId, String username, | |
| 192 | + LoginLogTypeEnum logTypeEnum, LoginResultEnum loginResult) { | |
| 188 | 193 | // 获得用户 |
| 189 | - AdminUserDO user = userService.getUserByUsername(username); | |
| 194 | + if (userId == null) { | |
| 195 | + AdminUserDO user = userService.getUserByUsername(username); | |
| 196 | + userId = user != null ? user.getId() : null; | |
| 197 | + } | |
| 190 | 198 | // 插入登录日志 |
| 191 | 199 | LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO(); |
| 192 | 200 | reqDTO.setLogType(logTypeEnum.getType()); |
| 193 | 201 | reqDTO.setTraceId(TracerUtils.getTraceId()); |
| 194 | - if (user != null) { | |
| 195 | - reqDTO.setUserId(user.getId()); | |
| 202 | + if (userId != null) { | |
| 203 | + reqDTO.setUserId(userId); | |
| 196 | 204 | } |
| 197 | 205 | reqDTO.setUserType(getUserType().getValue()); |
| 198 | 206 | reqDTO.setUsername(username); |
| ... | ... | @@ -201,8 +209,8 @@ public class AdminAuthServiceImpl implements AdminAuthService { |
| 201 | 209 | reqDTO.setResult(loginResult.getResult()); |
| 202 | 210 | loginLogService.createLoginLog(reqDTO); |
| 203 | 211 | // 更新最后登录时间 |
| 204 | - if (user != null && Objects.equals(LoginResultEnum.SUCCESS.getResult(), loginResult.getResult())) { | |
| 205 | - userService.updateUserLogin(user.getId(), ServletUtils.getClientIP()); | |
| 212 | + if (userId != null && Objects.equals(LoginResultEnum.SUCCESS.getResult(), loginResult.getResult())) { | |
| 213 | + userService.updateUserLogin(userId, ServletUtils.getClientIP()); | |
| 206 | 214 | } |
| 207 | 215 | } |
| 208 | 216 | |
| ... | ... | @@ -225,7 +233,8 @@ public class AdminAuthServiceImpl implements AdminAuthService { |
| 225 | 233 | LoginUser loginUser = buildLoginUser(user); |
| 226 | 234 | |
| 227 | 235 | // 缓存登录用户到 Redis 中,返回 Token 令牌 |
| 228 | - return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_SOCIAL, userIp, userAgent); | |
| 236 | + return createUserSessionAfterLoginSuccess(loginUser, null, | |
| 237 | + LoginLogTypeEnum.LOGIN_SOCIAL, userIp, userAgent); | |
| 229 | 238 | } |
| 230 | 239 | |
| 231 | 240 | @Override |
| ... | ... | @@ -237,12 +246,14 @@ public class AdminAuthServiceImpl implements AdminAuthService { |
| 237 | 246 | socialUserService.bindSocialUser(AuthConvert.INSTANCE.convert(loginUser.getId(), getUserType().getValue(), reqVO)); |
| 238 | 247 | |
| 239 | 248 | // 缓存登录用户到 Redis 中,返回 Token 令牌 |
| 240 | - return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_SOCIAL, userIp, userAgent); | |
| 249 | + return createUserSessionAfterLoginSuccess(loginUser, reqVO.getUsername(), | |
| 250 | + LoginLogTypeEnum.LOGIN_SOCIAL, userIp, userAgent); | |
| 241 | 251 | } |
| 242 | 252 | |
| 243 | - private String createUserSessionAfterLoginSuccess(LoginUser loginUser, LoginLogTypeEnum logType, String userIp, String userAgent) { | |
| 253 | + private String createUserSessionAfterLoginSuccess(LoginUser loginUser, String username, | |
| 254 | + LoginLogTypeEnum logType, String userIp, String userAgent) { | |
| 244 | 255 | // 插入登陆日志 |
| 245 | - createLoginLog(loginUser.getUsername(), logType, LoginResultEnum.SUCCESS); | |
| 256 | + createLoginLog(loginUser.getId(), username, logType, LoginResultEnum.SUCCESS); | |
| 246 | 257 | // 缓存登录用户到 Redis 中,返回 Token 令牌 |
| 247 | 258 | return userSessionService.createUserSession(loginUser, userIp, userAgent); |
| 248 | 259 | } |
| ... | ... | @@ -257,7 +268,7 @@ public class AdminAuthServiceImpl implements AdminAuthService { |
| 257 | 268 | // 删除 session |
| 258 | 269 | userSessionService.deleteUserSession(token); |
| 259 | 270 | // 记录登出日志 |
| 260 | - createLogoutLog(loginUser.getId(), loginUser.getUsername()); | |
| 271 | + createLogoutLog(loginUser.getId()); | |
| 261 | 272 | } |
| 262 | 273 | |
| 263 | 274 | @Override |
| ... | ... | @@ -265,13 +276,13 @@ public class AdminAuthServiceImpl implements AdminAuthService { |
| 265 | 276 | return UserTypeEnum.ADMIN; |
| 266 | 277 | } |
| 267 | 278 | |
| 268 | - private void createLogoutLog(Long userId, String username) { | |
| 279 | + private void createLogoutLog(Long userId) { | |
| 269 | 280 | LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO(); |
| 270 | 281 | reqDTO.setLogType(LoginLogTypeEnum.LOGOUT_SELF.getType()); |
| 271 | 282 | reqDTO.setTraceId(TracerUtils.getTraceId()); |
| 272 | 283 | reqDTO.setUserId(userId); |
| 284 | + reqDTO.setUsername(getUsername(userId)); | |
| 273 | 285 | reqDTO.setUserType(getUserType().getValue()); |
| 274 | - reqDTO.setUsername(username); | |
| 275 | 286 | reqDTO.setUserAgent(ServletUtils.getUserAgent()); |
| 276 | 287 | reqDTO.setUserIp(ServletUtils.getClientIP()); |
| 277 | 288 | reqDTO.setResult(LoginResultEnum.SUCCESS.getResult()); |
| ... | ... | @@ -280,36 +291,19 @@ public class AdminAuthServiceImpl implements AdminAuthService { |
| 280 | 291 | |
| 281 | 292 | @Override |
| 282 | 293 | public LoginUser verifyTokenAndRefresh(String token) { |
| 283 | - // 获得 LoginUser | |
| 284 | - LoginUser loginUser = userSessionService.getLoginUser(token); | |
| 285 | - if (loginUser == null) { | |
| 286 | - return null; | |
| 287 | - } | |
| 288 | - // 刷新 LoginUser 缓存 | |
| 289 | - return this.refreshLoginUserCache(token, loginUser); | |
| 294 | + return userSessionService.getLoginUser(token); | |
| 290 | 295 | } |
| 291 | 296 | |
| 292 | - private LoginUser refreshLoginUserCache(String token, LoginUser loginUser) { | |
| 293 | - // 每 1/3 的 Session 超时时间,刷新 LoginUser 缓存 | |
| 294 | - if (System.currentTimeMillis() - loginUser.getUpdateTime().getTime() < | |
| 295 | - userSessionService.getSessionTimeoutMillis() / 3) { | |
| 296 | - return loginUser; | |
| 297 | - } | |
| 298 | - | |
| 299 | - // 重新加载 AdminUserDO 信息 | |
| 300 | - AdminUserDO user = userService.getUser(loginUser.getId()); | |
| 301 | - if (user == null || CommonStatusEnum.DISABLE.getStatus().equals(user.getStatus())) { | |
| 302 | - throw exception(AUTH_TOKEN_EXPIRED); // 校验 token 时,用户被禁用的情况下,也认为 token 过期,方便前端跳转到登录界面 | |
| 303 | - } | |
| 304 | - | |
| 305 | - // 刷新 LoginUser 缓存 | |
| 306 | - LoginUser newLoginUser= buildLoginUser(user); | |
| 307 | - userSessionService.refreshUserSession(token, newLoginUser); | |
| 308 | - return newLoginUser; | |
| 297 | + private LoginUser buildLoginUser(AdminUserDO user) { | |
| 298 | + return AuthConvert.INSTANCE.convert(user).setUserType(getUserType().getValue()); | |
| 309 | 299 | } |
| 310 | 300 | |
| 311 | - private LoginUser buildLoginUser(AdminUserDO user) { | |
| 312 | - return AuthConvert.INSTANCE.convert(user); | |
| 301 | + private String getUsername(Long userId) { | |
| 302 | + if (userId == null) { | |
| 303 | + return null; | |
| 304 | + } | |
| 305 | + AdminUserDO user = userService.getUser(userId); | |
| 306 | + return user != null ? user.getUsername() : null; | |
| 313 | 307 | } |
| 314 | 308 | |
| 315 | 309 | } | ... | ... |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/UserSessionServiceImpl.java
| ... | ... | @@ -23,7 +23,6 @@ import org.springframework.stereotype.Service; |
| 23 | 23 | import javax.annotation.Resource; |
| 24 | 24 | import java.time.Duration; |
| 25 | 25 | import java.util.Collection; |
| 26 | -import java.util.Date; | |
| 27 | 26 | import java.util.List; |
| 28 | 27 | |
| 29 | 28 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; |
| ... | ... | @@ -106,12 +105,11 @@ public class UserSessionServiceImpl implements UserSessionService { |
| 106 | 105 | // 生成 Session 编号 |
| 107 | 106 | String token = generateToken(); |
| 108 | 107 | // 写入 Redis 缓存 |
| 109 | - loginUser.setUpdateTime(new Date()); | |
| 110 | 108 | loginUserRedisDAO.set(token, loginUser); |
| 111 | 109 | // 写入 DB 中 |
| 112 | 110 | UserSessionDO userSession = UserSessionDO.builder().token(token) |
| 113 | 111 | .userId(loginUser.getId()).userType(loginUser.getUserType()) |
| 114 | - .userIp(userIp).userAgent(userAgent).username(loginUser.getUsername()) | |
| 112 | + .userIp(userIp).userAgent(userAgent).username("") | |
| 115 | 113 | .sessionTimeout(addTime(Duration.ofMillis(getSessionTimeoutMillis()))) |
| 116 | 114 | .build(); |
| 117 | 115 | userSessionMapper.insert(userSession); |
| ... | ... | @@ -121,15 +119,7 @@ public class UserSessionServiceImpl implements UserSessionService { |
| 121 | 119 | |
| 122 | 120 | @Override |
| 123 | 121 | public void refreshUserSession(String token, LoginUser loginUser) { |
| 124 | - // 写入 Redis 缓存 | |
| 125 | - loginUser.setUpdateTime(new Date()); | |
| 126 | - loginUserRedisDAO.set(token, loginUser); | |
| 127 | - // 更新 DB 中 | |
| 128 | - UserSessionDO updateObj = UserSessionDO.builder().build(); | |
| 129 | - updateObj.setUsername(loginUser.getUsername()); | |
| 130 | - updateObj.setUpdateTime(new Date()); | |
| 131 | - updateObj.setSessionTimeout(addTime(Duration.ofMillis(getSessionTimeoutMillis()))); | |
| 132 | - userSessionMapper.updateByToken(token, updateObj); | |
| 122 | + | |
| 133 | 123 | } |
| 134 | 124 | |
| 135 | 125 | @Override | ... | ... |
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/UserSessionServiceImplTest.java
| ... | ... | @@ -201,7 +201,7 @@ public class UserSessionServiceImplTest extends BaseDbAndRedisUnitTest { |
| 201 | 201 | assertPojoEquals(redisLoginUser, loginUser, "username", "password"); |
| 202 | 202 | // 校验 UserSessionDO 记录 |
| 203 | 203 | UserSessionDO updateDO = userSessionMapper.selectOne(UserSessionDO::getToken, token); |
| 204 | - assertEquals(updateDO.getUsername(), loginUser.getUsername()); | |
| 204 | +// assertEquals(updateDO.getUsername(), loginUser.getUsername()); | |
| 205 | 205 | assertNotNull(userSession.getUpdateTime()); |
| 206 | 206 | assertNotNull(userSession.getSessionTimeout()); |
| 207 | 207 | } | ... | ... |