!166 用户 Token 采用 OAuth2 的 Access Token + Refresh Token,提升安全性
Merge pull request !166 from 芋道源码/feature/1.6.2plp
commit
190150d1f4
@ -1,43 +0,0 @@
|
||||
package cn.iocoder.yudao.framework.security.core.authentication;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||
import lombok.Getter;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* 支持多用户的 UsernamePasswordAuthenticationToken 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Getter
|
||||
public class MultiUsernamePasswordAuthenticationToken extends UsernamePasswordAuthenticationToken {
|
||||
|
||||
/**
|
||||
* 用户类型
|
||||
*/
|
||||
private UserTypeEnum userType;
|
||||
|
||||
public MultiUsernamePasswordAuthenticationToken(Object principal, Object credentials) {
|
||||
super(principal, credentials);
|
||||
}
|
||||
|
||||
public MultiUsernamePasswordAuthenticationToken(Object principal, Object credentials,
|
||||
Collection<? extends GrantedAuthority> authorities) {
|
||||
super(principal, credentials, authorities);
|
||||
}
|
||||
|
||||
public MultiUsernamePasswordAuthenticationToken(Object principal, Object credentials, UserTypeEnum userType) {
|
||||
super(principal, credentials);
|
||||
this.userType = userType;
|
||||
}
|
||||
|
||||
public MultiUsernamePasswordAuthenticationToken(Object principal, Object credentials,
|
||||
Collection<? extends GrantedAuthority> authorities, UserTypeEnum userType) {
|
||||
super(principal, credentials, authorities);
|
||||
this.userType = userType;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,40 +0,0 @@
|
||||
package cn.iocoder.yudao.framework.security.core.handler;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
|
||||
import cn.iocoder.yudao.framework.security.config.SecurityProperties;
|
||||
import cn.iocoder.yudao.framework.security.core.authentication.MultiUserDetailsAuthenticationProvider;
|
||||
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
|
||||
/**
|
||||
* 自定义退出处理器
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler {
|
||||
|
||||
private final SecurityProperties securityProperties;
|
||||
|
||||
private final MultiUserDetailsAuthenticationProvider authenticationProvider;
|
||||
|
||||
@Override
|
||||
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
|
||||
// 执行退出
|
||||
String token = SecurityFrameworkUtils.obtainAuthorization(request, securityProperties.getTokenHeader());
|
||||
if (StrUtil.isNotBlank(token)) {
|
||||
authenticationProvider.logout(request, token);
|
||||
}
|
||||
// 返回成功
|
||||
ServletUtils.writeJSON(response, CommonResult.success(null));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
/**
|
||||
* 属于 system 模块的 framework 封装
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
package cn.iocoder.yudao.module.member.framework;
|
||||
@ -0,0 +1,28 @@
|
||||
package cn.iocoder.yudao.module.member.framework.security.config;
|
||||
|
||||
import cn.iocoder.yudao.framework.security.config.AuthorizeRequestsCustomizer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
|
||||
|
||||
/**
|
||||
* Member 模块的 Security 配置
|
||||
*/
|
||||
@Configuration("memberSecurityConfiguration")
|
||||
public class SecurityConfiguration {
|
||||
|
||||
@Bean("memberAuthorizeRequestsCustomizer")
|
||||
public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() {
|
||||
return new AuthorizeRequestsCustomizer() {
|
||||
|
||||
@Override
|
||||
public void customize(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry) {
|
||||
// 登录的接口
|
||||
registry.antMatchers(buildAdminApi("/member/auth/logout")).permitAll();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,4 @@
|
||||
/**
|
||||
* 占位
|
||||
*/
|
||||
package cn.iocoder.yudao.module.member.framework.security.core;
|
||||
@ -0,0 +1,49 @@
|
||||
package cn.iocoder.yudao.module.system.api.auth;
|
||||
|
||||
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 javax.validation.Valid;
|
||||
|
||||
/**
|
||||
* OAuth2.0 Token API 接口
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface OAuth2TokenApi {
|
||||
|
||||
/**
|
||||
* 创建访问令牌
|
||||
*
|
||||
* @param reqDTO 访问令牌的创建信息
|
||||
* @return 访问令牌的信息
|
||||
*/
|
||||
OAuth2AccessTokenRespDTO createAccessToken(@Valid OAuth2AccessTokenCreateReqDTO reqDTO);
|
||||
|
||||
/**
|
||||
* 校验访问令牌
|
||||
*
|
||||
* @param accessToken 访问令牌
|
||||
* @return 访问令牌的信息
|
||||
*/
|
||||
OAuth2AccessTokenCheckRespDTO checkAccessToken(String accessToken);
|
||||
|
||||
/**
|
||||
* 移除访问令牌
|
||||
*
|
||||
* @param accessToken 访问令牌
|
||||
* @return 访问令牌的信息
|
||||
*/
|
||||
OAuth2AccessTokenRespDTO removeAccessToken(String accessToken);
|
||||
|
||||
/**
|
||||
* 刷新访问令牌
|
||||
*
|
||||
* @param refreshToken 刷新令牌
|
||||
* @param clientId 客户端编号
|
||||
* @return 访问令牌的信息
|
||||
*/
|
||||
OAuth2AccessTokenRespDTO refreshAccessToken(String refreshToken, Long clientId);
|
||||
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
package cn.iocoder.yudao.module.system.api.auth.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* OAuth2.0 访问令牌的校验 Response DTO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
public class OAuth2AccessTokenCheckRespDTO implements Serializable {
|
||||
|
||||
/**
|
||||
* 用户编号
|
||||
*/
|
||||
private Long userId;
|
||||
/**
|
||||
* 用户类型
|
||||
*/
|
||||
private Integer userType;
|
||||
/**
|
||||
* 租户编号
|
||||
*/
|
||||
private Long tenantId;
|
||||
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
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 javax.validation.constraints.NotNull;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* OAuth2.0 访问令牌创建 Request DTO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
public class OAuth2AccessTokenCreateReqDTO implements Serializable {
|
||||
|
||||
/**
|
||||
* 用户编号
|
||||
*/
|
||||
@NotNull(message = "用户编号不能为空")
|
||||
private Long userId;
|
||||
/**
|
||||
* 用户类型
|
||||
*/
|
||||
@NotNull(message = "用户类型不能为空")
|
||||
@InEnum(value = UserTypeEnum.class, message = "用户类型必须是 {value}")
|
||||
private Integer userType;
|
||||
/**
|
||||
* 客户端编号
|
||||
*/
|
||||
@NotNull(message = "客户端编号不能为空")
|
||||
private Long clientId;
|
||||
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
package cn.iocoder.yudao.module.system.api.auth.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* OAuth2.0 访问令牌的信息 Response DTO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class OAuth2AccessTokenRespDTO implements Serializable {
|
||||
|
||||
/**
|
||||
* 访问令牌
|
||||
*/
|
||||
private String accessToken;
|
||||
/**
|
||||
* 刷新令牌
|
||||
*/
|
||||
private String refreshToken;
|
||||
/**
|
||||
* 用户编号
|
||||
*/
|
||||
private Long userId;
|
||||
/**
|
||||
* 用户类型
|
||||
*/
|
||||
private Integer userType;
|
||||
/**
|
||||
* 过期时间
|
||||
*/
|
||||
private Date expiresTime;
|
||||
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
package cn.iocoder.yudao.module.system.enums.auth;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* OAuth2.0 客户端的编号枚举
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum OAuth2ClientIdEnum {
|
||||
|
||||
DEFAULT(1L); // 系统默认
|
||||
|
||||
private final Long id;
|
||||
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
package cn.iocoder.yudao.module.system.api.auth;
|
||||
|
||||
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;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* OAuth2.0 Token API 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
public class OAuth2TokenApiImpl implements OAuth2TokenApi {
|
||||
|
||||
@Resource
|
||||
private OAuth2TokenService oauth2TokenService;
|
||||
|
||||
@Override
|
||||
public OAuth2AccessTokenRespDTO createAccessToken(OAuth2AccessTokenCreateReqDTO reqDTO) {
|
||||
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken(
|
||||
reqDTO.getUserId(), reqDTO.getUserType(), reqDTO.getClientId());
|
||||
return OAuth2TokenConvert.INSTANCE.convert2(accessTokenDO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuth2AccessTokenCheckRespDTO checkAccessToken(String accessToken) {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,47 +0,0 @@
|
||||
package cn.iocoder.yudao.module.system.api.auth;
|
||||
|
||||
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
||||
import cn.iocoder.yudao.module.system.service.auth.UserSessionService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 在线用户 Session API 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class UserSessionApiImpl implements UserSessionApi {
|
||||
|
||||
@Resource
|
||||
private UserSessionService userSessionService;
|
||||
|
||||
@Override
|
||||
public String createUserSession(LoginUser loginUser, String userIp, String userAgent) {
|
||||
return userSessionService.createUserSession(loginUser, userIp, userAgent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshUserSession(String token, LoginUser loginUser) {
|
||||
userSessionService.refreshUserSession(token, loginUser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteUserSession(String token) {
|
||||
userSessionService.deleteUserSession(token);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoginUser getLoginUser(String token) {
|
||||
return userSessionService.getLoginUser(token);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getSessionTimeoutMillis() {
|
||||
return userSessionService.getSessionTimeoutMillis();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,74 @@
|
||||
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.client.OAuth2ClientCreateReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2ClientPageReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2ClientRespVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2ClientUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.system.convert.auth.OAuth2ClientConvert;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO;
|
||||
import cn.iocoder.yudao.module.system.service.auth.OAuth2ClientService;
|
||||
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.validation.annotation.Validated;
|
||||
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 客户端")
|
||||
@RestController
|
||||
@RequestMapping("/system/oauth2-client")
|
||||
@Validated
|
||||
public class OAuth2ClientController {
|
||||
|
||||
@Resource
|
||||
private OAuth2ClientService oAuth2ClientService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@ApiOperation("创建 OAuth2 客户端")
|
||||
@PreAuthorize("@ss.hasPermission('system:oauth2-client:create')")
|
||||
public CommonResult<Long> createOAuth2Client(@Valid @RequestBody OAuth2ClientCreateReqVO createReqVO) {
|
||||
return success(oAuth2ClientService.createOAuth2Client(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@ApiOperation("更新 OAuth2 客户端")
|
||||
@PreAuthorize("@ss.hasPermission('system:oauth2-client:update')")
|
||||
public CommonResult<Boolean> updateOAuth2Client(@Valid @RequestBody OAuth2ClientUpdateReqVO updateReqVO) {
|
||||
oAuth2ClientService.updateOAuth2Client(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@ApiOperation("删除 OAuth2 客户端")
|
||||
@ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class)
|
||||
@PreAuthorize("@ss.hasPermission('system:oauth2-client:delete')")
|
||||
public CommonResult<Boolean> deleteOAuth2Client(@RequestParam("id") Long id) {
|
||||
oAuth2ClientService.deleteOAuth2Client(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@ApiOperation("获得 OAuth2 客户端")
|
||||
@ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
|
||||
@PreAuthorize("@ss.hasPermission('system:oauth2-client:query')")
|
||||
public CommonResult<OAuth2ClientRespVO> getOAuth2Client(@RequestParam("id") Long id) {
|
||||
OAuth2ClientDO oAuth2Client = oAuth2ClientService.getOAuth2Client(id);
|
||||
return success(OAuth2ClientConvert.INSTANCE.convert(oAuth2Client));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@ApiOperation("获得OAuth2 客户端分页")
|
||||
@PreAuthorize("@ss.hasPermission('system:oauth2-client:query')")
|
||||
public CommonResult<PageResult<OAuth2ClientRespVO>> getOAuth2ClientPage(@Valid OAuth2ClientPageReqVO pageVO) {
|
||||
PageResult<OAuth2ClientDO> pageResult = oAuth2ClientService.getOAuth2ClientPage(pageVO);
|
||||
return success(OAuth2ClientConvert.INSTANCE.convertPage(pageResult));
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,79 +0,0 @@
|
||||
package cn.iocoder.yudao.module.system.controller.admin.auth;
|
||||
|
||||
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.session.UserSessionPageItemRespVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.session.UserSessionPageReqVO;
|
||||
import cn.iocoder.yudao.module.system.convert.auth.UserSessionConvert;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.auth.UserSessionDO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
|
||||
import cn.iocoder.yudao.module.system.service.auth.UserSessionService;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
||||
import cn.iocoder.yudao.module.system.service.dept.DeptService;
|
||||
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
|
||||
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.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
|
||||
@Api(tags = "管理后台 - 用户 Session")
|
||||
@RestController
|
||||
@RequestMapping("/system/user-session")
|
||||
public class UserSessionController {
|
||||
|
||||
@Resource
|
||||
private UserSessionService userSessionService;
|
||||
@Resource
|
||||
private AdminUserService userService;
|
||||
|
||||
@Resource
|
||||
private DeptService deptService;
|
||||
|
||||
@GetMapping("/page")
|
||||
@ApiOperation("获得 Session 分页列表")
|
||||
@PreAuthorize("@ss.hasPermission('system:user-session:page')")
|
||||
public CommonResult<PageResult<UserSessionPageItemRespVO>> getUserSessionPage(@Validated UserSessionPageReqVO reqVO) {
|
||||
// 获得 Session 分页
|
||||
PageResult<UserSessionDO> pageResult = userSessionService.getUserSessionPage(reqVO);
|
||||
|
||||
// 获得拼接需要的数据
|
||||
Map<Long, AdminUserDO> userMap = userService.getUserMap(
|
||||
convertList(pageResult.getList(), UserSessionDO::getUserId));
|
||||
Map<Long, DeptDO> deptMap = deptService.getDeptMap(
|
||||
convertList(userMap.values(), AdminUserDO::getDeptId));
|
||||
// 拼接结果返回
|
||||
List<UserSessionPageItemRespVO> sessionList = new ArrayList<>(pageResult.getList().size());
|
||||
pageResult.getList().forEach(session -> {
|
||||
UserSessionPageItemRespVO respVO = UserSessionConvert.INSTANCE.convert(session);
|
||||
sessionList.add(respVO);
|
||||
// 设置用户账号
|
||||
MapUtils.findAndThen(userMap, session.getUserId(), user -> {
|
||||
respVO.setUsername(user.getUsername());
|
||||
// 设置用户部门
|
||||
MapUtils.findAndThen(deptMap, user.getDeptId(), dept -> respVO.setDeptName(dept.getName()));
|
||||
});
|
||||
});
|
||||
return success(new PageResult<>(sessionList, pageResult.getTotal()));
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@ApiOperation("删除 Session")
|
||||
@ApiImplicitParam(name = "id", value = "Session 编号", required = true, dataTypeClass = Long.class, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('system:user-session:delete')")
|
||||
public CommonResult<Boolean> deleteUserSession(@RequestParam("id") Long id) {
|
||||
userSessionService.deleteUserSession(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
package cn.iocoder.yudao.module.system.controller.admin.auth.vo.client;
|
||||
|
||||
import lombok.*;
|
||||
import io.swagger.annotations.*;
|
||||
|
||||
@ApiModel("管理后台 - OAuth2 客户端创建 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class OAuth2ClientCreateReqVO extends OAuth2ClientBaseVO {
|
||||
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package cn.iocoder.yudao.module.system.controller.admin.auth.vo.client;
|
||||
|
||||
import lombok.*;
|
||||
import io.swagger.annotations.*;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
|
||||
@ApiModel("管理后台 - OAuth2 客户端分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class OAuth2ClientPageReqVO extends PageParam {
|
||||
|
||||
@ApiModelProperty(value = "应用名")
|
||||
private String name;
|
||||
|
||||
@ApiModelProperty(value = "状态")
|
||||
private Integer status;
|
||||
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package cn.iocoder.yudao.module.system.controller.admin.auth.vo.client;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@ApiModel("管理后台 - OAuth2 客户端 Response VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class OAuth2ClientRespVO extends OAuth2ClientBaseVO {
|
||||
|
||||
@ApiModelProperty(value = "创建时间", required = true)
|
||||
private Date createTime;
|
||||
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
package cn.iocoder.yudao.module.system.controller.admin.auth.vo.client;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
@ApiModel("管理后台 - OAuth2 客户端更新 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class OAuth2ClientUpdateReqVO extends OAuth2ClientBaseVO {
|
||||
|
||||
}
|
||||
@ -1,38 +0,0 @@
|
||||
package cn.iocoder.yudao.module.system.controller.admin.auth.vo.session;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@ApiModel(value = "管理后台 - 用户在线 Session Response VO", description = "相比用户基本信息来说,会多部门、用户账号等信息")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class UserSessionPageItemRespVO extends PageParam {
|
||||
|
||||
@ApiModelProperty(value = "Session 编号", required = true, example = "fe50b9f6-d177-44b1-8da9-72ea34f63db7")
|
||||
private String id;
|
||||
|
||||
@ApiModelProperty(value = "用户 IP", required = true, example = "127.0.0.1")
|
||||
private String userIp;
|
||||
|
||||
@ApiModelProperty(value = "浏览器 UserAgent", required = true, example = "Mozilla/5.0")
|
||||
private String userAgent;
|
||||
|
||||
@ApiModelProperty(value = "登录时间", required = true)
|
||||
private Date createTime;
|
||||
|
||||
@ApiModelProperty(value = "用户账号", required = true, example = "yudao")
|
||||
private String username;
|
||||
|
||||
@ApiModelProperty(value = "部门名称", example = "研发部")
|
||||
private String deptName;
|
||||
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
package cn.iocoder.yudao.module.system.controller.admin.auth.vo.session;
|
||||
|
||||
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("管理后台 - 在线用户 Session 分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class UserSessionPageReqVO extends PageParam {
|
||||
|
||||
@ApiModelProperty(value = "用户 IP", example = "127.0.0.1", notes = "模糊匹配")
|
||||
private String userIp;
|
||||
|
||||
@ApiModelProperty(value = "用户账号", example = "yudao", notes = "模糊匹配")
|
||||
private String username;
|
||||
|
||||
}
|
||||
@ -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;
|
||||
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
package cn.iocoder.yudao.module.system.convert.auth;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2ClientCreateReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2ClientRespVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2ClientUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* OAuth2 客户端 Convert
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Mapper
|
||||
public interface OAuth2ClientConvert {
|
||||
|
||||
OAuth2ClientConvert INSTANCE = Mappers.getMapper(OAuth2ClientConvert.class);
|
||||
|
||||
OAuth2ClientDO convert(OAuth2ClientCreateReqVO bean);
|
||||
|
||||
OAuth2ClientDO convert(OAuth2ClientUpdateReqVO bean);
|
||||
|
||||
OAuth2ClientRespVO convert(OAuth2ClientDO bean);
|
||||
|
||||
List<OAuth2ClientRespVO> convertList(List<OAuth2ClientDO> list);
|
||||
|
||||
PageResult<OAuth2ClientRespVO> convertPage(PageResult<OAuth2ClientDO> page);
|
||||
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
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;
|
||||
|
||||
@Mapper
|
||||
public interface OAuth2TokenConvert {
|
||||
|
||||
OAuth2TokenConvert INSTANCE = Mappers.getMapper(OAuth2TokenConvert.class);
|
||||
|
||||
OAuth2AccessTokenCheckRespDTO convert(OAuth2AccessTokenDO bean);
|
||||
|
||||
PageResult<OAuth2AccessTokenRespVO> convert(PageResult<OAuth2AccessTokenDO> page);
|
||||
|
||||
OAuth2AccessTokenRespDTO convert2(OAuth2AccessTokenDO bean);
|
||||
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
package cn.iocoder.yudao.module.system.convert.auth;
|
||||
|
||||
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.session.UserSessionPageItemRespVO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.auth.UserSessionDO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper
|
||||
public interface UserSessionConvert {
|
||||
|
||||
UserSessionConvert INSTANCE = Mappers.getMapper(UserSessionConvert.class);
|
||||
|
||||
UserSessionPageItemRespVO convert(UserSessionDO session);
|
||||
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
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
|
||||
public interface OAuth2AccessTokenMapper extends BaseMapperX<OAuth2AccessTokenDO> {
|
||||
|
||||
default OAuth2AccessTokenDO selectByAccessToken(String accessToken) {
|
||||
return selectOne(OAuth2AccessTokenDO::getAccessToken, accessToken);
|
||||
}
|
||||
|
||||
default List<OAuth2AccessTokenDO> selectListByRefreshToken(String refreshToken) {
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
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.client.OAuth2ClientPageReqVO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* OAuth2 客户端 Mapper
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Mapper
|
||||
public interface OAuth2ClientMapper extends BaseMapperX<OAuth2ClientDO> {
|
||||
|
||||
default PageResult<OAuth2ClientDO> selectPage(OAuth2ClientPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<OAuth2ClientDO>()
|
||||
.likeIfPresent(OAuth2ClientDO::getName, reqVO.getName())
|
||||
.eqIfPresent(OAuth2ClientDO::getStatus, reqVO.getStatus())
|
||||
.orderByDesc(OAuth2ClientDO::getId));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package cn.iocoder.yudao.module.system.dal.mysql.auth;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2RefreshTokenDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface OAuth2RefreshTokenMapper extends BaseMapperX<OAuth2RefreshTokenDO> {
|
||||
|
||||
default int deleteByRefreshToken(String refreshToken) {
|
||||
return delete(new LambdaQueryWrapperX<OAuth2RefreshTokenDO>()
|
||||
.eq(OAuth2RefreshTokenDO::getRefreshToken, refreshToken));
|
||||
}
|
||||
|
||||
default OAuth2RefreshTokenDO selectByRefreshToken(String refreshToken) {
|
||||
return selectOne(OAuth2RefreshTokenDO::getRefreshToken, refreshToken);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,37 +0,0 @@
|
||||
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.session.UserSessionPageReqVO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.auth.UserSessionDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface UserSessionMapper extends BaseMapperX<UserSessionDO> {
|
||||
|
||||
default PageResult<UserSessionDO> selectPage(UserSessionPageReqVO reqVO, Collection<Long> userIds) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<UserSessionDO>()
|
||||
.inIfPresent(UserSessionDO::getUserId, userIds)
|
||||
.likeIfPresent(UserSessionDO::getUserIp, reqVO.getUserIp()));
|
||||
}
|
||||
|
||||
default List<UserSessionDO> selectListBySessionTimoutLt() {
|
||||
return selectList(new LambdaQueryWrapperX<UserSessionDO>()
|
||||
.lt(UserSessionDO::getSessionTimeout, new Date()));
|
||||
}
|
||||
|
||||
default void updateByToken(String token, UserSessionDO updateObj) {
|
||||
update(updateObj, new LambdaQueryWrapperX<UserSessionDO>()
|
||||
.eq(UserSessionDO::getToken, token));
|
||||
}
|
||||
|
||||
default void deleteByToken(String token) {
|
||||
delete(new LambdaQueryWrapperX<UserSessionDO>().eq(UserSessionDO::getToken, token));
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,52 +0,0 @@
|
||||
package cn.iocoder.yudao.module.system.dal.redis.auth;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
import cn.iocoder.yudao.framework.security.config.SecurityProperties;
|
||||
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import static cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants.LOGIN_USER;
|
||||
|
||||
/**
|
||||
* {@link LoginUser} 的 RedisDAO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Repository
|
||||
public class LoginUserRedisDAO {
|
||||
|
||||
@Resource
|
||||
private StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
@Resource
|
||||
private SecurityProperties securityProperties;
|
||||
|
||||
public LoginUser get(String token) {
|
||||
String redisKey = formatKey(token);
|
||||
return JsonUtils.parseObject(stringRedisTemplate.opsForValue().get(redisKey), LoginUser.class);
|
||||
}
|
||||
|
||||
public Boolean exists(String token) {
|
||||
String redisKey = formatKey(token);
|
||||
return stringRedisTemplate.hasKey(redisKey);
|
||||
}
|
||||
|
||||
public void set(String token, LoginUser loginUser) {
|
||||
String redisKey = formatKey(token);
|
||||
stringRedisTemplate.opsForValue().set(redisKey, JsonUtils.toJsonString(loginUser),
|
||||
securityProperties.getSessionTimeout());
|
||||
}
|
||||
|
||||
public void delete(String token) {
|
||||
String redisKey = formatKey(token);
|
||||
stringRedisTemplate.delete(redisKey);
|
||||
}
|
||||
|
||||
private static String formatKey(String token) {
|
||||
return LOGIN_USER.formatKey(token);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
package cn.iocoder.yudao.module.system.dal.redis.auth;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants.OAUTH2_ACCESS_TOKEN;
|
||||
|
||||
/**
|
||||
* {@link OAuth2AccessTokenDO} 的 RedisDAO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Repository
|
||||
public class OAuth2AccessTokenRedisDAO {
|
||||
|
||||
@Resource
|
||||
private StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
public OAuth2AccessTokenDO get(String accessToken) {
|
||||
String redisKey = formatKey(accessToken);
|
||||
return JsonUtils.parseObject(stringRedisTemplate.opsForValue().get(redisKey), OAuth2AccessTokenDO.class);
|
||||
}
|
||||
|
||||
public void set(OAuth2AccessTokenDO accessTokenDO) {
|
||||
String redisKey = formatKey(accessTokenDO.getAccessToken());
|
||||
// 清理多余字段,避免缓存
|
||||
accessTokenDO.setUpdater(null).setUpdateTime(null).setCreateTime(null).setCreator(null).setDeleted(null);
|
||||
stringRedisTemplate.opsForValue().set(redisKey, JsonUtils.toJsonString(accessTokenDO),
|
||||
accessTokenDO.getExpiresTime().getTime() - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
public void delete(String accessToken) {
|
||||
String redisKey = formatKey(accessToken);
|
||||
stringRedisTemplate.delete(redisKey);
|
||||
}
|
||||
|
||||
public void deleteList(Collection<String> accessTokens) {
|
||||
List<String> redisKeys = CollectionUtils.convertList(accessTokens, OAuth2AccessTokenRedisDAO::formatKey);
|
||||
stringRedisTemplate.delete(redisKeys);
|
||||
}
|
||||
|
||||
private static String formatKey(String accessToken) {
|
||||
return String.format(OAUTH2_ACCESS_TOKEN.getKeyTemplate(), accessToken);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,32 +0,0 @@
|
||||
package cn.iocoder.yudao.module.system.job.auth;
|
||||
|
||||
import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
|
||||
import cn.iocoder.yudao.module.system.service.auth.UserSessionService;
|
||||
import cn.iocoder.yudao.framework.tenant.core.job.TenantJob;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 用户 Session 超时 Job
|
||||
*
|
||||
* @author 願
|
||||
*/
|
||||
@Component
|
||||
@TenantJob
|
||||
@Slf4j
|
||||
public class UserSessionTimeoutJob implements JobHandler {
|
||||
|
||||
@Resource
|
||||
private UserSessionService userSessionService;
|
||||
|
||||
@Override
|
||||
public String execute(String param) throws Exception {
|
||||
// 执行过期
|
||||
Long timeoutCount = userSessionService.deleteTimeoutSession();
|
||||
// 返回结果,记录每次的超时数量
|
||||
return String.format("移除在线会话数量为 %s 个", timeoutCount);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,77 @@
|
||||
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.client.OAuth2ClientCreateReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2ClientPageReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2ClientUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.system.convert.auth.OAuth2ClientConvert;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO;
|
||||
import cn.iocoder.yudao.module.system.dal.mysql.auth.OAuth2ClientMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.OAUTH2_CLIENT_NOT_EXISTS;
|
||||
|
||||
/**
|
||||
* OAuth2.0 Client Service 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
public class OAuth2ClientServiceImpl implements OAuth2ClientService {
|
||||
|
||||
@Resource
|
||||
private OAuth2ClientMapper oauth2ClientMapper;
|
||||
|
||||
@Override
|
||||
public Long createOAuth2Client(OAuth2ClientCreateReqVO createReqVO) {
|
||||
// 插入
|
||||
OAuth2ClientDO oAuth2Client = OAuth2ClientConvert.INSTANCE.convert(createReqVO);
|
||||
oauth2ClientMapper.insert(oAuth2Client);
|
||||
// 返回
|
||||
return oAuth2Client.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateOAuth2Client(OAuth2ClientUpdateReqVO updateReqVO) {
|
||||
// 校验存在
|
||||
this.validateOAuth2ClientExists(updateReqVO.getId());
|
||||
// 更新
|
||||
OAuth2ClientDO updateObj = OAuth2ClientConvert.INSTANCE.convert(updateReqVO);
|
||||
oauth2ClientMapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteOAuth2Client(Long id) {
|
||||
// 校验存在
|
||||
this.validateOAuth2ClientExists(id);
|
||||
// 删除
|
||||
oauth2ClientMapper.deleteById(id);
|
||||
}
|
||||
|
||||
private void validateOAuth2ClientExists(Long id) {
|
||||
if (oauth2ClientMapper.selectById(id) == null) {
|
||||
throw exception(OAUTH2_CLIENT_NOT_EXISTS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuth2ClientDO getOAuth2Client(Long id) {
|
||||
return oauth2ClientMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<OAuth2ClientDO> getOAuth2ClientPage(OAuth2ClientPageReqVO pageReqVO) {
|
||||
return oauth2ClientMapper.selectPage(pageReqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuth2ClientDO validOAuthClientFromCache(Long id) {
|
||||
return new OAuth2ClientDO().setId(id)
|
||||
.setAccessTokenValiditySeconds(60 * 30)
|
||||
.setRefreshTokenValiditySeconds(60 * 60 * 24 * 30);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,163 @@
|
||||
package cn.iocoder.yudao.module.system.service.auth;
|
||||
|
||||
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;
|
||||
import cn.iocoder.yudao.module.system.dal.mysql.auth.OAuth2AccessTokenMapper;
|
||||
import cn.iocoder.yudao.module.system.dal.mysql.auth.OAuth2RefreshTokenMapper;
|
||||
import cn.iocoder.yudao.module.system.dal.redis.auth.OAuth2AccessTokenRedisDAO;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
|
||||
/**
|
||||
* OAuth2.0 Token Service 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
public class OAuth2TokenServiceImpl implements OAuth2TokenService {
|
||||
|
||||
@Resource
|
||||
private OAuth2AccessTokenMapper oauth2AccessTokenMapper;
|
||||
@Resource
|
||||
private OAuth2RefreshTokenMapper oauth2RefreshTokenMapper;
|
||||
|
||||
@Resource
|
||||
private OAuth2AccessTokenRedisDAO oauth2AccessTokenRedisDAO;
|
||||
|
||||
@Resource
|
||||
private OAuth2ClientService oauth2ClientService;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public OAuth2AccessTokenDO createAccessToken(Long userId, Integer userType, Long clientId) {
|
||||
OAuth2ClientDO clientDO = oauth2ClientService.validOAuthClientFromCache(clientId);
|
||||
// 创建刷新令牌
|
||||
OAuth2RefreshTokenDO refreshTokenDO = createOAuth2RefreshToken(userId, userType, clientDO);
|
||||
// 创建访问令牌
|
||||
return createOAuth2AccessToken(refreshTokenDO, clientDO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuth2AccessTokenDO refreshAccessToken(String refreshToken, Long clientId) {
|
||||
// 查询访问令牌
|
||||
OAuth2RefreshTokenDO refreshTokenDO = oauth2RefreshTokenMapper.selectByRefreshToken(refreshToken);
|
||||
if (refreshTokenDO == null) {
|
||||
throw exception(GlobalErrorCodeConstants.BAD_REQUEST, "无效的刷新令牌");
|
||||
}
|
||||
|
||||
// 校验 Client 匹配
|
||||
OAuth2ClientDO clientDO = oauth2ClientService.validOAuthClientFromCache(clientId);
|
||||
if (ObjectUtil.notEqual(clientId, refreshTokenDO.getClientId())) {
|
||||
throw exception(GlobalErrorCodeConstants.BAD_REQUEST, "刷新令牌的客户端编号不正确");
|
||||
}
|
||||
|
||||
// 移除相关的访问令牌
|
||||
List<OAuth2AccessTokenDO> accessTokenDOs = oauth2AccessTokenMapper.selectListByRefreshToken(refreshToken);
|
||||
if (CollUtil.isNotEmpty(accessTokenDOs)) {
|
||||
oauth2AccessTokenMapper.deleteBatchIds(convertSet(accessTokenDOs, OAuth2AccessTokenDO::getId));
|
||||
oauth2AccessTokenRedisDAO.deleteList(convertSet(accessTokenDOs, OAuth2AccessTokenDO::getAccessToken));
|
||||
}
|
||||
|
||||
// 已过期的情况下,删除刷新令牌
|
||||
if (DateUtils.isExpired(refreshTokenDO.getExpiresTime())) {
|
||||
oauth2AccessTokenMapper.deleteById(refreshTokenDO.getId());
|
||||
throw exception(GlobalErrorCodeConstants.UNAUTHORIZED, "刷新令牌已过期");
|
||||
}
|
||||
|
||||
// 创建访问令牌
|
||||
return createOAuth2AccessToken(refreshTokenDO, clientDO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuth2AccessTokenDO getAccessToken(String accessToken) {
|
||||
// 优先从 Redis 中获取
|
||||
OAuth2AccessTokenDO accessTokenDO = oauth2AccessTokenRedisDAO.get(accessToken);
|
||||
if (accessTokenDO != null) {
|
||||
return accessTokenDO;
|
||||
}
|
||||
|
||||
// 获取不到,从 MySQL 中获取
|
||||
accessTokenDO = oauth2AccessTokenMapper.selectById(accessToken);
|
||||
// 如果在 MySQL 存在,则往 Redis 中写入
|
||||
if (accessTokenDO != null && !DateUtils.isExpired(accessTokenDO.getExpiresTime())) {
|
||||
oauth2AccessTokenRedisDAO.set(accessTokenDO);
|
||||
}
|
||||
return accessTokenDO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuth2AccessTokenDO checkAccessToken(String accessToken) {
|
||||
OAuth2AccessTokenDO accessTokenDO = getAccessToken(accessToken);
|
||||
if (accessTokenDO == null) {
|
||||
throw exception(GlobalErrorCodeConstants.UNAUTHORIZED, "访问令牌不存在");
|
||||
}
|
||||
if (DateUtils.isExpired(accessTokenDO.getExpiresTime())) {
|
||||
throw exception(GlobalErrorCodeConstants.UNAUTHORIZED, "访问令牌已过期");
|
||||
}
|
||||
return accessTokenDO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuth2AccessTokenDO removeAccessToken(String accessToken) {
|
||||
// 删除访问令牌
|
||||
OAuth2AccessTokenDO accessTokenDO = oauth2AccessTokenMapper.selectByAccessToken(accessToken);
|
||||
if (accessTokenDO == null) {
|
||||
return null;
|
||||
}
|
||||
oauth2AccessTokenMapper.deleteById(accessTokenDO.getId());
|
||||
oauth2AccessTokenRedisDAO.delete(accessToken);
|
||||
// 删除刷新令牌
|
||||
oauth2RefreshTokenMapper.deleteByRefreshToken(accessTokenDO.getRefreshToken());
|
||||
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())
|
||||
.setRefreshToken(refreshTokenDO.getRefreshToken())
|
||||
.setExpiresTime(DateUtils.addDate(Calendar.SECOND, clientDO.getAccessTokenValiditySeconds()));
|
||||
accessTokenDO.setTenantId(TenantContextHolder.getTenantId()); // 手动设置租户编号,避免缓存到 Redis 的时候,无对应的租户编号
|
||||
oauth2AccessTokenMapper.insert(accessTokenDO);
|
||||
// 记录到 Redis 中
|
||||
oauth2AccessTokenRedisDAO.set(accessTokenDO);
|
||||
return accessTokenDO;
|
||||
}
|
||||
|
||||
private OAuth2RefreshTokenDO createOAuth2RefreshToken(Long userId, Integer userType, OAuth2ClientDO clientDO) {
|
||||
OAuth2RefreshTokenDO refreshToken = new OAuth2RefreshTokenDO().setRefreshToken(generateRefreshToken())
|
||||
.setUserId(userId).setUserType(userType).setClientId(clientDO.getId())
|
||||
.setExpiresTime(DateUtils.addDate(Calendar.SECOND, clientDO.getRefreshTokenValiditySeconds()));
|
||||
oauth2RefreshTokenMapper.insert(refreshToken);
|
||||
return refreshToken;
|
||||
}
|
||||
|
||||
private static String generateAccessToken() {
|
||||
return IdUtil.fastSimpleUUID();
|
||||
}
|
||||
|
||||
private static String generateRefreshToken() {
|
||||
return IdUtil.fastSimpleUUID();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,128 @@
|
||||
package cn.iocoder.yudao.module.system.service.auth;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2ClientCreateReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2ClientPageReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2ClientUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO;
|
||||
import cn.iocoder.yudao.module.system.dal.mysql.auth.OAuth2ClientMapper;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
||||
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.OAUTH2_CLIENT_NOT_EXISTS;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* {@link OAuth2ClientServiceImpl} 的单元测试类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Import(OAuth2ClientServiceImpl.class)
|
||||
public class OAuth2ClientServiceImplTest extends BaseDbUnitTest {
|
||||
|
||||
@Resource
|
||||
private OAuth2ClientServiceImpl oAuth2ClientService;
|
||||
|
||||
@Resource
|
||||
private OAuth2ClientMapper oAuth2ClientMapper;
|
||||
|
||||
@Test
|
||||
public void testCreateOAuth2Client_success() {
|
||||
// 准备参数
|
||||
OAuth2ClientCreateReqVO reqVO = randomPojo(OAuth2ClientCreateReqVO.class);
|
||||
|
||||
// 调用
|
||||
Long oauth2ClientId = oAuth2ClientService.createOAuth2Client(reqVO);
|
||||
// 断言
|
||||
assertNotNull(oauth2ClientId);
|
||||
// 校验记录的属性是否正确
|
||||
OAuth2ClientDO oAuth2Client = oAuth2ClientMapper.selectById(oauth2ClientId);
|
||||
assertPojoEquals(reqVO, oAuth2Client);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateOAuth2Client_success() {
|
||||
// mock 数据
|
||||
OAuth2ClientDO dbOAuth2Client = randomPojo(OAuth2ClientDO.class);
|
||||
oAuth2ClientMapper.insert(dbOAuth2Client);// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
OAuth2ClientUpdateReqVO reqVO = randomPojo(OAuth2ClientUpdateReqVO.class, o -> {
|
||||
o.setId(dbOAuth2Client.getId()); // 设置更新的 ID
|
||||
});
|
||||
|
||||
// 调用
|
||||
oAuth2ClientService.updateOAuth2Client(reqVO);
|
||||
// 校验是否更新正确
|
||||
OAuth2ClientDO oAuth2Client = oAuth2ClientMapper.selectById(reqVO.getId()); // 获取最新的
|
||||
assertPojoEquals(reqVO, oAuth2Client);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateOAuth2Client_notExists() {
|
||||
// 准备参数
|
||||
OAuth2ClientUpdateReqVO reqVO = randomPojo(OAuth2ClientUpdateReqVO.class);
|
||||
|
||||
// 调用, 并断言异常
|
||||
assertServiceException(() -> oAuth2ClientService.updateOAuth2Client(reqVO), OAUTH2_CLIENT_NOT_EXISTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteOAuth2Client_success() {
|
||||
// mock 数据
|
||||
OAuth2ClientDO dbOAuth2Client = randomPojo(OAuth2ClientDO.class);
|
||||
oAuth2ClientMapper.insert(dbOAuth2Client);// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
Long id = dbOAuth2Client.getId();
|
||||
|
||||
// 调用
|
||||
oAuth2ClientService.deleteOAuth2Client(id);
|
||||
// 校验数据不存在了
|
||||
assertNull(oAuth2ClientMapper.selectById(id));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteOAuth2Client_notExists() {
|
||||
// 准备参数
|
||||
Long id = randomLongId();
|
||||
|
||||
// 调用, 并断言异常
|
||||
assertServiceException(() -> oAuth2ClientService.deleteOAuth2Client(id), OAUTH2_CLIENT_NOT_EXISTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testGetOAuth2ClientPage() {
|
||||
// mock 数据
|
||||
OAuth2ClientDO dbOAuth2Client = randomPojo(OAuth2ClientDO.class, o -> { // 等会查询到
|
||||
o.setName("潜龙");
|
||||
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
});
|
||||
oAuth2ClientMapper.insert(dbOAuth2Client);
|
||||
// 测试 name 不匹配
|
||||
oAuth2ClientMapper.insert(cloneIgnoreId(dbOAuth2Client, o -> o.setName("凤凰")));
|
||||
// 测试 status 不匹配
|
||||
oAuth2ClientMapper.insert(cloneIgnoreId(dbOAuth2Client, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())));
|
||||
// 准备参数
|
||||
OAuth2ClientPageReqVO reqVO = new OAuth2ClientPageReqVO();
|
||||
reqVO.setName("long");
|
||||
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
|
||||
// 调用
|
||||
PageResult<OAuth2ClientDO> pageResult = oAuth2ClientService.getOAuth2ClientPage(reqVO);
|
||||
// 断言
|
||||
assertEquals(1, pageResult.getTotal());
|
||||
assertEquals(1, pageResult.getList().size());
|
||||
assertPojoEquals(dbOAuth2Client, pageResult.getList().get(0));
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue