parent
6ed624861d
commit
5cf68961e1
|
@ -6,7 +6,6 @@ import org.springframework.validation.annotation.Validated;
|
|||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.time.Duration;
|
||||
|
||||
@ConfigurationProperties(prefix = "yudao.security")
|
||||
@Validated
|
||||
|
@ -18,18 +17,6 @@ public class SecurityProperties {
|
|||
*/
|
||||
@NotEmpty(message = "Token Header 不能为空")
|
||||
private String tokenHeader;
|
||||
/**
|
||||
* Token 过期时间
|
||||
*/
|
||||
@NotNull(message = "Token 过期时间不能为空")
|
||||
private Duration tokenTimeout;
|
||||
/**
|
||||
* Session 过期时间
|
||||
*
|
||||
* 当 User 用户超过当前时间未操作,则 Session 会过期
|
||||
*/
|
||||
@NotNull(message = "Session 过期时间不能为空")
|
||||
private Duration sessionTimeout;
|
||||
|
||||
/**
|
||||
* mock 模式的开关
|
||||
|
|
|
@ -19,7 +19,7 @@ tenant-id: {{appTenentId}}
|
|||
}
|
||||
|
||||
### 请求 /sms-login 接口 => 成功
|
||||
POST {{appApi}}/member/sms-login
|
||||
POST {{appApi}}/member/auth/sms-login
|
||||
Content-Type: application/json
|
||||
tenant-id: {{appTenentId}}
|
||||
|
||||
|
@ -29,7 +29,12 @@ tenant-id: {{appTenentId}}
|
|||
}
|
||||
|
||||
### 请求 /logout 接口 => 成功
|
||||
POST {{appApi}}/member/logout
|
||||
POST {{appApi}}/member/auth/logout
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer c1b76bdaf2c146c581caa4d7fd81ee66
|
||||
tenant-id: {{appTenentId}}
|
||||
|
||||
### 请求 /auth/refresh-token 接口 => 成功
|
||||
POST {{appApi}}/member/auth/refresh-token?refreshToken=bc43d929094849a28b3a69f6e6940d70
|
||||
Content-Type: application/json
|
||||
tenant-id: {{appTenentId}}
|
||||
|
|
|
@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.member.controller.app.auth;
|
|||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
|
||||
import cn.iocoder.yudao.framework.security.config.SecurityProperties;
|
||||
import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
|
||||
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
|
||||
|
@ -20,8 +21,6 @@ import javax.servlet.http.HttpServletRequest;
|
|||
import javax.validation.Valid;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
|
||||
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getUserAgent;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Api(tags = "用户 APP - 认证")
|
||||
|
@ -40,8 +39,7 @@ public class AppAuthController {
|
|||
@PostMapping("/login")
|
||||
@ApiOperation("使用手机 + 密码登录")
|
||||
public CommonResult<AppAuthLoginRespVO> login(@RequestBody @Valid AppAuthLoginReqVO reqVO) {
|
||||
String token = authService.login(reqVO, getClientIP(), getUserAgent());
|
||||
return success(AppAuthLoginRespVO.builder().token(token).build());
|
||||
return success(authService.login(reqVO));
|
||||
}
|
||||
|
||||
@PostMapping("/logout")
|
||||
|
@ -54,12 +52,20 @@ public class AppAuthController {
|
|||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/refresh-token")
|
||||
@ApiOperation("刷新令牌")
|
||||
@ApiImplicitParam(name = "refreshToken", value = "刷新令牌", required = true, dataTypeClass = String.class)
|
||||
@OperateLog(enable = false) // 避免 Post 请求被记录操作日志
|
||||
public CommonResult<AppAuthLoginRespVO> refreshToken(@RequestParam("refreshToken") String refreshToken) {
|
||||
return success(authService.refreshToken(refreshToken));
|
||||
}
|
||||
|
||||
// ========== 短信登录相关 ==========
|
||||
|
||||
@PostMapping("/sms-login")
|
||||
@ApiOperation("使用手机 + 验证码登录")
|
||||
public CommonResult<AppAuthLoginRespVO> smsLogin(@RequestBody @Valid AppAuthSmsLoginReqVO reqVO) {
|
||||
String token = authService.smsLogin(reqVO, getClientIP(), getUserAgent());
|
||||
// 返回结果
|
||||
return success(AppAuthLoginRespVO.builder().token(token).build());
|
||||
return success(authService.smsLogin(reqVO));
|
||||
}
|
||||
|
||||
@PostMapping("/send-sms-code")
|
||||
|
@ -100,16 +106,14 @@ public class AppAuthController {
|
|||
|
||||
@PostMapping("/social-quick-login")
|
||||
@ApiOperation(value = "社交快捷登录,使用 code 授权码", notes = "适合未登录的用户,但是社交账号已绑定用户")
|
||||
public CommonResult<AppAuthLoginRespVO> socialLogin(@RequestBody @Valid AppAuthSocialQuickLoginReqVO reqVO) {
|
||||
String token = authService.socialQuickLogin(reqVO, getClientIP(), getUserAgent());
|
||||
return success(AppAuthLoginRespVO.builder().token(token).build());
|
||||
public CommonResult<AppAuthLoginRespVO> socialQuickLogin(@RequestBody @Valid AppAuthSocialQuickLoginReqVO reqVO) {
|
||||
return success(authService.socialQuickLogin(reqVO));
|
||||
}
|
||||
|
||||
@PostMapping("/social-bind-login")
|
||||
@ApiOperation(value = "社交绑定登录,使用 手机号 + 手机验证码", notes = "适合未登录的用户,进行登录 + 绑定")
|
||||
public CommonResult<AppAuthLoginRespVO> socialLogin2(@RequestBody @Valid AppAuthSocialBindLoginReqVO reqVO) {
|
||||
String token = authService.socialBindLogin(reqVO, getClientIP(), getUserAgent());
|
||||
return success(AppAuthLoginRespVO.builder().token(token).build());
|
||||
public CommonResult<AppAuthLoginRespVO> socialBindLogin(@RequestBody @Valid AppAuthSocialBindLoginReqVO reqVO) {
|
||||
return success(authService.socialBindLogin(reqVO));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,14 +7,25 @@ import lombok.Builder;
|
|||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@ApiModel("用户 APP - 手机密码登录 Response VO")
|
||||
import java.util.Date;
|
||||
|
||||
@ApiModel("用户 APP - 登录 Response VO")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class AppAuthLoginRespVO {
|
||||
|
||||
@ApiModelProperty(value = "token", required = true, example = "yudaoyuanma")
|
||||
private String token;
|
||||
@ApiModelProperty(value = "用户编号", required = true, example = "1024")
|
||||
private Long userId;
|
||||
|
||||
@ApiModelProperty(value = "访问令牌", required = true, example = "happy")
|
||||
private String accessToken;
|
||||
|
||||
@ApiModelProperty(value = "刷新令牌", required = true, example = "nice")
|
||||
private String refreshToken;
|
||||
|
||||
@ApiModelProperty(value = "过期时间", required = true)
|
||||
private Date expiresTime;
|
||||
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
package cn.iocoder.yudao.module.member.convert.auth;
|
||||
|
||||
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
||||
import cn.iocoder.yudao.module.member.controller.app.auth.vo.*;
|
||||
import cn.iocoder.yudao.module.member.controller.app.social.vo.AppSocialUserUnbindReqVO;
|
||||
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
|
||||
import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenRespDTO;
|
||||
import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO;
|
||||
import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
|
||||
|
@ -17,8 +16,6 @@ public interface AuthConvert {
|
|||
|
||||
AuthConvert INSTANCE = Mappers.getMapper(AuthConvert.class);
|
||||
|
||||
LoginUser convert(MemberUserDO bean);
|
||||
|
||||
SocialUserBindReqDTO convert(Long userId, Integer userType, AppAuthSocialBindLoginReqVO reqVO);
|
||||
SocialUserBindReqDTO convert(Long userId, Integer userType, AppAuthSocialQuickLoginReqVO reqVO);
|
||||
SocialUserUnbindReqDTO convert(Long userId, Integer userType, AppSocialUserUnbindReqVO reqVO);
|
||||
|
@ -27,4 +24,6 @@ public interface AuthConvert {
|
|||
SmsCodeUseReqDTO convert(AppAuthResetPasswordReqVO reqVO, SmsSceneEnum scene, String usedIp);
|
||||
SmsCodeUseReqDTO convert(AppAuthSmsLoginReqVO reqVO, Integer scene, String usedIp);
|
||||
|
||||
AppAuthLoginRespVO convert(OAuth2AccessTokenRespDTO bean);
|
||||
|
||||
}
|
||||
|
|
|
@ -17,11 +17,9 @@ public interface MemberAuthService {
|
|||
* 手机 + 密码登录
|
||||
*
|
||||
* @param reqVO 登录信息
|
||||
* @param userIp 用户 IP
|
||||
* @param userAgent 用户 UA
|
||||
* @return 身份令牌,使用 JWT 方式
|
||||
* @return 登录结果
|
||||
*/
|
||||
String login(@Valid AppAuthLoginReqVO reqVO, String userIp, String userAgent);
|
||||
AppAuthLoginRespVO login(@Valid AppAuthLoginReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 基于 token 退出登录
|
||||
|
@ -34,31 +32,25 @@ public interface MemberAuthService {
|
|||
* 手机 + 验证码登陆
|
||||
*
|
||||
* @param reqVO 登陆信息
|
||||
* @param userIp 用户 IP
|
||||
* @param userAgent 用户 UA
|
||||
* @return 身份令牌,使用 JWT 方式
|
||||
* @return 登录结果
|
||||
*/
|
||||
String smsLogin(@Valid AppAuthSmsLoginReqVO reqVO, String userIp, String userAgent);
|
||||
AppAuthLoginRespVO smsLogin(@Valid AppAuthSmsLoginReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 社交登录,使用 code 授权码
|
||||
*
|
||||
* @param reqVO 登录信息
|
||||
* @param userIp 用户 IP
|
||||
* @param userAgent 用户 UA
|
||||
* @return 身份令牌,使用 JWT 方式
|
||||
* @return 登录结果
|
||||
*/
|
||||
String socialQuickLogin(@Valid AppAuthSocialQuickLoginReqVO reqVO, String userIp, String userAgent);
|
||||
AppAuthLoginRespVO socialQuickLogin(@Valid AppAuthSocialQuickLoginReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 社交登录,使用 手机号 + 手机验证码
|
||||
*
|
||||
* @param reqVO 登录信息
|
||||
* @param userIp 用户 IP
|
||||
* @param userAgent 用户 UA
|
||||
* @return 身份令牌,使用 JWT 方式
|
||||
* @return 登录结果
|
||||
*/
|
||||
String socialBindLogin(@Valid AppAuthSocialBindLoginReqVO reqVO, String userIp, String userAgent);
|
||||
AppAuthLoginRespVO socialBindLogin(@Valid AppAuthSocialBindLoginReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 获得社交认证 URL
|
||||
|
@ -90,4 +82,11 @@ public interface MemberAuthService {
|
|||
*/
|
||||
void sendSmsCode(Long userId, AppAuthSmsSendReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 刷新访问令牌
|
||||
*
|
||||
* @param refreshToken 刷新令牌
|
||||
* @return 登录结果
|
||||
*/
|
||||
AppAuthLoginRespVO refreshToken(String refreshToken);
|
||||
}
|
||||
|
|
|
@ -6,17 +6,19 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
|||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||
import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
|
||||
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
||||
import cn.iocoder.yudao.module.member.controller.app.auth.vo.*;
|
||||
import cn.iocoder.yudao.module.member.convert.auth.AuthConvert;
|
||||
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
|
||||
import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper;
|
||||
import cn.iocoder.yudao.module.member.service.user.MemberUserService;
|
||||
import cn.iocoder.yudao.module.system.api.auth.UserSessionApi;
|
||||
import cn.iocoder.yudao.module.system.api.auth.OAuth2TokenApi;
|
||||
import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenCreateReqDTO;
|
||||
import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenRespDTO;
|
||||
import cn.iocoder.yudao.module.system.api.logger.LoginLogApi;
|
||||
import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO;
|
||||
import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
|
||||
import cn.iocoder.yudao.module.system.api.social.SocialUserApi;
|
||||
import cn.iocoder.yudao.module.system.enums.auth.OAuth2ClientIdEnum;
|
||||
import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum;
|
||||
import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum;
|
||||
import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum;
|
||||
|
@ -49,9 +51,9 @@ public class MemberAuthServiceImpl implements MemberAuthService {
|
|||
@Resource
|
||||
private LoginLogApi loginLogApi;
|
||||
@Resource
|
||||
private UserSessionApi userSessionApi;
|
||||
@Resource
|
||||
private SocialUserApi socialUserApi;
|
||||
@Resource
|
||||
private OAuth2TokenApi oauth2TokenApi;
|
||||
|
||||
@Resource
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
@ -59,35 +61,31 @@ public class MemberAuthServiceImpl implements MemberAuthService {
|
|||
private MemberUserMapper userMapper;
|
||||
|
||||
@Override
|
||||
public String login(AppAuthLoginReqVO reqVO, String userIp, String userAgent) {
|
||||
public AppAuthLoginRespVO login(AppAuthLoginReqVO reqVO) {
|
||||
// 使用手机 + 密码,进行登录。
|
||||
LoginUser loginUser = login0(reqVO.getMobile(), reqVO.getPassword());
|
||||
MemberUserDO user = login0(reqVO.getMobile(), reqVO.getPassword());
|
||||
|
||||
// 缓存登录用户到 Redis 中,返回 Token 令牌
|
||||
return createUserSessionAfterLoginSuccess(loginUser, reqVO.getMobile(),
|
||||
LoginLogTypeEnum.LOGIN_USERNAME, userIp, userAgent);
|
||||
// 创建 Token 令牌,记录登录日志
|
||||
return createTokenAfterLoginSuccess(user, reqVO.getMobile(), LoginLogTypeEnum.LOGIN_MOBILE);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public String smsLogin(AppAuthSmsLoginReqVO reqVO, String userIp, String userAgent) {
|
||||
public AppAuthLoginRespVO smsLogin(AppAuthSmsLoginReqVO reqVO) {
|
||||
// 校验验证码
|
||||
String userIp = getClientIP();
|
||||
smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SmsSceneEnum.MEMBER_LOGIN.getScene(), userIp));
|
||||
|
||||
// 获得获得注册用户
|
||||
MemberUserDO user = userService.createUserIfAbsent(reqVO.getMobile(), userIp);
|
||||
Assert.notNull(user, "获取用户失败,结果为空");
|
||||
|
||||
// 执行登陆
|
||||
LoginUser loginUser = buildLoginUser(user);
|
||||
|
||||
// 缓存登录用户到 Redis 中,返回 Token 令牌
|
||||
return createUserSessionAfterLoginSuccess(loginUser, reqVO.getMobile(),
|
||||
LoginLogTypeEnum.LOGIN_SMS, userIp, userAgent);
|
||||
// 创建 Token 令牌,记录登录日志
|
||||
return createTokenAfterLoginSuccess(user, reqVO.getMobile(), LoginLogTypeEnum.LOGIN_SMS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String socialQuickLogin(AppAuthSocialQuickLoginReqVO reqVO, String userIp, String userAgent) {
|
||||
public AppAuthLoginRespVO socialQuickLogin(AppAuthSocialQuickLoginReqVO reqVO) {
|
||||
// 使用 code 授权码,进行登录。然后,获得到绑定的用户编号
|
||||
Long userId = socialUserApi.getBindUserId(UserTypeEnum.MEMBER.getValue(), reqVO.getType(),
|
||||
reqVO.getCode(), reqVO.getState());
|
||||
|
@ -101,33 +99,30 @@ public class MemberAuthServiceImpl implements MemberAuthService {
|
|||
throw exception(USER_NOT_EXISTS);
|
||||
}
|
||||
|
||||
// 创建 LoginUser 对象
|
||||
LoginUser loginUser = buildLoginUser(user);
|
||||
|
||||
// 缓存登录用户到 Redis 中,返回 Token 令牌
|
||||
return createUserSessionAfterLoginSuccess(loginUser, null,
|
||||
LoginLogTypeEnum.LOGIN_SOCIAL, userIp, userAgent);
|
||||
// 创建 Token 令牌,记录登录日志
|
||||
return createTokenAfterLoginSuccess(user, null, LoginLogTypeEnum.LOGIN_SOCIAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String socialBindLogin(AppAuthSocialBindLoginReqVO reqVO, String userIp, String userAgent) {
|
||||
public AppAuthLoginRespVO socialBindLogin(AppAuthSocialBindLoginReqVO reqVO) {
|
||||
// 使用手机号、手机验证码登录
|
||||
AppAuthSmsLoginReqVO loginReqVO = AppAuthSmsLoginReqVO.builder()
|
||||
.mobile(reqVO.getMobile()).code(reqVO.getSmsCode()).build();
|
||||
String token = this.smsLogin(loginReqVO, userIp, userAgent);
|
||||
LoginUser loginUser = userSessionApi.getLoginUser(token);
|
||||
AppAuthLoginRespVO token = smsLogin(loginReqVO);
|
||||
|
||||
// 绑定社交用户
|
||||
socialUserApi.bindSocialUser(AuthConvert.INSTANCE.convert(loginUser.getId(), getUserType().getValue(), reqVO));
|
||||
socialUserApi.bindSocialUser(AuthConvert.INSTANCE.convert(token.getUserId(), getUserType().getValue(), reqVO));
|
||||
return token;
|
||||
}
|
||||
|
||||
private String createUserSessionAfterLoginSuccess(LoginUser loginUser, String mobile,
|
||||
LoginLogTypeEnum logType, String userIp, String userAgent) {
|
||||
private AppAuthLoginRespVO createTokenAfterLoginSuccess(MemberUserDO user, String mobile, LoginLogTypeEnum logType) {
|
||||
// 插入登陆日志
|
||||
createLoginLog(loginUser.getId(), mobile, logType, LoginResultEnum.SUCCESS);
|
||||
// 缓存登录用户到 Redis 中,返回 Token 令牌
|
||||
return userSessionApi.createUserSession(loginUser, userIp, userAgent);
|
||||
createLoginLog(user.getId(), mobile, logType, LoginResultEnum.SUCCESS);
|
||||
// 创建 Token 令牌
|
||||
OAuth2AccessTokenRespDTO accessTokenRespDTO = oauth2TokenApi.createAccessToken(new OAuth2AccessTokenCreateReqDTO()
|
||||
.setUserId(user.getId()).setUserType(getUserType().getValue()).setClientId(OAuth2ClientIdEnum.DEFAULT.getId()));
|
||||
// 构建返回结果
|
||||
return AuthConvert.INSTANCE.convert(accessTokenRespDTO);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -135,7 +130,7 @@ public class MemberAuthServiceImpl implements MemberAuthService {
|
|||
return socialUserApi.getAuthorizeUrl(type, redirectUri);
|
||||
}
|
||||
|
||||
private LoginUser login0(String mobile, String password) {
|
||||
private MemberUserDO login0(String mobile, String password) {
|
||||
final LoginLogTypeEnum logTypeEnum = LoginLogTypeEnum.LOGIN_MOBILE;
|
||||
// 校验账号是否存在
|
||||
MemberUserDO user = userService.getUserByMobile(mobile);
|
||||
|
@ -152,9 +147,7 @@ public class MemberAuthServiceImpl implements MemberAuthService {
|
|||
createLoginLog(user.getId(), mobile, logTypeEnum, LoginResultEnum.USER_DISABLED);
|
||||
throw exception(AUTH_LOGIN_USER_DISABLED);
|
||||
}
|
||||
|
||||
// 构建 User 对象
|
||||
return buildLoginUser(user);
|
||||
return user;
|
||||
}
|
||||
|
||||
private void createLoginLog(Long userId, String mobile, LoginLogTypeEnum logType, LoginResultEnum loginResult) {
|
||||
|
@ -177,15 +170,13 @@ public class MemberAuthServiceImpl implements MemberAuthService {
|
|||
|
||||
@Override
|
||||
public void logout(String token) {
|
||||
// 查询用户信息
|
||||
LoginUser loginUser = userSessionApi.getLoginUser(token);
|
||||
if (loginUser == null) {
|
||||
// 删除访问令牌
|
||||
OAuth2AccessTokenRespDTO accessTokenRespDTO = oauth2TokenApi.removeAccessToken(token);
|
||||
if (accessTokenRespDTO == null) {
|
||||
return;
|
||||
}
|
||||
// 删除 session
|
||||
userSessionApi.deleteUserSession(token);
|
||||
// 记录登出日志
|
||||
createLogoutLog(loginUser.getId());
|
||||
// 删除成功,则记录登出日志
|
||||
createLogoutLog(accessTokenRespDTO.getUserId());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -219,6 +210,12 @@ public class MemberAuthServiceImpl implements MemberAuthService {
|
|||
smsCodeApi.sendSmsCode(AuthConvert.INSTANCE.convert(reqVO).setCreateIp(getClientIP()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppAuthLoginRespVO refreshToken(String refreshToken) {
|
||||
OAuth2AccessTokenRespDTO accessTokenDO = oauth2TokenApi.refreshAccessToken(refreshToken, OAuth2ClientIdEnum.DEFAULT.getId());
|
||||
return AuthConvert.INSTANCE.convert(accessTokenDO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验旧密码
|
||||
*
|
||||
|
@ -260,10 +257,6 @@ public class MemberAuthServiceImpl implements MemberAuthService {
|
|||
loginLogApi.createLoginLog(reqDTO);
|
||||
}
|
||||
|
||||
private LoginUser buildLoginUser(MemberUserDO user) {
|
||||
return AuthConvert.INSTANCE.convert(user).setUserType(getUserType().getValue());
|
||||
}
|
||||
|
||||
private String getMobile(Long userId) {
|
||||
if (userId == null) {
|
||||
return null;
|
||||
|
|
|
@ -9,7 +9,7 @@ import cn.iocoder.yudao.module.member.controller.app.auth.vo.AppAuthUpdatePasswo
|
|||
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
|
||||
import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper;
|
||||
import cn.iocoder.yudao.module.member.service.user.MemberUserService;
|
||||
import cn.iocoder.yudao.module.system.api.auth.UserSessionApi;
|
||||
import cn.iocoder.yudao.module.system.api.auth.OAuth2TokenApi;
|
||||
import cn.iocoder.yudao.module.system.api.logger.LoginLogApi;
|
||||
import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
|
||||
import cn.iocoder.yudao.module.system.api.social.SocialUserApi;
|
||||
|
@ -46,7 +46,7 @@ public class MemberAuthServiceTest extends BaseDbAndRedisUnitTest {
|
|||
@MockBean
|
||||
private LoginLogApi loginLogApi;
|
||||
@MockBean
|
||||
private UserSessionApi userSessionApi;
|
||||
private OAuth2TokenApi oauth2TokenApi;
|
||||
@MockBean
|
||||
private SocialUserApi socialUserApi;
|
||||
@MockBean
|
||||
|
|
|
@ -13,10 +13,37 @@ import javax.validation.Valid;
|
|||
*/
|
||||
public interface OAuth2TokenApi {
|
||||
|
||||
/**
|
||||
* 创建访问令牌
|
||||
*
|
||||
* @param reqDTO 访问令牌的创建信息
|
||||
* @return 访问令牌的信息
|
||||
*/
|
||||
OAuth2AccessTokenRespDTO createAccessToken(@Valid OAuth2AccessTokenCreateReqDTO reqDTO);
|
||||
|
||||
/**
|
||||
* 校验访问令牌
|
||||
*
|
||||
* @param accessToken 访问令牌
|
||||
* @return 访问令牌的信息
|
||||
*/
|
||||
OAuth2AccessTokenCheckRespDTO checkAccessToken(String accessToken);
|
||||
|
||||
// void removeToken(OAuth2RemoveTokenByUserReqDTO removeTokenDTO);
|
||||
/**
|
||||
* 移除访问令牌
|
||||
*
|
||||
* @param accessToken 访问令牌
|
||||
* @return 访问令牌的信息
|
||||
*/
|
||||
OAuth2AccessTokenRespDTO removeAccessToken(String accessToken);
|
||||
|
||||
/**
|
||||
* 刷新访问令牌
|
||||
*
|
||||
* @param refreshToken 刷新令牌
|
||||
* @param clientId 客户端编号
|
||||
* @return 访问令牌的信息
|
||||
*/
|
||||
OAuth2AccessTokenRespDTO refreshAccessToken(String refreshToken, Long clientId);
|
||||
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.system.api.auth.dto;
|
|||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.io.Serializable;
|
||||
|
@ -14,14 +13,13 @@ import java.io.Serializable;
|
|||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class OAuth2AccessTokenCreateReqDTO implements Serializable {
|
||||
|
||||
/**
|
||||
* 用户编号
|
||||
*/
|
||||
@NotNull(message = "用户编号不能为空")
|
||||
private Integer userId;
|
||||
private Long userId;
|
||||
/**
|
||||
* 用户类型
|
||||
*/
|
||||
|
|
|
@ -26,7 +26,7 @@ public class OAuth2AccessTokenRespDTO implements Serializable {
|
|||
/**
|
||||
* 用户编号
|
||||
*/
|
||||
private Integer userId;
|
||||
private Long userId;
|
||||
/**
|
||||
* 用户类型
|
||||
*/
|
||||
|
|
|
@ -4,6 +4,7 @@ import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenCheckRespDTO
|
|||
import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenCreateReqDTO;
|
||||
import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenRespDTO;
|
||||
import cn.iocoder.yudao.module.system.convert.auth.OAuth2TokenConvert;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO;
|
||||
import cn.iocoder.yudao.module.system.service.auth.OAuth2TokenService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
|
@ -22,7 +23,9 @@ public class OAuth2TokenApiImpl implements OAuth2TokenApi {
|
|||
|
||||
@Override
|
||||
public OAuth2AccessTokenRespDTO createAccessToken(OAuth2AccessTokenCreateReqDTO reqDTO) {
|
||||
return null;
|
||||
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken(
|
||||
reqDTO.getUserId(), reqDTO.getUserType(), reqDTO.getClientId());
|
||||
return OAuth2TokenConvert.INSTANCE.convert2(accessTokenDO);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -30,4 +33,16 @@ public class OAuth2TokenApiImpl implements OAuth2TokenApi {
|
|||
return OAuth2TokenConvert.INSTANCE.convert(oauth2TokenService.checkAccessToken(accessToken));
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuth2AccessTokenRespDTO removeAccessToken(String accessToken) {
|
||||
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.removeAccessToken(accessToken);
|
||||
return OAuth2TokenConvert.INSTANCE.convert2(accessTokenDO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuth2AccessTokenRespDTO refreshAccessToken(String refreshToken, Long clientId) {
|
||||
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, clientId);
|
||||
return OAuth2TokenConvert.INSTANCE.convert2(accessTokenDO);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import cn.iocoder.yudao.module.system.convert.auth.AuthConvert;
|
|||
import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
|
||||
import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum;
|
||||
import cn.iocoder.yudao.module.system.enums.permission.MenuTypeEnum;
|
||||
import cn.iocoder.yudao.module.system.service.auth.AdminAuthService;
|
||||
import cn.iocoder.yudao.module.system.service.permission.PermissionService;
|
||||
|
@ -70,14 +71,15 @@ public class AuthController {
|
|||
public CommonResult<Boolean> logout(HttpServletRequest request) {
|
||||
String token = obtainAuthorization(request, securityProperties.getTokenHeader());
|
||||
if (StrUtil.isNotBlank(token)) {
|
||||
authService.logout(token);
|
||||
authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType());
|
||||
}
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/refresh-token")
|
||||
@ApiOperation("刷新令牌")
|
||||
@OperateLog(enable = false) // 避免 Post 请求被记录操作日志 TODO 接口文档
|
||||
@ApiImplicitParam(name = "refreshToken", value = "刷新令牌", required = true, dataTypeClass = String.class)
|
||||
@OperateLog(enable = false) // 避免 Post 请求被记录操作日志
|
||||
public CommonResult<AuthLoginRespVO> refreshToken(@RequestParam("refreshToken") String refreshToken) {
|
||||
return success(authService.refreshToken(refreshToken));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
package cn.iocoder.yudao.module.system.controller.admin.auth;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.token.OAuth2AccessTokenPageReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.token.OAuth2AccessTokenRespVO;
|
||||
import cn.iocoder.yudao.module.system.convert.auth.OAuth2TokenConvert;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO;
|
||||
import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum;
|
||||
import cn.iocoder.yudao.module.system.service.auth.AdminAuthService;
|
||||
import cn.iocoder.yudao.module.system.service.auth.OAuth2TokenService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Api(tags = "管理后台 - OAuth2.0 令牌")
|
||||
@RestController
|
||||
@RequestMapping("/system/oauth2-token")
|
||||
public class OAuth2TokenController {
|
||||
|
||||
@Resource
|
||||
private OAuth2TokenService oauth2TokenService;
|
||||
@Resource
|
||||
private AdminAuthService authService;
|
||||
|
||||
@GetMapping("/page")
|
||||
@ApiOperation(value = "获得访问令牌分页", notes = "只返回有效期内的")
|
||||
@PreAuthorize("@ss.hasPermission('system:oauth2-token:page')")
|
||||
public CommonResult<PageResult<OAuth2AccessTokenRespVO>> getAccessTokenPage(@Valid OAuth2AccessTokenPageReqVO reqVO) {
|
||||
PageResult<OAuth2AccessTokenDO> pageResult = oauth2TokenService.getAccessTokenPage(reqVO);
|
||||
return success(OAuth2TokenConvert.INSTANCE.convert(pageResult));
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@ApiOperation("删除访问令牌")
|
||||
@ApiImplicitParam(name = "accessToken", value = "访问令牌", required = true, dataTypeClass = String.class, example = "tudou")
|
||||
@PreAuthorize("@ss.hasPermission('system:oauth2-token:delete')")
|
||||
public CommonResult<Boolean> deleteAccessToken(@RequestParam("accessToken") String accessToken) {
|
||||
authService.logout(accessToken, LoginLogTypeEnum.LOGOUT_DELETE.getType());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package cn.iocoder.yudao.module.system.controller.admin.auth.vo.token;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@ApiModel("管理后台 - 访问令牌分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class OAuth2AccessTokenPageReqVO extends PageParam {
|
||||
|
||||
@ApiModelProperty(value = "用户编号", required = true, example = "666")
|
||||
private Long userId;
|
||||
|
||||
@ApiModelProperty(value = "用户类型", required = true, example = "2", notes = "参见 UserTypeEnum 枚举")
|
||||
private Integer userType;
|
||||
|
||||
@ApiModelProperty(value = "客户端编号", required = true, example = "2")
|
||||
private Long clientId;
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package cn.iocoder.yudao.module.system.controller.admin.auth.vo.token;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@ApiModel("管理后台 - 访问令牌 Response VO")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class OAuth2AccessTokenRespVO {
|
||||
|
||||
@ApiModelProperty(value = "编号", required = true, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@ApiModelProperty(value = "访问令牌", required = true, example = "tudou")
|
||||
private String accessToken;
|
||||
|
||||
@ApiModelProperty(value = "刷新令牌", required = true, example = "nice")
|
||||
private String refreshToken;
|
||||
|
||||
@ApiModelProperty(value = "用户编号", required = true, example = "666")
|
||||
private Long userId;
|
||||
|
||||
@ApiModelProperty(value = "用户类型", required = true, example = "2", notes = "参见 UserTypeEnum 枚举")
|
||||
private Integer userType;
|
||||
|
||||
@ApiModelProperty(value = "客户端编号", required = true, example = "2")
|
||||
private Long clientId;
|
||||
|
||||
@ApiModelProperty(value = "创建时间", required = true)
|
||||
private Date createTime;
|
||||
|
||||
@ApiModelProperty(value = "过期时间", required = true)
|
||||
private Date expiresTime;
|
||||
|
||||
}
|
|
@ -1,6 +1,9 @@
|
|||
package cn.iocoder.yudao.module.system.convert.auth;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenCheckRespDTO;
|
||||
import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenRespDTO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.token.OAuth2AccessTokenRespVO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
@ -12,4 +15,8 @@ public interface OAuth2TokenConvert {
|
|||
|
||||
OAuth2AccessTokenCheckRespDTO convert(OAuth2AccessTokenDO bean);
|
||||
|
||||
PageResult<OAuth2AccessTokenRespVO> convert(PageResult<OAuth2AccessTokenDO> page);
|
||||
|
||||
OAuth2AccessTokenRespDTO convert2(OAuth2AccessTokenDO bean);
|
||||
|
||||
}
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
package cn.iocoder.yudao.module.system.dal.mysql.auth;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.token.OAuth2AccessTokenPageReqVO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
|
@ -17,4 +21,13 @@ public interface OAuth2AccessTokenMapper extends BaseMapperX<OAuth2AccessTokenDO
|
|||
return selectList(OAuth2AccessTokenDO::getRefreshToken, refreshToken);
|
||||
}
|
||||
|
||||
default PageResult<OAuth2AccessTokenDO> selectPage(OAuth2AccessTokenPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<OAuth2AccessTokenDO>()
|
||||
.eqIfPresent(OAuth2AccessTokenDO::getUserId, reqVO.getUserId())
|
||||
.eqIfPresent(OAuth2AccessTokenDO::getUserType, reqVO.getUserType())
|
||||
.eqIfPresent(OAuth2AccessTokenDO::getClientId, reqVO.getClientId())
|
||||
.gt(OAuth2AccessTokenDO::getExpiresTime, new Date())
|
||||
.orderByDesc(OAuth2AccessTokenDO::getId));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -25,8 +25,9 @@ public interface AdminAuthService {
|
|||
* 基于 token 退出登录
|
||||
*
|
||||
* @param token token
|
||||
* @param logType 登出类型
|
||||
*/
|
||||
void logout(String token);
|
||||
void logout(String token, Integer logType);
|
||||
|
||||
/**
|
||||
* 短信验证码发送
|
||||
|
|
|
@ -18,6 +18,7 @@ import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum;
|
|||
import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum;
|
||||
import cn.iocoder.yudao.module.system.service.common.CaptchaService;
|
||||
import cn.iocoder.yudao.module.system.service.logger.LoginLogService;
|
||||
import cn.iocoder.yudao.module.system.service.member.MemberService;
|
||||
import cn.iocoder.yudao.module.system.service.social.SocialUserService;
|
||||
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
@ -51,6 +52,8 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
|||
private OAuth2TokenService oauth2TokenService;
|
||||
@Resource
|
||||
private SocialUserService socialUserService;
|
||||
@Resource
|
||||
private MemberService memberService;
|
||||
|
||||
@Resource
|
||||
private Validator validator;
|
||||
|
@ -209,23 +212,27 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void logout(String token) {
|
||||
public void logout(String token, Integer logType) {
|
||||
// 删除访问令牌
|
||||
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.removeAccessToken(token);
|
||||
if (accessTokenDO == null) {
|
||||
return;
|
||||
}
|
||||
// 删除成功,则记录登出日志
|
||||
createLogoutLog(accessTokenDO.getUserId());
|
||||
createLogoutLog(accessTokenDO.getUserId(), accessTokenDO.getUserType(), logType);
|
||||
}
|
||||
|
||||
private void createLogoutLog(Long userId) {
|
||||
private void createLogoutLog(Long userId, Integer userType, Integer logType) {
|
||||
LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO();
|
||||
reqDTO.setLogType(LoginLogTypeEnum.LOGOUT_SELF.getType());
|
||||
reqDTO.setLogType(logType);
|
||||
reqDTO.setTraceId(TracerUtils.getTraceId());
|
||||
reqDTO.setUserId(userId);
|
||||
reqDTO.setUsername(getUsername(userId));
|
||||
reqDTO.setUserType(getUserType().getValue());
|
||||
reqDTO.setUserType(userType);
|
||||
if (ObjectUtil.notEqual(getUserType(), userType)) {
|
||||
reqDTO.setUsername(getUsername(userId));
|
||||
} else {
|
||||
reqDTO.setUsername(memberService.getMemberUserMobile(userId));
|
||||
}
|
||||
reqDTO.setUserAgent(ServletUtils.getUserAgent());
|
||||
reqDTO.setUserIp(ServletUtils.getClientIP());
|
||||
reqDTO.setResult(LoginResultEnum.SUCCESS.getResult());
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package cn.iocoder.yudao.module.system.service.auth;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.token.OAuth2AccessTokenPageReqVO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO;
|
||||
|
||||
/**
|
||||
|
@ -64,4 +66,12 @@ public interface OAuth2TokenService {
|
|||
*/
|
||||
OAuth2AccessTokenDO removeAccessToken(String accessToken);
|
||||
|
||||
/**
|
||||
* 获得访问令牌分页
|
||||
*
|
||||
* @param reqVO 请求
|
||||
* @return 访问令牌分页
|
||||
*/
|
||||
PageResult<OAuth2AccessTokenDO> getAccessTokenPage(OAuth2AccessTokenPageReqVO reqVO);
|
||||
|
||||
}
|
||||
|
|
|
@ -4,8 +4,10 @@ import cn.hutool.core.collection.CollUtil;
|
|||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
|
||||
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.token.OAuth2AccessTokenPageReqVO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2RefreshTokenDO;
|
||||
|
@ -125,6 +127,11 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
|
|||
return accessTokenDO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<OAuth2AccessTokenDO> getAccessTokenPage(OAuth2AccessTokenPageReqVO reqVO) {
|
||||
return oauth2AccessTokenMapper.selectPage(reqVO);
|
||||
}
|
||||
|
||||
private OAuth2AccessTokenDO createOAuth2AccessToken(OAuth2RefreshTokenDO refreshTokenDO, OAuth2ClientDO clientDO) {
|
||||
OAuth2AccessTokenDO accessTokenDO = new OAuth2AccessTokenDO().setAccessToken(generateAccessToken())
|
||||
.setUserId(refreshTokenDO.getUserId()).setUserType(refreshTokenDO.getUserType()).setClientId(clientDO.getId())
|
||||
|
|
|
@ -221,7 +221,7 @@ public class AuthServiceImplTest extends BaseDbUnitTest {
|
|||
when(oauth2TokenService.removeAccessToken(eq(token))).thenReturn(accessTokenDO);
|
||||
|
||||
// 调用
|
||||
authService.logout(token);
|
||||
authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType());
|
||||
// 校验调用参数
|
||||
verify(loginLogService).createLoginLog(argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGOUT_SELF.getType())
|
||||
&& o.getResult().equals(LoginResultEnum.SUCCESS.getResult()))
|
||||
|
@ -234,7 +234,7 @@ public class AuthServiceImplTest extends BaseDbUnitTest {
|
|||
String token = randomString();
|
||||
|
||||
// 调用
|
||||
authService.logout(token);
|
||||
authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType());
|
||||
// 校验调用参数
|
||||
verify(loginLogService, never()).createLoginLog(any());
|
||||
}
|
||||
|
|
|
@ -21,11 +21,11 @@
|
|||
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
|
||||
|
||||
<dependencies>
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>cn.iocoder.boot</groupId>-->
|
||||
<!-- <artifactId>yudao-module-member-biz</artifactId>-->
|
||||
<!-- <version>${revision}</version>-->
|
||||
<!-- </dependency>-->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-module-member-biz</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-module-system-biz</artifactId>
|
||||
|
|
|
@ -173,8 +173,6 @@ wx: # 参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-sta
|
|||
yudao:
|
||||
security:
|
||||
token-header: Authorization
|
||||
token-timeout: 1d
|
||||
session-timeout: 30m
|
||||
mock-enable: true
|
||||
mock-secret: test
|
||||
xss:
|
||||
|
|
|
@ -193,8 +193,6 @@ yudao:
|
|||
enable: false # 本地环境,暂时关闭图片验证码,方便登录等接口的测试
|
||||
security:
|
||||
token-header: Authorization
|
||||
token-timeout: 1d
|
||||
session-timeout: 1d
|
||||
mock-enable: true
|
||||
mock-secret: test
|
||||
xss:
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 获得访问令牌分页
|
||||
export function getAccessTokenPage(query) {
|
||||
return request({
|
||||
url: '/system/oauth2-token/page',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 删除访问令牌
|
||||
export function deleteAccessToken(accessToken) {
|
||||
return request({
|
||||
url: '/system/oauth2-token/delete?accessToken=' + accessToken,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询在线用户列表
|
||||
export function list(query) {
|
||||
return request({
|
||||
url: '/system/user-session/page',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 强退用户
|
||||
export function forceLogout(tokenId) {
|
||||
return request({
|
||||
url: '/system/user-session/delete?id=' + tokenId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
|
@ -3,11 +3,14 @@
|
|||
<doc-alert title="用户体系" url="https://doc.iocoder.cn/user-center/" />
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" label-width="68px">
|
||||
<el-form-item label="登录地址" prop="userIp">
|
||||
<el-input v-model="queryParams.userIp" placeholder="请输入登录地址" clearable @keyup.enter.native="handleQuery"/>
|
||||
<el-form-item label="用户编号" prop="userId">
|
||||
<el-input v-model="queryParams.userId" placeholder="请输入用户编号" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="用户名称" prop="username">
|
||||
<el-input v-model="queryParams.username" placeholder="请输入用户名称" clearable @keyup.enter.native="handleQuery"/>
|
||||
<el-form-item label="用户类型" prop="userType">
|
||||
<el-select v-model="queryParams.userType" placeholder="请选择用户类型" clearable>
|
||||
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.USER_TYPE)"
|
||||
:key="dict.value" :label="dict.label" :value="dict.value"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
|
||||
|
@ -16,20 +19,28 @@
|
|||
|
||||
</el-form>
|
||||
<el-table v-loading="loading" :data="list" style="width: 100%;">
|
||||
<el-table-column label="会话编号" align="center" prop="id" width="300" />
|
||||
<el-table-column label="登录名称" align="center" prop="username" width="100" />
|
||||
<el-table-column label="部门名称" align="center" prop="deptName" width="100" />
|
||||
<el-table-column label="登录地址" align="center" prop="userIp" width="100" />
|
||||
<el-table-column label="userAgent" align="center" prop="userAgent" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="登录时间" align="center" prop="createTime" width="180">
|
||||
<el-table-column label="访问令牌" align="center" prop="accessToken" width="300" />
|
||||
<el-table-column label="刷新令牌" align="center" prop="refreshToken" width="300" />
|
||||
<el-table-column label="用户编号" align="center" prop="userId" />
|
||||
<el-table-column label="用户类型" align="center" prop="userType" width="100">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :type="DICT_TYPE.USER_TYPE" :value="scope.row.userType"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="过期时间" align="center" prop="expiresTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.expiresTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleForceLogout(scope.row)"
|
||||
v-hasPermi="['system:user-session:delete']">强退</el-button>
|
||||
v-hasPermi="['system:oauth2-token:delete']">强退</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
@ -40,10 +51,10 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { list, forceLogout } from "@/api/system/session";
|
||||
import { getAccessTokenPage, deleteAccessToken } from "@/api/system/oauth2/oauth2Token";
|
||||
|
||||
export default {
|
||||
name: "Online",
|
||||
name: "OAuth2Token",
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
|
@ -56,8 +67,8 @@ export default {
|
|||
queryParams: {
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
userIp: undefined,
|
||||
username: undefined
|
||||
userId: undefined,
|
||||
userType: undefined
|
||||
}
|
||||
};
|
||||
},
|
||||
|
@ -68,7 +79,7 @@ export default {
|
|||
/** 查询登录日志列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
list(this.queryParams).then(response => {
|
||||
getAccessTokenPage(this.queryParams).then(response => {
|
||||
this.list = response.data.list;
|
||||
this.total = response.data.total;
|
||||
this.loading = false;
|
||||
|
@ -86,8 +97,8 @@ export default {
|
|||
},
|
||||
/** 强退按钮操作 */
|
||||
handleForceLogout(row) {
|
||||
this.$modal.confirm('是否确认强退名称为"' + row.username + '"的数据项?').then(function() {
|
||||
return forceLogout(row.id);
|
||||
this.$modal.confirm('是否确认强退令牌为"' + row.accessToken + '"的数据项?').then(function() {
|
||||
return deleteAccessToken(row.accessToken);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("强退成功");
|
Loading…
Reference in New Issue