Commit 5cf68961e1a5d7b310e6e9d8b9308c88c254b0dd
1 parent
6ed62486
1. 修改在线会话的实现
2. 接入到会员管理 OAuth2.0
Showing
28 changed files
with
356 additions
and
150 deletions
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/SecurityProperties.java
| ... | ... | @@ -6,7 +6,6 @@ import org.springframework.validation.annotation.Validated; |
| 6 | 6 | |
| 7 | 7 | import javax.validation.constraints.NotEmpty; |
| 8 | 8 | import javax.validation.constraints.NotNull; |
| 9 | -import java.time.Duration; | |
| 10 | 9 | |
| 11 | 10 | @ConfigurationProperties(prefix = "yudao.security") |
| 12 | 11 | @Validated |
| ... | ... | @@ -18,18 +17,6 @@ public class SecurityProperties { |
| 18 | 17 | */ |
| 19 | 18 | @NotEmpty(message = "Token Header 不能为空") |
| 20 | 19 | private String tokenHeader; |
| 21 | - /** | |
| 22 | - * Token 过期时间 | |
| 23 | - */ | |
| 24 | - @NotNull(message = "Token 过期时间不能为空") | |
| 25 | - private Duration tokenTimeout; | |
| 26 | - /** | |
| 27 | - * Session 过期时间 | |
| 28 | - * | |
| 29 | - * 当 User 用户超过当前时间未操作,则 Session 会过期 | |
| 30 | - */ | |
| 31 | - @NotNull(message = "Session 过期时间不能为空") | |
| 32 | - private Duration sessionTimeout; | |
| 33 | 20 | |
| 34 | 21 | /** |
| 35 | 22 | * mock 模式的开关 | ... | ... |
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.http
| ... | ... | @@ -19,7 +19,7 @@ tenant-id: {{appTenentId}} |
| 19 | 19 | } |
| 20 | 20 | |
| 21 | 21 | ### 请求 /sms-login 接口 => 成功 |
| 22 | -POST {{appApi}}/member/sms-login | |
| 22 | +POST {{appApi}}/member/auth/sms-login | |
| 23 | 23 | Content-Type: application/json |
| 24 | 24 | tenant-id: {{appTenentId}} |
| 25 | 25 | |
| ... | ... | @@ -29,7 +29,12 @@ tenant-id: {{appTenentId}} |
| 29 | 29 | } |
| 30 | 30 | |
| 31 | 31 | ### 请求 /logout 接口 => 成功 |
| 32 | -POST {{appApi}}/member/logout | |
| 32 | +POST {{appApi}}/member/auth/logout | |
| 33 | 33 | Content-Type: application/json |
| 34 | 34 | Authorization: Bearer c1b76bdaf2c146c581caa4d7fd81ee66 |
| 35 | 35 | tenant-id: {{appTenentId}} |
| 36 | + | |
| 37 | +### 请求 /auth/refresh-token 接口 => 成功 | |
| 38 | +POST {{appApi}}/member/auth/refresh-token?refreshToken=bc43d929094849a28b3a69f6e6940d70 | |
| 39 | +Content-Type: application/json | |
| 40 | +tenant-id: {{appTenentId}} | ... | ... |
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.java
| ... | ... | @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.member.controller.app.auth; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.util.StrUtil; |
| 4 | 4 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 5 | +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; | |
| 5 | 6 | import cn.iocoder.yudao.framework.security.config.SecurityProperties; |
| 6 | 7 | import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated; |
| 7 | 8 | import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; |
| ... | ... | @@ -20,8 +21,6 @@ import javax.servlet.http.HttpServletRequest; |
| 20 | 21 | import javax.validation.Valid; |
| 21 | 22 | |
| 22 | 23 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 23 | -import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; | |
| 24 | -import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getUserAgent; | |
| 25 | 24 | import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; |
| 26 | 25 | |
| 27 | 26 | @Api(tags = "用户 APP - 认证") |
| ... | ... | @@ -40,8 +39,7 @@ public class AppAuthController { |
| 40 | 39 | @PostMapping("/login") |
| 41 | 40 | @ApiOperation("使用手机 + 密码登录") |
| 42 | 41 | public CommonResult<AppAuthLoginRespVO> login(@RequestBody @Valid AppAuthLoginReqVO reqVO) { |
| 43 | - String token = authService.login(reqVO, getClientIP(), getUserAgent()); | |
| 44 | - return success(AppAuthLoginRespVO.builder().token(token).build()); | |
| 42 | + return success(authService.login(reqVO)); | |
| 45 | 43 | } |
| 46 | 44 | |
| 47 | 45 | @PostMapping("/logout") |
| ... | ... | @@ -54,12 +52,20 @@ public class AppAuthController { |
| 54 | 52 | return success(true); |
| 55 | 53 | } |
| 56 | 54 | |
| 55 | + @PostMapping("/refresh-token") | |
| 56 | + @ApiOperation("刷新令牌") | |
| 57 | + @ApiImplicitParam(name = "refreshToken", value = "刷新令牌", required = true, dataTypeClass = String.class) | |
| 58 | + @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 | |
| 59 | + public CommonResult<AppAuthLoginRespVO> refreshToken(@RequestParam("refreshToken") String refreshToken) { | |
| 60 | + return success(authService.refreshToken(refreshToken)); | |
| 61 | + } | |
| 62 | + | |
| 63 | + // ========== 短信登录相关 ========== | |
| 64 | + | |
| 57 | 65 | @PostMapping("/sms-login") |
| 58 | 66 | @ApiOperation("使用手机 + 验证码登录") |
| 59 | 67 | public CommonResult<AppAuthLoginRespVO> smsLogin(@RequestBody @Valid AppAuthSmsLoginReqVO reqVO) { |
| 60 | - String token = authService.smsLogin(reqVO, getClientIP(), getUserAgent()); | |
| 61 | - // 返回结果 | |
| 62 | - return success(AppAuthLoginRespVO.builder().token(token).build()); | |
| 68 | + return success(authService.smsLogin(reqVO)); | |
| 63 | 69 | } |
| 64 | 70 | |
| 65 | 71 | @PostMapping("/send-sms-code") |
| ... | ... | @@ -100,16 +106,14 @@ public class AppAuthController { |
| 100 | 106 | |
| 101 | 107 | @PostMapping("/social-quick-login") |
| 102 | 108 | @ApiOperation(value = "社交快捷登录,使用 code 授权码", notes = "适合未登录的用户,但是社交账号已绑定用户") |
| 103 | - public CommonResult<AppAuthLoginRespVO> socialLogin(@RequestBody @Valid AppAuthSocialQuickLoginReqVO reqVO) { | |
| 104 | - String token = authService.socialQuickLogin(reqVO, getClientIP(), getUserAgent()); | |
| 105 | - return success(AppAuthLoginRespVO.builder().token(token).build()); | |
| 109 | + public CommonResult<AppAuthLoginRespVO> socialQuickLogin(@RequestBody @Valid AppAuthSocialQuickLoginReqVO reqVO) { | |
| 110 | + return success(authService.socialQuickLogin(reqVO)); | |
| 106 | 111 | } |
| 107 | 112 | |
| 108 | 113 | @PostMapping("/social-bind-login") |
| 109 | 114 | @ApiOperation(value = "社交绑定登录,使用 手机号 + 手机验证码", notes = "适合未登录的用户,进行登录 + 绑定") |
| 110 | - public CommonResult<AppAuthLoginRespVO> socialLogin2(@RequestBody @Valid AppAuthSocialBindLoginReqVO reqVO) { | |
| 111 | - String token = authService.socialBindLogin(reqVO, getClientIP(), getUserAgent()); | |
| 112 | - return success(AppAuthLoginRespVO.builder().token(token).build()); | |
| 115 | + public CommonResult<AppAuthLoginRespVO> socialBindLogin(@RequestBody @Valid AppAuthSocialBindLoginReqVO reqVO) { | |
| 116 | + return success(authService.socialBindLogin(reqVO)); | |
| 113 | 117 | } |
| 114 | 118 | |
| 115 | 119 | } | ... | ... |
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthLoginRespVO.java
| ... | ... | @@ -7,14 +7,25 @@ import lombok.Builder; |
| 7 | 7 | import lombok.Data; |
| 8 | 8 | import lombok.NoArgsConstructor; |
| 9 | 9 | |
| 10 | -@ApiModel("用户 APP - 手机密码登录 Response VO") | |
| 10 | +import java.util.Date; | |
| 11 | + | |
| 12 | +@ApiModel("用户 APP - 登录 Response VO") | |
| 11 | 13 | @Data |
| 12 | 14 | @NoArgsConstructor |
| 13 | 15 | @AllArgsConstructor |
| 14 | 16 | @Builder |
| 15 | 17 | public class AppAuthLoginRespVO { |
| 16 | 18 | |
| 17 | - @ApiModelProperty(value = "token", required = true, example = "yudaoyuanma") | |
| 18 | - private String token; | |
| 19 | + @ApiModelProperty(value = "用户编号", required = true, example = "1024") | |
| 20 | + private Long userId; | |
| 21 | + | |
| 22 | + @ApiModelProperty(value = "访问令牌", required = true, example = "happy") | |
| 23 | + private String accessToken; | |
| 24 | + | |
| 25 | + @ApiModelProperty(value = "刷新令牌", required = true, example = "nice") | |
| 26 | + private String refreshToken; | |
| 27 | + | |
| 28 | + @ApiModelProperty(value = "过期时间", required = true) | |
| 29 | + private Date expiresTime; | |
| 19 | 30 | |
| 20 | 31 | } | ... | ... |
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.security.core.LoginUser; | |
| 4 | 3 | import cn.iocoder.yudao.module.member.controller.app.auth.vo.*; |
| 5 | 4 | import cn.iocoder.yudao.module.member.controller.app.social.vo.AppSocialUserUnbindReqVO; |
| 6 | -import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; | |
| 5 | +import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenRespDTO; | |
| 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; |
| ... | ... | @@ -17,8 +16,6 @@ public interface AuthConvert { |
| 17 | 16 | |
| 18 | 17 | AuthConvert INSTANCE = Mappers.getMapper(AuthConvert.class); |
| 19 | 18 | |
| 20 | - LoginUser convert(MemberUserDO bean); | |
| 21 | - | |
| 22 | 19 | SocialUserBindReqDTO convert(Long userId, Integer userType, AppAuthSocialBindLoginReqVO reqVO); |
| 23 | 20 | SocialUserBindReqDTO convert(Long userId, Integer userType, AppAuthSocialQuickLoginReqVO reqVO); |
| 24 | 21 | SocialUserUnbindReqDTO convert(Long userId, Integer userType, AppSocialUserUnbindReqVO reqVO); |
| ... | ... | @@ -27,4 +24,6 @@ public interface AuthConvert { |
| 27 | 24 | SmsCodeUseReqDTO convert(AppAuthResetPasswordReqVO reqVO, SmsSceneEnum scene, String usedIp); |
| 28 | 25 | SmsCodeUseReqDTO convert(AppAuthSmsLoginReqVO reqVO, Integer scene, String usedIp); |
| 29 | 26 | |
| 27 | + AppAuthLoginRespVO convert(OAuth2AccessTokenRespDTO bean); | |
| 28 | + | |
| 30 | 29 | } | ... | ... |
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthService.java
| ... | ... | @@ -17,11 +17,9 @@ public interface MemberAuthService { |
| 17 | 17 | * 手机 + 密码登录 |
| 18 | 18 | * |
| 19 | 19 | * @param reqVO 登录信息 |
| 20 | - * @param userIp 用户 IP | |
| 21 | - * @param userAgent 用户 UA | |
| 22 | - * @return 身份令牌,使用 JWT 方式 | |
| 20 | + * @return 登录结果 | |
| 23 | 21 | */ |
| 24 | - String login(@Valid AppAuthLoginReqVO reqVO, String userIp, String userAgent); | |
| 22 | + AppAuthLoginRespVO login(@Valid AppAuthLoginReqVO reqVO); | |
| 25 | 23 | |
| 26 | 24 | /** |
| 27 | 25 | * 基于 token 退出登录 |
| ... | ... | @@ -34,31 +32,25 @@ public interface MemberAuthService { |
| 34 | 32 | * 手机 + 验证码登陆 |
| 35 | 33 | * |
| 36 | 34 | * @param reqVO 登陆信息 |
| 37 | - * @param userIp 用户 IP | |
| 38 | - * @param userAgent 用户 UA | |
| 39 | - * @return 身份令牌,使用 JWT 方式 | |
| 35 | + * @return 登录结果 | |
| 40 | 36 | */ |
| 41 | - String smsLogin(@Valid AppAuthSmsLoginReqVO reqVO, String userIp, String userAgent); | |
| 37 | + AppAuthLoginRespVO smsLogin(@Valid AppAuthSmsLoginReqVO reqVO); | |
| 42 | 38 | |
| 43 | 39 | /** |
| 44 | 40 | * 社交登录,使用 code 授权码 |
| 45 | 41 | * |
| 46 | 42 | * @param reqVO 登录信息 |
| 47 | - * @param userIp 用户 IP | |
| 48 | - * @param userAgent 用户 UA | |
| 49 | - * @return 身份令牌,使用 JWT 方式 | |
| 43 | + * @return 登录结果 | |
| 50 | 44 | */ |
| 51 | - String socialQuickLogin(@Valid AppAuthSocialQuickLoginReqVO reqVO, String userIp, String userAgent); | |
| 45 | + AppAuthLoginRespVO socialQuickLogin(@Valid AppAuthSocialQuickLoginReqVO reqVO); | |
| 52 | 46 | |
| 53 | 47 | /** |
| 54 | 48 | * 社交登录,使用 手机号 + 手机验证码 |
| 55 | 49 | * |
| 56 | 50 | * @param reqVO 登录信息 |
| 57 | - * @param userIp 用户 IP | |
| 58 | - * @param userAgent 用户 UA | |
| 59 | - * @return 身份令牌,使用 JWT 方式 | |
| 51 | + * @return 登录结果 | |
| 60 | 52 | */ |
| 61 | - String socialBindLogin(@Valid AppAuthSocialBindLoginReqVO reqVO, String userIp, String userAgent); | |
| 53 | + AppAuthLoginRespVO socialBindLogin(@Valid AppAuthSocialBindLoginReqVO reqVO); | |
| 62 | 54 | |
| 63 | 55 | /** |
| 64 | 56 | * 获得社交认证 URL |
| ... | ... | @@ -90,4 +82,11 @@ public interface MemberAuthService { |
| 90 | 82 | */ |
| 91 | 83 | void sendSmsCode(Long userId, AppAuthSmsSendReqVO reqVO); |
| 92 | 84 | |
| 85 | + /** | |
| 86 | + * 刷新访问令牌 | |
| 87 | + * | |
| 88 | + * @param refreshToken 刷新令牌 | |
| 89 | + * @return 登录结果 | |
| 90 | + */ | |
| 91 | + AppAuthLoginRespVO refreshToken(String refreshToken); | |
| 93 | 92 | } | ... | ... |
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthServiceImpl.java
| ... | ... | @@ -6,17 +6,19 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 6 | 6 | import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; |
| 7 | 7 | import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils; |
| 8 | 8 | import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; |
| 9 | -import cn.iocoder.yudao.framework.security.core.LoginUser; | |
| 10 | 9 | import cn.iocoder.yudao.module.member.controller.app.auth.vo.*; |
| 11 | 10 | import cn.iocoder.yudao.module.member.convert.auth.AuthConvert; |
| 12 | 11 | import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; |
| 13 | 12 | import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper; |
| 14 | 13 | import cn.iocoder.yudao.module.member.service.user.MemberUserService; |
| 15 | -import cn.iocoder.yudao.module.system.api.auth.UserSessionApi; | |
| 14 | +import cn.iocoder.yudao.module.system.api.auth.OAuth2TokenApi; | |
| 15 | +import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenCreateReqDTO; | |
| 16 | +import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenRespDTO; | |
| 16 | 17 | import cn.iocoder.yudao.module.system.api.logger.LoginLogApi; |
| 17 | 18 | import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO; |
| 18 | 19 | import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; |
| 19 | 20 | import cn.iocoder.yudao.module.system.api.social.SocialUserApi; |
| 21 | +import cn.iocoder.yudao.module.system.enums.auth.OAuth2ClientIdEnum; | |
| 20 | 22 | import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; |
| 21 | 23 | import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum; |
| 22 | 24 | import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; |
| ... | ... | @@ -49,9 +51,9 @@ public class MemberAuthServiceImpl implements MemberAuthService { |
| 49 | 51 | @Resource |
| 50 | 52 | private LoginLogApi loginLogApi; |
| 51 | 53 | @Resource |
| 52 | - private UserSessionApi userSessionApi; | |
| 53 | - @Resource | |
| 54 | 54 | private SocialUserApi socialUserApi; |
| 55 | + @Resource | |
| 56 | + private OAuth2TokenApi oauth2TokenApi; | |
| 55 | 57 | |
| 56 | 58 | @Resource |
| 57 | 59 | private PasswordEncoder passwordEncoder; |
| ... | ... | @@ -59,35 +61,31 @@ public class MemberAuthServiceImpl implements MemberAuthService { |
| 59 | 61 | private MemberUserMapper userMapper; |
| 60 | 62 | |
| 61 | 63 | @Override |
| 62 | - public String login(AppAuthLoginReqVO reqVO, String userIp, String userAgent) { | |
| 64 | + public AppAuthLoginRespVO login(AppAuthLoginReqVO reqVO) { | |
| 63 | 65 | // 使用手机 + 密码,进行登录。 |
| 64 | - LoginUser loginUser = login0(reqVO.getMobile(), reqVO.getPassword()); | |
| 66 | + MemberUserDO user = login0(reqVO.getMobile(), reqVO.getPassword()); | |
| 65 | 67 | |
| 66 | - // 缓存登录用户到 Redis 中,返回 Token 令牌 | |
| 67 | - return createUserSessionAfterLoginSuccess(loginUser, reqVO.getMobile(), | |
| 68 | - LoginLogTypeEnum.LOGIN_USERNAME, userIp, userAgent); | |
| 68 | + // 创建 Token 令牌,记录登录日志 | |
| 69 | + return createTokenAfterLoginSuccess(user, reqVO.getMobile(), LoginLogTypeEnum.LOGIN_MOBILE); | |
| 69 | 70 | } |
| 70 | 71 | |
| 71 | 72 | @Override |
| 72 | 73 | @Transactional |
| 73 | - public String smsLogin(AppAuthSmsLoginReqVO reqVO, String userIp, String userAgent) { | |
| 74 | + public AppAuthLoginRespVO smsLogin(AppAuthSmsLoginReqVO reqVO) { | |
| 74 | 75 | // 校验验证码 |
| 76 | + String userIp = getClientIP(); | |
| 75 | 77 | smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SmsSceneEnum.MEMBER_LOGIN.getScene(), userIp)); |
| 76 | 78 | |
| 77 | 79 | // 获得获得注册用户 |
| 78 | 80 | MemberUserDO user = userService.createUserIfAbsent(reqVO.getMobile(), userIp); |
| 79 | 81 | Assert.notNull(user, "获取用户失败,结果为空"); |
| 80 | 82 | |
| 81 | - // 执行登陆 | |
| 82 | - LoginUser loginUser = buildLoginUser(user); | |
| 83 | - | |
| 84 | - // 缓存登录用户到 Redis 中,返回 Token 令牌 | |
| 85 | - return createUserSessionAfterLoginSuccess(loginUser, reqVO.getMobile(), | |
| 86 | - LoginLogTypeEnum.LOGIN_SMS, userIp, userAgent); | |
| 83 | + // 创建 Token 令牌,记录登录日志 | |
| 84 | + return createTokenAfterLoginSuccess(user, reqVO.getMobile(), LoginLogTypeEnum.LOGIN_SMS); | |
| 87 | 85 | } |
| 88 | 86 | |
| 89 | 87 | @Override |
| 90 | - public String socialQuickLogin(AppAuthSocialQuickLoginReqVO reqVO, String userIp, String userAgent) { | |
| 88 | + public AppAuthLoginRespVO socialQuickLogin(AppAuthSocialQuickLoginReqVO reqVO) { | |
| 91 | 89 | // 使用 code 授权码,进行登录。然后,获得到绑定的用户编号 |
| 92 | 90 | Long userId = socialUserApi.getBindUserId(UserTypeEnum.MEMBER.getValue(), reqVO.getType(), |
| 93 | 91 | reqVO.getCode(), reqVO.getState()); |
| ... | ... | @@ -101,33 +99,30 @@ public class MemberAuthServiceImpl implements MemberAuthService { |
| 101 | 99 | throw exception(USER_NOT_EXISTS); |
| 102 | 100 | } |
| 103 | 101 | |
| 104 | - // 创建 LoginUser 对象 | |
| 105 | - LoginUser loginUser = buildLoginUser(user); | |
| 106 | - | |
| 107 | - // 缓存登录用户到 Redis 中,返回 Token 令牌 | |
| 108 | - return createUserSessionAfterLoginSuccess(loginUser, null, | |
| 109 | - LoginLogTypeEnum.LOGIN_SOCIAL, userIp, userAgent); | |
| 102 | + // 创建 Token 令牌,记录登录日志 | |
| 103 | + return createTokenAfterLoginSuccess(user, null, LoginLogTypeEnum.LOGIN_SOCIAL); | |
| 110 | 104 | } |
| 111 | 105 | |
| 112 | 106 | @Override |
| 113 | - public String socialBindLogin(AppAuthSocialBindLoginReqVO reqVO, String userIp, String userAgent) { | |
| 107 | + public AppAuthLoginRespVO socialBindLogin(AppAuthSocialBindLoginReqVO reqVO) { | |
| 114 | 108 | // 使用手机号、手机验证码登录 |
| 115 | 109 | AppAuthSmsLoginReqVO loginReqVO = AppAuthSmsLoginReqVO.builder() |
| 116 | 110 | .mobile(reqVO.getMobile()).code(reqVO.getSmsCode()).build(); |
| 117 | - String token = this.smsLogin(loginReqVO, userIp, userAgent); | |
| 118 | - LoginUser loginUser = userSessionApi.getLoginUser(token); | |
| 111 | + AppAuthLoginRespVO token = smsLogin(loginReqVO); | |
| 119 | 112 | |
| 120 | 113 | // 绑定社交用户 |
| 121 | - socialUserApi.bindSocialUser(AuthConvert.INSTANCE.convert(loginUser.getId(), getUserType().getValue(), reqVO)); | |
| 114 | + socialUserApi.bindSocialUser(AuthConvert.INSTANCE.convert(token.getUserId(), getUserType().getValue(), reqVO)); | |
| 122 | 115 | return token; |
| 123 | 116 | } |
| 124 | 117 | |
| 125 | - private String createUserSessionAfterLoginSuccess(LoginUser loginUser, String mobile, | |
| 126 | - LoginLogTypeEnum logType, String userIp, String userAgent) { | |
| 118 | + private AppAuthLoginRespVO createTokenAfterLoginSuccess(MemberUserDO user, String mobile, LoginLogTypeEnum logType) { | |
| 127 | 119 | // 插入登陆日志 |
| 128 | - createLoginLog(loginUser.getId(), mobile, logType, LoginResultEnum.SUCCESS); | |
| 129 | - // 缓存登录用户到 Redis 中,返回 Token 令牌 | |
| 130 | - return userSessionApi.createUserSession(loginUser, userIp, userAgent); | |
| 120 | + createLoginLog(user.getId(), mobile, logType, LoginResultEnum.SUCCESS); | |
| 121 | + // 创建 Token 令牌 | |
| 122 | + OAuth2AccessTokenRespDTO accessTokenRespDTO = oauth2TokenApi.createAccessToken(new OAuth2AccessTokenCreateReqDTO() | |
| 123 | + .setUserId(user.getId()).setUserType(getUserType().getValue()).setClientId(OAuth2ClientIdEnum.DEFAULT.getId())); | |
| 124 | + // 构建返回结果 | |
| 125 | + return AuthConvert.INSTANCE.convert(accessTokenRespDTO); | |
| 131 | 126 | } |
| 132 | 127 | |
| 133 | 128 | @Override |
| ... | ... | @@ -135,7 +130,7 @@ public class MemberAuthServiceImpl implements MemberAuthService { |
| 135 | 130 | return socialUserApi.getAuthorizeUrl(type, redirectUri); |
| 136 | 131 | } |
| 137 | 132 | |
| 138 | - private LoginUser login0(String mobile, String password) { | |
| 133 | + private MemberUserDO login0(String mobile, String password) { | |
| 139 | 134 | final LoginLogTypeEnum logTypeEnum = LoginLogTypeEnum.LOGIN_MOBILE; |
| 140 | 135 | // 校验账号是否存在 |
| 141 | 136 | MemberUserDO user = userService.getUserByMobile(mobile); |
| ... | ... | @@ -152,9 +147,7 @@ public class MemberAuthServiceImpl implements MemberAuthService { |
| 152 | 147 | createLoginLog(user.getId(), mobile, logTypeEnum, LoginResultEnum.USER_DISABLED); |
| 153 | 148 | throw exception(AUTH_LOGIN_USER_DISABLED); |
| 154 | 149 | } |
| 155 | - | |
| 156 | - // 构建 User 对象 | |
| 157 | - return buildLoginUser(user); | |
| 150 | + return user; | |
| 158 | 151 | } |
| 159 | 152 | |
| 160 | 153 | private void createLoginLog(Long userId, String mobile, LoginLogTypeEnum logType, LoginResultEnum loginResult) { |
| ... | ... | @@ -177,15 +170,13 @@ public class MemberAuthServiceImpl implements MemberAuthService { |
| 177 | 170 | |
| 178 | 171 | @Override |
| 179 | 172 | public void logout(String token) { |
| 180 | - // 查询用户信息 | |
| 181 | - LoginUser loginUser = userSessionApi.getLoginUser(token); | |
| 182 | - if (loginUser == null) { | |
| 173 | + // 删除访问令牌 | |
| 174 | + OAuth2AccessTokenRespDTO accessTokenRespDTO = oauth2TokenApi.removeAccessToken(token); | |
| 175 | + if (accessTokenRespDTO == null) { | |
| 183 | 176 | return; |
| 184 | 177 | } |
| 185 | - // 删除 session | |
| 186 | - userSessionApi.deleteUserSession(token); | |
| 187 | - // 记录登出日志 | |
| 188 | - createLogoutLog(loginUser.getId()); | |
| 178 | + // 删除成功,则记录登出日志 | |
| 179 | + createLogoutLog(accessTokenRespDTO.getUserId()); | |
| 189 | 180 | } |
| 190 | 181 | |
| 191 | 182 | @Override |
| ... | ... | @@ -219,6 +210,12 @@ public class MemberAuthServiceImpl implements MemberAuthService { |
| 219 | 210 | smsCodeApi.sendSmsCode(AuthConvert.INSTANCE.convert(reqVO).setCreateIp(getClientIP())); |
| 220 | 211 | } |
| 221 | 212 | |
| 213 | + @Override | |
| 214 | + public AppAuthLoginRespVO refreshToken(String refreshToken) { | |
| 215 | + OAuth2AccessTokenRespDTO accessTokenDO = oauth2TokenApi.refreshAccessToken(refreshToken, OAuth2ClientIdEnum.DEFAULT.getId()); | |
| 216 | + return AuthConvert.INSTANCE.convert(accessTokenDO); | |
| 217 | + } | |
| 218 | + | |
| 222 | 219 | /** |
| 223 | 220 | * 校验旧密码 |
| 224 | 221 | * |
| ... | ... | @@ -260,10 +257,6 @@ public class MemberAuthServiceImpl implements MemberAuthService { |
| 260 | 257 | loginLogApi.createLoginLog(reqDTO); |
| 261 | 258 | } |
| 262 | 259 | |
| 263 | - private LoginUser buildLoginUser(MemberUserDO user) { | |
| 264 | - return AuthConvert.INSTANCE.convert(user).setUserType(getUserType().getValue()); | |
| 265 | - } | |
| 266 | - | |
| 267 | 260 | private String getMobile(Long userId) { |
| 268 | 261 | if (userId == null) { |
| 269 | 262 | return null; | ... | ... |
yudao-module-member/yudao-module-member-biz/src/test/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthServiceTest.java
| ... | ... | @@ -9,7 +9,7 @@ import cn.iocoder.yudao.module.member.controller.app.auth.vo.AppAuthUpdatePasswo |
| 9 | 9 | import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; |
| 10 | 10 | import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper; |
| 11 | 11 | import cn.iocoder.yudao.module.member.service.user.MemberUserService; |
| 12 | -import cn.iocoder.yudao.module.system.api.auth.UserSessionApi; | |
| 12 | +import cn.iocoder.yudao.module.system.api.auth.OAuth2TokenApi; | |
| 13 | 13 | import cn.iocoder.yudao.module.system.api.logger.LoginLogApi; |
| 14 | 14 | import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; |
| 15 | 15 | import cn.iocoder.yudao.module.system.api.social.SocialUserApi; |
| ... | ... | @@ -46,7 +46,7 @@ public class MemberAuthServiceTest extends BaseDbAndRedisUnitTest { |
| 46 | 46 | @MockBean |
| 47 | 47 | private LoginLogApi loginLogApi; |
| 48 | 48 | @MockBean |
| 49 | - private UserSessionApi userSessionApi; | |
| 49 | + private OAuth2TokenApi oauth2TokenApi; | |
| 50 | 50 | @MockBean |
| 51 | 51 | private SocialUserApi socialUserApi; |
| 52 | 52 | @MockBean | ... | ... |
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/OAuth2TokenApi.java
| ... | ... | @@ -13,10 +13,37 @@ import javax.validation.Valid; |
| 13 | 13 | */ |
| 14 | 14 | public interface OAuth2TokenApi { |
| 15 | 15 | |
| 16 | + /** | |
| 17 | + * 创建访问令牌 | |
| 18 | + * | |
| 19 | + * @param reqDTO 访问令牌的创建信息 | |
| 20 | + * @return 访问令牌的信息 | |
| 21 | + */ | |
| 16 | 22 | OAuth2AccessTokenRespDTO createAccessToken(@Valid OAuth2AccessTokenCreateReqDTO reqDTO); |
| 17 | 23 | |
| 24 | + /** | |
| 25 | + * 校验访问令牌 | |
| 26 | + * | |
| 27 | + * @param accessToken 访问令牌 | |
| 28 | + * @return 访问令牌的信息 | |
| 29 | + */ | |
| 18 | 30 | OAuth2AccessTokenCheckRespDTO checkAccessToken(String accessToken); |
| 19 | 31 | |
| 20 | -// void removeToken(OAuth2RemoveTokenByUserReqDTO removeTokenDTO); | |
| 32 | + /** | |
| 33 | + * 移除访问令牌 | |
| 34 | + * | |
| 35 | + * @param accessToken 访问令牌 | |
| 36 | + * @return 访问令牌的信息 | |
| 37 | + */ | |
| 38 | + OAuth2AccessTokenRespDTO removeAccessToken(String accessToken); | |
| 39 | + | |
| 40 | + /** | |
| 41 | + * 刷新访问令牌 | |
| 42 | + * | |
| 43 | + * @param refreshToken 刷新令牌 | |
| 44 | + * @param clientId 客户端编号 | |
| 45 | + * @return 访问令牌的信息 | |
| 46 | + */ | |
| 47 | + OAuth2AccessTokenRespDTO refreshAccessToken(String refreshToken, Long clientId); | |
| 21 | 48 | |
| 22 | 49 | } | ... | ... |
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/dto/OAuth2AccessTokenCreateReqDTO.java
| ... | ... | @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.system.api.auth.dto; |
| 3 | 3 | import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; |
| 4 | 4 | import cn.iocoder.yudao.framework.common.validation.InEnum; |
| 5 | 5 | import lombok.Data; |
| 6 | -import lombok.experimental.Accessors; | |
| 7 | 6 | |
| 8 | 7 | import javax.validation.constraints.NotNull; |
| 9 | 8 | import java.io.Serializable; |
| ... | ... | @@ -14,14 +13,13 @@ import java.io.Serializable; |
| 14 | 13 | * @author 芋道源码 |
| 15 | 14 | */ |
| 16 | 15 | @Data |
| 17 | -@Accessors(chain = true) | |
| 18 | 16 | public class OAuth2AccessTokenCreateReqDTO implements Serializable { |
| 19 | 17 | |
| 20 | 18 | /** |
| 21 | 19 | * 用户编号 |
| 22 | 20 | */ |
| 23 | 21 | @NotNull(message = "用户编号不能为空") |
| 24 | - private Integer userId; | |
| 22 | + private Long userId; | |
| 25 | 23 | /** |
| 26 | 24 | * 用户类型 |
| 27 | 25 | */ | ... | ... |
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/dto/OAuth2AccessTokenRespDTO.java
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/auth/OAuth2TokenApiImpl.java
| ... | ... | @@ -4,6 +4,7 @@ import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenCheckRespDTO |
| 4 | 4 | import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenCreateReqDTO; |
| 5 | 5 | import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenRespDTO; |
| 6 | 6 | import cn.iocoder.yudao.module.system.convert.auth.OAuth2TokenConvert; |
| 7 | +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; | |
| 7 | 8 | import cn.iocoder.yudao.module.system.service.auth.OAuth2TokenService; |
| 8 | 9 | import org.springframework.stereotype.Service; |
| 9 | 10 | |
| ... | ... | @@ -22,7 +23,9 @@ public class OAuth2TokenApiImpl implements OAuth2TokenApi { |
| 22 | 23 | |
| 23 | 24 | @Override |
| 24 | 25 | public OAuth2AccessTokenRespDTO createAccessToken(OAuth2AccessTokenCreateReqDTO reqDTO) { |
| 25 | - return null; | |
| 26 | + OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken( | |
| 27 | + reqDTO.getUserId(), reqDTO.getUserType(), reqDTO.getClientId()); | |
| 28 | + return OAuth2TokenConvert.INSTANCE.convert2(accessTokenDO); | |
| 26 | 29 | } |
| 27 | 30 | |
| 28 | 31 | @Override |
| ... | ... | @@ -30,4 +33,16 @@ public class OAuth2TokenApiImpl implements OAuth2TokenApi { |
| 30 | 33 | return OAuth2TokenConvert.INSTANCE.convert(oauth2TokenService.checkAccessToken(accessToken)); |
| 31 | 34 | } |
| 32 | 35 | |
| 36 | + @Override | |
| 37 | + public OAuth2AccessTokenRespDTO removeAccessToken(String accessToken) { | |
| 38 | + OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.removeAccessToken(accessToken); | |
| 39 | + return OAuth2TokenConvert.INSTANCE.convert2(accessTokenDO); | |
| 40 | + } | |
| 41 | + | |
| 42 | + @Override | |
| 43 | + public OAuth2AccessTokenRespDTO refreshAccessToken(String refreshToken, Long clientId) { | |
| 44 | + OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, clientId); | |
| 45 | + return OAuth2TokenConvert.INSTANCE.convert2(accessTokenDO); | |
| 46 | + } | |
| 47 | + | |
| 33 | 48 | } | ... | ... |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java
| ... | ... | @@ -11,6 +11,7 @@ import cn.iocoder.yudao.module.system.convert.auth.AuthConvert; |
| 11 | 11 | import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; |
| 12 | 12 | import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; |
| 13 | 13 | import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; |
| 14 | +import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; | |
| 14 | 15 | import cn.iocoder.yudao.module.system.enums.permission.MenuTypeEnum; |
| 15 | 16 | import cn.iocoder.yudao.module.system.service.auth.AdminAuthService; |
| 16 | 17 | import cn.iocoder.yudao.module.system.service.permission.PermissionService; |
| ... | ... | @@ -70,14 +71,15 @@ public class AuthController { |
| 70 | 71 | public CommonResult<Boolean> logout(HttpServletRequest request) { |
| 71 | 72 | String token = obtainAuthorization(request, securityProperties.getTokenHeader()); |
| 72 | 73 | if (StrUtil.isNotBlank(token)) { |
| 73 | - authService.logout(token); | |
| 74 | + authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType()); | |
| 74 | 75 | } |
| 75 | 76 | return success(true); |
| 76 | 77 | } |
| 77 | 78 | |
| 78 | 79 | @PostMapping("/refresh-token") |
| 79 | 80 | @ApiOperation("刷新令牌") |
| 80 | - @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 TODO 接口文档 | |
| 81 | + @ApiImplicitParam(name = "refreshToken", value = "刷新令牌", required = true, dataTypeClass = String.class) | |
| 82 | + @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 | |
| 81 | 83 | public CommonResult<AuthLoginRespVO> refreshToken(@RequestParam("refreshToken") String refreshToken) { |
| 82 | 84 | return success(authService.refreshToken(refreshToken)); |
| 83 | 85 | } | ... | ... |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/OAuth2TokenController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.system.controller.admin.auth; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 4 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 5 | +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.token.OAuth2AccessTokenPageReqVO; | |
| 6 | +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.token.OAuth2AccessTokenRespVO; | |
| 7 | +import cn.iocoder.yudao.module.system.convert.auth.OAuth2TokenConvert; | |
| 8 | +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; | |
| 9 | +import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; | |
| 10 | +import cn.iocoder.yudao.module.system.service.auth.AdminAuthService; | |
| 11 | +import cn.iocoder.yudao.module.system.service.auth.OAuth2TokenService; | |
| 12 | +import io.swagger.annotations.Api; | |
| 13 | +import io.swagger.annotations.ApiImplicitParam; | |
| 14 | +import io.swagger.annotations.ApiOperation; | |
| 15 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 16 | +import org.springframework.web.bind.annotation.*; | |
| 17 | + | |
| 18 | +import javax.annotation.Resource; | |
| 19 | +import javax.validation.Valid; | |
| 20 | + | |
| 21 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 22 | + | |
| 23 | +@Api(tags = "管理后台 - OAuth2.0 令牌") | |
| 24 | +@RestController | |
| 25 | +@RequestMapping("/system/oauth2-token") | |
| 26 | +public class OAuth2TokenController { | |
| 27 | + | |
| 28 | + @Resource | |
| 29 | + private OAuth2TokenService oauth2TokenService; | |
| 30 | + @Resource | |
| 31 | + private AdminAuthService authService; | |
| 32 | + | |
| 33 | + @GetMapping("/page") | |
| 34 | + @ApiOperation(value = "获得访问令牌分页", notes = "只返回有效期内的") | |
| 35 | + @PreAuthorize("@ss.hasPermission('system:oauth2-token:page')") | |
| 36 | + public CommonResult<PageResult<OAuth2AccessTokenRespVO>> getAccessTokenPage(@Valid OAuth2AccessTokenPageReqVO reqVO) { | |
| 37 | + PageResult<OAuth2AccessTokenDO> pageResult = oauth2TokenService.getAccessTokenPage(reqVO); | |
| 38 | + return success(OAuth2TokenConvert.INSTANCE.convert(pageResult)); | |
| 39 | + } | |
| 40 | + | |
| 41 | + @DeleteMapping("/delete") | |
| 42 | + @ApiOperation("删除访问令牌") | |
| 43 | + @ApiImplicitParam(name = "accessToken", value = "访问令牌", required = true, dataTypeClass = String.class, example = "tudou") | |
| 44 | + @PreAuthorize("@ss.hasPermission('system:oauth2-token:delete')") | |
| 45 | + public CommonResult<Boolean> deleteAccessToken(@RequestParam("accessToken") String accessToken) { | |
| 46 | + authService.logout(accessToken, LoginLogTypeEnum.LOGOUT_DELETE.getType()); | |
| 47 | + return success(true); | |
| 48 | + } | |
| 49 | + | |
| 50 | +} | ... | ... |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/token/OAuth2AccessTokenPageReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.system.controller.admin.auth.vo.token; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageParam; | |
| 4 | +import io.swagger.annotations.ApiModel; | |
| 5 | +import io.swagger.annotations.ApiModelProperty; | |
| 6 | +import lombok.Data; | |
| 7 | +import lombok.EqualsAndHashCode; | |
| 8 | + | |
| 9 | +@ApiModel("管理后台 - 访问令牌分页 Request VO") | |
| 10 | +@Data | |
| 11 | +@EqualsAndHashCode(callSuper = true) | |
| 12 | +public class OAuth2AccessTokenPageReqVO extends PageParam { | |
| 13 | + | |
| 14 | + @ApiModelProperty(value = "用户编号", required = true, example = "666") | |
| 15 | + private Long userId; | |
| 16 | + | |
| 17 | + @ApiModelProperty(value = "用户类型", required = true, example = "2", notes = "参见 UserTypeEnum 枚举") | |
| 18 | + private Integer userType; | |
| 19 | + | |
| 20 | + @ApiModelProperty(value = "客户端编号", required = true, example = "2") | |
| 21 | + private Long clientId; | |
| 22 | + | |
| 23 | +} | ... | ... |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/token/OAuth2AccessTokenRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.system.controller.admin.auth.vo.token; | |
| 2 | + | |
| 3 | +import io.swagger.annotations.ApiModel; | |
| 4 | +import io.swagger.annotations.ApiModelProperty; | |
| 5 | +import lombok.AllArgsConstructor; | |
| 6 | +import lombok.Data; | |
| 7 | +import lombok.NoArgsConstructor; | |
| 8 | + | |
| 9 | +import java.util.Date; | |
| 10 | + | |
| 11 | +@ApiModel("管理后台 - 访问令牌 Response VO") | |
| 12 | +@Data | |
| 13 | +@NoArgsConstructor | |
| 14 | +@AllArgsConstructor | |
| 15 | +public class OAuth2AccessTokenRespVO { | |
| 16 | + | |
| 17 | + @ApiModelProperty(value = "编号", required = true, example = "1024") | |
| 18 | + private Long id; | |
| 19 | + | |
| 20 | + @ApiModelProperty(value = "访问令牌", required = true, example = "tudou") | |
| 21 | + private String accessToken; | |
| 22 | + | |
| 23 | + @ApiModelProperty(value = "刷新令牌", required = true, example = "nice") | |
| 24 | + private String refreshToken; | |
| 25 | + | |
| 26 | + @ApiModelProperty(value = "用户编号", required = true, example = "666") | |
| 27 | + private Long userId; | |
| 28 | + | |
| 29 | + @ApiModelProperty(value = "用户类型", required = true, example = "2", notes = "参见 UserTypeEnum 枚举") | |
| 30 | + private Integer userType; | |
| 31 | + | |
| 32 | + @ApiModelProperty(value = "客户端编号", required = true, example = "2") | |
| 33 | + private Long clientId; | |
| 34 | + | |
| 35 | + @ApiModelProperty(value = "创建时间", required = true) | |
| 36 | + private Date createTime; | |
| 37 | + | |
| 38 | + @ApiModelProperty(value = "过期时间", required = true) | |
| 39 | + private Date expiresTime; | |
| 40 | + | |
| 41 | +} | ... | ... |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/OAuth2TokenConvert.java
| 1 | 1 | package cn.iocoder.yudao.module.system.convert.auth; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 3 | 4 | import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenCheckRespDTO; |
| 5 | +import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenRespDTO; | |
| 6 | +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.token.OAuth2AccessTokenRespVO; | |
| 4 | 7 | import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; |
| 5 | 8 | import org.mapstruct.Mapper; |
| 6 | 9 | import org.mapstruct.factory.Mappers; |
| ... | ... | @@ -12,4 +15,8 @@ public interface OAuth2TokenConvert { |
| 12 | 15 | |
| 13 | 16 | OAuth2AccessTokenCheckRespDTO convert(OAuth2AccessTokenDO bean); |
| 14 | 17 | |
| 18 | + PageResult<OAuth2AccessTokenRespVO> convert(PageResult<OAuth2AccessTokenDO> page); | |
| 19 | + | |
| 20 | + OAuth2AccessTokenRespDTO convert2(OAuth2AccessTokenDO bean); | |
| 21 | + | |
| 15 | 22 | } | ... | ... |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2AccessTokenMapper.java
| 1 | 1 | package cn.iocoder.yudao.module.system.dal.mysql.auth; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 3 | 4 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| 5 | +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; | |
| 6 | +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.token.OAuth2AccessTokenPageReqVO; | |
| 4 | 7 | import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; |
| 5 | 8 | import org.apache.ibatis.annotations.Mapper; |
| 6 | 9 | |
| 10 | +import java.util.Date; | |
| 7 | 11 | import java.util.List; |
| 8 | 12 | |
| 9 | 13 | @Mapper |
| ... | ... | @@ -17,4 +21,13 @@ public interface OAuth2AccessTokenMapper extends BaseMapperX<OAuth2AccessTokenDO |
| 17 | 21 | return selectList(OAuth2AccessTokenDO::getRefreshToken, refreshToken); |
| 18 | 22 | } |
| 19 | 23 | |
| 24 | + default PageResult<OAuth2AccessTokenDO> selectPage(OAuth2AccessTokenPageReqVO reqVO) { | |
| 25 | + return selectPage(reqVO, new LambdaQueryWrapperX<OAuth2AccessTokenDO>() | |
| 26 | + .eqIfPresent(OAuth2AccessTokenDO::getUserId, reqVO.getUserId()) | |
| 27 | + .eqIfPresent(OAuth2AccessTokenDO::getUserType, reqVO.getUserType()) | |
| 28 | + .eqIfPresent(OAuth2AccessTokenDO::getClientId, reqVO.getClientId()) | |
| 29 | + .gt(OAuth2AccessTokenDO::getExpiresTime, new Date()) | |
| 30 | + .orderByDesc(OAuth2AccessTokenDO::getId)); | |
| 31 | + } | |
| 32 | + | |
| 20 | 33 | } | ... | ... |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java
| ... | ... | @@ -18,6 +18,7 @@ import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum; |
| 18 | 18 | import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; |
| 19 | 19 | import cn.iocoder.yudao.module.system.service.common.CaptchaService; |
| 20 | 20 | import cn.iocoder.yudao.module.system.service.logger.LoginLogService; |
| 21 | +import cn.iocoder.yudao.module.system.service.member.MemberService; | |
| 21 | 22 | import cn.iocoder.yudao.module.system.service.social.SocialUserService; |
| 22 | 23 | import cn.iocoder.yudao.module.system.service.user.AdminUserService; |
| 23 | 24 | import com.google.common.annotations.VisibleForTesting; |
| ... | ... | @@ -51,6 +52,8 @@ public class AdminAuthServiceImpl implements AdminAuthService { |
| 51 | 52 | private OAuth2TokenService oauth2TokenService; |
| 52 | 53 | @Resource |
| 53 | 54 | private SocialUserService socialUserService; |
| 55 | + @Resource | |
| 56 | + private MemberService memberService; | |
| 54 | 57 | |
| 55 | 58 | @Resource |
| 56 | 59 | private Validator validator; |
| ... | ... | @@ -209,23 +212,27 @@ public class AdminAuthServiceImpl implements AdminAuthService { |
| 209 | 212 | } |
| 210 | 213 | |
| 211 | 214 | @Override |
| 212 | - public void logout(String token) { | |
| 215 | + public void logout(String token, Integer logType) { | |
| 213 | 216 | // 删除访问令牌 |
| 214 | 217 | OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.removeAccessToken(token); |
| 215 | 218 | if (accessTokenDO == null) { |
| 216 | 219 | return; |
| 217 | 220 | } |
| 218 | 221 | // 删除成功,则记录登出日志 |
| 219 | - createLogoutLog(accessTokenDO.getUserId()); | |
| 222 | + createLogoutLog(accessTokenDO.getUserId(), accessTokenDO.getUserType(), logType); | |
| 220 | 223 | } |
| 221 | 224 | |
| 222 | - private void createLogoutLog(Long userId) { | |
| 225 | + private void createLogoutLog(Long userId, Integer userType, Integer logType) { | |
| 223 | 226 | LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO(); |
| 224 | - reqDTO.setLogType(LoginLogTypeEnum.LOGOUT_SELF.getType()); | |
| 227 | + reqDTO.setLogType(logType); | |
| 225 | 228 | reqDTO.setTraceId(TracerUtils.getTraceId()); |
| 226 | 229 | reqDTO.setUserId(userId); |
| 227 | - reqDTO.setUsername(getUsername(userId)); | |
| 228 | - reqDTO.setUserType(getUserType().getValue()); | |
| 230 | + reqDTO.setUserType(userType); | |
| 231 | + if (ObjectUtil.notEqual(getUserType(), userType)) { | |
| 232 | + reqDTO.setUsername(getUsername(userId)); | |
| 233 | + } else { | |
| 234 | + reqDTO.setUsername(memberService.getMemberUserMobile(userId)); | |
| 235 | + } | |
| 229 | 236 | reqDTO.setUserAgent(ServletUtils.getUserAgent()); |
| 230 | 237 | reqDTO.setUserIp(ServletUtils.getClientIP()); |
| 231 | 238 | reqDTO.setResult(LoginResultEnum.SUCCESS.getResult()); | ... | ... |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2TokenService.java
| 1 | 1 | package cn.iocoder.yudao.module.system.service.auth; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 4 | +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.token.OAuth2AccessTokenPageReqVO; | |
| 3 | 5 | import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; |
| 4 | 6 | |
| 5 | 7 | /** |
| ... | ... | @@ -64,4 +66,12 @@ public interface OAuth2TokenService { |
| 64 | 66 | */ |
| 65 | 67 | OAuth2AccessTokenDO removeAccessToken(String accessToken); |
| 66 | 68 | |
| 69 | + /** | |
| 70 | + * 获得访问令牌分页 | |
| 71 | + * | |
| 72 | + * @param reqVO 请求 | |
| 73 | + * @return 访问令牌分页 | |
| 74 | + */ | |
| 75 | + PageResult<OAuth2AccessTokenDO> getAccessTokenPage(OAuth2AccessTokenPageReqVO reqVO); | |
| 76 | + | |
| 67 | 77 | } | ... | ... |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2TokenServiceImpl.java
| ... | ... | @@ -4,8 +4,10 @@ import cn.hutool.core.collection.CollUtil; |
| 4 | 4 | import cn.hutool.core.util.IdUtil; |
| 5 | 5 | import cn.hutool.core.util.ObjectUtil; |
| 6 | 6 | import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; |
| 7 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 7 | 8 | import cn.iocoder.yudao.framework.common.util.date.DateUtils; |
| 8 | 9 | import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; |
| 10 | +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.token.OAuth2AccessTokenPageReqVO; | |
| 9 | 11 | import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; |
| 10 | 12 | import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO; |
| 11 | 13 | import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2RefreshTokenDO; |
| ... | ... | @@ -125,6 +127,11 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService { |
| 125 | 127 | return accessTokenDO; |
| 126 | 128 | } |
| 127 | 129 | |
| 130 | + @Override | |
| 131 | + public PageResult<OAuth2AccessTokenDO> getAccessTokenPage(OAuth2AccessTokenPageReqVO reqVO) { | |
| 132 | + return oauth2AccessTokenMapper.selectPage(reqVO); | |
| 133 | + } | |
| 134 | + | |
| 128 | 135 | private OAuth2AccessTokenDO createOAuth2AccessToken(OAuth2RefreshTokenDO refreshTokenDO, OAuth2ClientDO clientDO) { |
| 129 | 136 | OAuth2AccessTokenDO accessTokenDO = new OAuth2AccessTokenDO().setAccessToken(generateAccessToken()) |
| 130 | 137 | .setUserId(refreshTokenDO.getUserId()).setUserType(refreshTokenDO.getUserType()).setClientId(clientDO.getId()) | ... | ... |
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AuthServiceImplTest.java
| ... | ... | @@ -221,7 +221,7 @@ public class AuthServiceImplTest extends BaseDbUnitTest { |
| 221 | 221 | when(oauth2TokenService.removeAccessToken(eq(token))).thenReturn(accessTokenDO); |
| 222 | 222 | |
| 223 | 223 | // 调用 |
| 224 | - authService.logout(token); | |
| 224 | + authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType()); | |
| 225 | 225 | // 校验调用参数 |
| 226 | 226 | verify(loginLogService).createLoginLog(argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGOUT_SELF.getType()) |
| 227 | 227 | && o.getResult().equals(LoginResultEnum.SUCCESS.getResult())) |
| ... | ... | @@ -234,7 +234,7 @@ public class AuthServiceImplTest extends BaseDbUnitTest { |
| 234 | 234 | String token = randomString(); |
| 235 | 235 | |
| 236 | 236 | // 调用 |
| 237 | - authService.logout(token); | |
| 237 | + authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType()); | |
| 238 | 238 | // 校验调用参数 |
| 239 | 239 | verify(loginLogService, never()).createLoginLog(any()); |
| 240 | 240 | } | ... | ... |
yudao-server/pom.xml
| ... | ... | @@ -21,11 +21,11 @@ |
| 21 | 21 | <url>https://github.com/YunaiV/ruoyi-vue-pro</url> |
| 22 | 22 | |
| 23 | 23 | <dependencies> |
| 24 | -<!-- <dependency>--> | |
| 25 | -<!-- <groupId>cn.iocoder.boot</groupId>--> | |
| 26 | -<!-- <artifactId>yudao-module-member-biz</artifactId>--> | |
| 27 | -<!-- <version>${revision}</version>--> | |
| 28 | -<!-- </dependency>--> | |
| 24 | + <dependency> | |
| 25 | + <groupId>cn.iocoder.boot</groupId> | |
| 26 | + <artifactId>yudao-module-member-biz</artifactId> | |
| 27 | + <version>${revision}</version> | |
| 28 | + </dependency> | |
| 29 | 29 | <dependency> |
| 30 | 30 | <groupId>cn.iocoder.boot</groupId> |
| 31 | 31 | <artifactId>yudao-module-system-biz</artifactId> | ... | ... |
yudao-server/src/main/resources/application-dev.yaml
yudao-server/src/main/resources/application-local.yaml
yudao-ui-admin/src/api/system/session.js renamed to yudao-ui-admin/src/api/system/oauth2/oauth2Token.js
| 1 | 1 | import request from '@/utils/request' |
| 2 | 2 | |
| 3 | -// 查询在线用户列表 | |
| 4 | -export function list(query) { | |
| 3 | +// 获得访问令牌分页 | |
| 4 | +export function getAccessTokenPage(query) { | |
| 5 | 5 | return request({ |
| 6 | - url: '/system/user-session/page', | |
| 6 | + url: '/system/oauth2-token/page', | |
| 7 | 7 | method: 'get', |
| 8 | 8 | params: query |
| 9 | 9 | }) |
| 10 | 10 | } |
| 11 | 11 | |
| 12 | -// 强退用户 | |
| 13 | -export function forceLogout(tokenId) { | |
| 12 | +// 删除访问令牌 | |
| 13 | +export function deleteAccessToken(accessToken) { | |
| 14 | 14 | return request({ |
| 15 | - url: '/system/user-session/delete?id=' + tokenId, | |
| 15 | + url: '/system/oauth2-token/delete?accessToken=' + accessToken, | |
| 16 | 16 | method: 'delete' |
| 17 | 17 | }) |
| 18 | 18 | } | ... | ... |
yudao-ui-admin/src/views/system/session/index.vue renamed to yudao-ui-admin/src/views/system/oauth2/token/index.vue
| ... | ... | @@ -3,11 +3,14 @@ |
| 3 | 3 | <doc-alert title="用户体系" url="https://doc.iocoder.cn/user-center/" /> |
| 4 | 4 | <!-- 搜索工作栏 --> |
| 5 | 5 | <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" label-width="68px"> |
| 6 | - <el-form-item label="登录地址" prop="userIp"> | |
| 7 | - <el-input v-model="queryParams.userIp" placeholder="请输入登录地址" clearable @keyup.enter.native="handleQuery"/> | |
| 6 | + <el-form-item label="用户编号" prop="userId"> | |
| 7 | + <el-input v-model="queryParams.userId" placeholder="请输入用户编号" clearable @keyup.enter.native="handleQuery"/> | |
| 8 | 8 | </el-form-item> |
| 9 | - <el-form-item label="用户名称" prop="username"> | |
| 10 | - <el-input v-model="queryParams.username" placeholder="请输入用户名称" clearable @keyup.enter.native="handleQuery"/> | |
| 9 | + <el-form-item label="用户类型" prop="userType"> | |
| 10 | + <el-select v-model="queryParams.userType" placeholder="请选择用户类型" clearable> | |
| 11 | + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.USER_TYPE)" | |
| 12 | + :key="dict.value" :label="dict.label" :value="dict.value"/> | |
| 13 | + </el-select> | |
| 11 | 14 | </el-form-item> |
| 12 | 15 | <el-form-item> |
| 13 | 16 | <el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button> |
| ... | ... | @@ -16,20 +19,28 @@ |
| 16 | 19 | |
| 17 | 20 | </el-form> |
| 18 | 21 | <el-table v-loading="loading" :data="list" style="width: 100%;"> |
| 19 | - <el-table-column label="会话编号" align="center" prop="id" width="300" /> | |
| 20 | - <el-table-column label="登录名称" align="center" prop="username" width="100" /> | |
| 21 | - <el-table-column label="部门名称" align="center" prop="deptName" width="100" /> | |
| 22 | - <el-table-column label="登录地址" align="center" prop="userIp" width="100" /> | |
| 23 | - <el-table-column label="userAgent" align="center" prop="userAgent" :show-overflow-tooltip="true" /> | |
| 24 | - <el-table-column label="登录时间" align="center" prop="createTime" width="180"> | |
| 22 | + <el-table-column label="访问令牌" align="center" prop="accessToken" width="300" /> | |
| 23 | + <el-table-column label="刷新令牌" align="center" prop="refreshToken" width="300" /> | |
| 24 | + <el-table-column label="用户编号" align="center" prop="userId" /> | |
| 25 | + <el-table-column label="用户类型" align="center" prop="userType" width="100"> | |
| 26 | + <template slot-scope="scope"> | |
| 27 | + <dict-tag :type="DICT_TYPE.USER_TYPE" :value="scope.row.userType"/> | |
| 28 | + </template> | |
| 29 | + </el-table-column> | |
| 30 | + <el-table-column label="创建时间" align="center" prop="createTime" width="180"> | |
| 25 | 31 | <template slot-scope="scope"> |
| 26 | 32 | <span>{{ parseTime(scope.row.createTime) }}</span> |
| 27 | 33 | </template> |
| 28 | 34 | </el-table-column> |
| 35 | + <el-table-column label="过期时间" align="center" prop="expiresTime" width="180"> | |
| 36 | + <template slot-scope="scope"> | |
| 37 | + <span>{{ parseTime(scope.row.expiresTime) }}</span> | |
| 38 | + </template> | |
| 39 | + </el-table-column> | |
| 29 | 40 | <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> |
| 30 | 41 | <template slot-scope="scope"> |
| 31 | 42 | <el-button size="mini" type="text" icon="el-icon-delete" @click="handleForceLogout(scope.row)" |
| 32 | - v-hasPermi="['system:user-session:delete']">强退</el-button> | |
| 43 | + v-hasPermi="['system:oauth2-token:delete']">强退</el-button> | |
| 33 | 44 | </template> |
| 34 | 45 | </el-table-column> |
| 35 | 46 | </el-table> |
| ... | ... | @@ -40,10 +51,10 @@ |
| 40 | 51 | </template> |
| 41 | 52 | |
| 42 | 53 | <script> |
| 43 | -import { list, forceLogout } from "@/api/system/session"; | |
| 54 | +import { getAccessTokenPage, deleteAccessToken } from "@/api/system/oauth2/oauth2Token"; | |
| 44 | 55 | |
| 45 | 56 | export default { |
| 46 | - name: "Online", | |
| 57 | + name: "OAuth2Token", | |
| 47 | 58 | data() { |
| 48 | 59 | return { |
| 49 | 60 | // 遮罩层 |
| ... | ... | @@ -56,8 +67,8 @@ export default { |
| 56 | 67 | queryParams: { |
| 57 | 68 | pageNo: 1, |
| 58 | 69 | pageSize: 10, |
| 59 | - userIp: undefined, | |
| 60 | - username: undefined | |
| 70 | + userId: undefined, | |
| 71 | + userType: undefined | |
| 61 | 72 | } |
| 62 | 73 | }; |
| 63 | 74 | }, |
| ... | ... | @@ -68,7 +79,7 @@ export default { |
| 68 | 79 | /** 查询登录日志列表 */ |
| 69 | 80 | getList() { |
| 70 | 81 | this.loading = true; |
| 71 | - list(this.queryParams).then(response => { | |
| 82 | + getAccessTokenPage(this.queryParams).then(response => { | |
| 72 | 83 | this.list = response.data.list; |
| 73 | 84 | this.total = response.data.total; |
| 74 | 85 | this.loading = false; |
| ... | ... | @@ -86,8 +97,8 @@ export default { |
| 86 | 97 | }, |
| 87 | 98 | /** 强退按钮操作 */ |
| 88 | 99 | handleForceLogout(row) { |
| 89 | - this.$modal.confirm('是否确认强退名称为"' + row.username + '"的数据项?').then(function() { | |
| 90 | - return forceLogout(row.id); | |
| 100 | + this.$modal.confirm('是否确认强退令牌为"' + row.accessToken + '"的数据项?').then(function() { | |
| 101 | + return deleteAccessToken(row.accessToken); | |
| 91 | 102 | }).then(() => { |
| 92 | 103 | this.getList(); |
| 93 | 104 | this.$modal.msgSuccess("强退成功"); | ... | ... |