mirror of
https://gitee.com/huangge1199_admin/vue-pro.git
synced 2024-11-26 17:21:53 +08:00
完成 OAuth2 的客户端模块
This commit is contained in:
parent
1f36af8e6a
commit
97db4586a8
@ -113,8 +113,7 @@ public class JsonUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO @Li:和上面的风格保持一致哈。parseTree
|
public static JsonNode parseTree(String text) {
|
||||||
public static JsonNode readTree(String text) {
|
|
||||||
try {
|
try {
|
||||||
return objectMapper.readTree(text);
|
return objectMapper.readTree(text);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@ -123,7 +122,7 @@ public class JsonUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static JsonNode readTree(byte[] text) {
|
public static JsonNode parseTree(byte[] text) {
|
||||||
try {
|
try {
|
||||||
return objectMapper.readTree(text);
|
return objectMapper.readTree(text);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@ -132,4 +131,8 @@ public class JsonUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isJson(String text) {
|
||||||
|
return JSONUtil.isJson(text);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ 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.logger.dto.LoginLogCreateReqDTO;
|
||||||
import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
|
import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
|
||||||
import cn.iocoder.yudao.module.system.api.social.SocialUserApi;
|
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.auth.OAuth2ClientConstants;
|
||||||
import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum;
|
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.logger.LoginResultEnum;
|
||||||
import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum;
|
import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum;
|
||||||
@ -120,7 +120,7 @@ public class MemberAuthServiceImpl implements MemberAuthService {
|
|||||||
createLoginLog(user.getId(), mobile, logType, LoginResultEnum.SUCCESS);
|
createLoginLog(user.getId(), mobile, logType, LoginResultEnum.SUCCESS);
|
||||||
// 创建 Token 令牌
|
// 创建 Token 令牌
|
||||||
OAuth2AccessTokenRespDTO accessTokenRespDTO = oauth2TokenApi.createAccessToken(new OAuth2AccessTokenCreateReqDTO()
|
OAuth2AccessTokenRespDTO accessTokenRespDTO = oauth2TokenApi.createAccessToken(new OAuth2AccessTokenCreateReqDTO()
|
||||||
.setUserId(user.getId()).setUserType(getUserType().getValue()).setClientId(OAuth2ClientIdEnum.DEFAULT.getId()));
|
.setUserId(user.getId()).setUserType(getUserType().getValue()).setClientId(OAuth2ClientConstants.CLIENT_ID_DEFAULT));
|
||||||
// 构建返回结果
|
// 构建返回结果
|
||||||
return AuthConvert.INSTANCE.convert(accessTokenRespDTO);
|
return AuthConvert.INSTANCE.convert(accessTokenRespDTO);
|
||||||
}
|
}
|
||||||
@ -212,7 +212,7 @@ public class MemberAuthServiceImpl implements MemberAuthService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AppAuthLoginRespVO refreshToken(String refreshToken) {
|
public AppAuthLoginRespVO refreshToken(String refreshToken) {
|
||||||
OAuth2AccessTokenRespDTO accessTokenDO = oauth2TokenApi.refreshAccessToken(refreshToken, OAuth2ClientIdEnum.DEFAULT.getId());
|
OAuth2AccessTokenRespDTO accessTokenDO = oauth2TokenApi.refreshAccessToken(refreshToken, OAuth2ClientConstants.CLIENT_ID_DEFAULT);
|
||||||
return AuthConvert.INSTANCE.convert(accessTokenDO);
|
return AuthConvert.INSTANCE.convert(accessTokenDO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,6 +44,6 @@ public interface OAuth2TokenApi {
|
|||||||
* @param clientId 客户端编号
|
* @param clientId 客户端编号
|
||||||
* @return 访问令牌的信息
|
* @return 访问令牌的信息
|
||||||
*/
|
*/
|
||||||
OAuth2AccessTokenRespDTO refreshAccessToken(String refreshToken, Long clientId);
|
OAuth2AccessTokenRespDTO refreshAccessToken(String refreshToken, String clientId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,6 @@ public class OAuth2AccessTokenCreateReqDTO implements Serializable {
|
|||||||
* 客户端编号
|
* 客户端编号
|
||||||
*/
|
*/
|
||||||
@NotNull(message = "客户端编号不能为空")
|
@NotNull(message = "客户端编号不能为空")
|
||||||
private Long clientId;
|
private String clientId;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -125,5 +125,6 @@ public interface ErrorCodeConstants {
|
|||||||
|
|
||||||
// ========== 系统敏感词 1002020000 =========
|
// ========== 系统敏感词 1002020000 =========
|
||||||
ErrorCode OAUTH2_CLIENT_NOT_EXISTS = new ErrorCode(1002020000, "OAuth2 客户端不存在");
|
ErrorCode OAUTH2_CLIENT_NOT_EXISTS = new ErrorCode(1002020000, "OAuth2 客户端不存在");
|
||||||
|
ErrorCode OAUTH2_CLIENT_EXISTS = new ErrorCode(1002020001, "OAuth2 客户端编号已存在");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
package cn.iocoder.yudao.module.system.enums.auth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OAuth2.0 客户端的通用枚举
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
public interface OAuth2ClientConstants {
|
||||||
|
|
||||||
|
String CLIENT_ID_DEFAULT = "default";
|
||||||
|
|
||||||
|
}
|
@ -1,17 +0,0 @@
|
|||||||
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,24 @@
|
|||||||
|
package cn.iocoder.yudao.module.system.enums.auth;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OAuth2 授权类型(模式)的枚举
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public enum OAuth2GrantTypeEnum {
|
||||||
|
|
||||||
|
PASSWORD("password"), // 密码模式
|
||||||
|
AUTHORIZATION_CODE("authorization_code"), // 授权码模式
|
||||||
|
IMPLICIT("implicit"), // 简化模式
|
||||||
|
CLIENT_CREDENTIALS("client_credentials"), // 客户端模式
|
||||||
|
REFRESH_TOKEN("refresh_token"), // 刷新模式
|
||||||
|
;
|
||||||
|
|
||||||
|
private final String grantType;
|
||||||
|
|
||||||
|
}
|
@ -40,7 +40,7 @@ public class OAuth2TokenApiImpl implements OAuth2TokenApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OAuth2AccessTokenRespDTO refreshAccessToken(String refreshToken, Long clientId) {
|
public OAuth2AccessTokenRespDTO refreshAccessToken(String refreshToken, String clientId) {
|
||||||
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, clientId);
|
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, clientId);
|
||||||
return OAuth2TokenConvert.INSTANCE.convert2(accessTokenDO);
|
return OAuth2TokenConvert.INSTANCE.convert2(accessTokenDO);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
### 请求 /login 接口 => 成功
|
||||||
|
POST {{baseUrl}}/system/oauth2-client/create
|
||||||
|
Content-Type: application/json
|
||||||
|
Authorization: Bearer {{token}}
|
||||||
|
tenant-id: {{adminTenentId}}
|
||||||
|
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"secret": "admin123",
|
||||||
|
"name": "芋道源码",
|
||||||
|
"logo": "https://www.iocoder.cn/images/favicon.ico",
|
||||||
|
"description": "我是描述",
|
||||||
|
"status": 0,
|
||||||
|
"accessTokenValiditySeconds": 180,
|
||||||
|
"refreshTokenValiditySeconds": 8640,
|
||||||
|
"redirectUris": ["https://www.iocoder.cn"],
|
||||||
|
"autoApprove": true,
|
||||||
|
"authorizedGrantTypes": ["password"],
|
||||||
|
"scopes": ["user_info"],
|
||||||
|
"authorities": ["system:user:query"],
|
||||||
|
"resource_ids": ["1024"],
|
||||||
|
"additionalInformation": "{}"
|
||||||
|
}
|
@ -1,8 +1,13 @@
|
|||||||
package cn.iocoder.yudao.module.system.controller.admin.auth.vo.client;
|
package cn.iocoder.yudao.module.system.controller.admin.auth.vo.client;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||||
import io.swagger.annotations.ApiModelProperty;
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import org.hibernate.validator.constraints.URL;
|
||||||
|
|
||||||
|
import javax.validation.constraints.AssertTrue;
|
||||||
|
import javax.validation.constraints.NotEmpty;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -15,7 +20,7 @@ public class OAuth2ClientBaseVO {
|
|||||||
|
|
||||||
@ApiModelProperty(value = "客户端编号", required = true)
|
@ApiModelProperty(value = "客户端编号", required = true)
|
||||||
@NotNull(message = "客户端编号不能为空")
|
@NotNull(message = "客户端编号不能为空")
|
||||||
private Long id;
|
private String clientId;
|
||||||
|
|
||||||
@ApiModelProperty(value = "客户端密钥", required = true)
|
@ApiModelProperty(value = "客户端密钥", required = true)
|
||||||
@NotNull(message = "客户端密钥不能为空")
|
@NotNull(message = "客户端密钥不能为空")
|
||||||
@ -27,6 +32,7 @@ public class OAuth2ClientBaseVO {
|
|||||||
|
|
||||||
@ApiModelProperty(value = "应用图标", required = true)
|
@ApiModelProperty(value = "应用图标", required = true)
|
||||||
@NotNull(message = "应用图标不能为空")
|
@NotNull(message = "应用图标不能为空")
|
||||||
|
@URL(message = "应用图标的地址不正确")
|
||||||
private String logo;
|
private String logo;
|
||||||
|
|
||||||
@ApiModelProperty(value = "应用描述")
|
@ApiModelProperty(value = "应用描述")
|
||||||
@ -46,6 +52,32 @@ public class OAuth2ClientBaseVO {
|
|||||||
|
|
||||||
@ApiModelProperty(value = "可重定向的 URI 地址", required = true)
|
@ApiModelProperty(value = "可重定向的 URI 地址", required = true)
|
||||||
@NotNull(message = "可重定向的 URI 地址不能为空")
|
@NotNull(message = "可重定向的 URI 地址不能为空")
|
||||||
private List<String> redirectUris;
|
private List<@NotEmpty(message = "重定向的 URI 不能为空")
|
||||||
|
@URL(message = "重定向的 URI 格式不正确") String> redirectUris;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "是否自动授权", required = true, example = "true")
|
||||||
|
@NotNull(message = "是否自动授权不能为空")
|
||||||
|
private Boolean autoApprove;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "授权类型", required = true, example = "password", notes = "参见 OAuth2GrantTypeEnum 枚举")
|
||||||
|
@NotNull(message = "授权类型不能为空")
|
||||||
|
private List<String> authorizedGrantTypes;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "授权范围", example = "user_info")
|
||||||
|
private List<String> scopes;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "权限", example = "system:user:query")
|
||||||
|
private List<String> authorities;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "资源", example = "1024")
|
||||||
|
private List<String> resourceIds;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "附加信息", example = "{yunai: true}")
|
||||||
|
private String additionalInformation;
|
||||||
|
|
||||||
|
@AssertTrue(message = "附加信息必须是 JSON 格式")
|
||||||
|
public boolean isAdditionalInformationJson() {
|
||||||
|
return StrUtil.isEmpty(additionalInformation) || JsonUtils.isJson(additionalInformation);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,9 @@ import java.util.Date;
|
|||||||
@ToString(callSuper = true)
|
@ToString(callSuper = true)
|
||||||
public class OAuth2ClientRespVO extends OAuth2ClientBaseVO {
|
public class OAuth2ClientRespVO extends OAuth2ClientBaseVO {
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "编号", required = true)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
@ApiModelProperty(value = "创建时间", required = true)
|
@ApiModelProperty(value = "创建时间", required = true)
|
||||||
private Date createTime;
|
private Date createTime;
|
||||||
|
|
||||||
|
@ -1,14 +1,21 @@
|
|||||||
package cn.iocoder.yudao.module.system.controller.admin.auth.vo.client;
|
package cn.iocoder.yudao.module.system.controller.admin.auth.vo.client;
|
||||||
|
|
||||||
import io.swagger.annotations.ApiModel;
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
@ApiModel("管理后台 - OAuth2 客户端更新 Request VO")
|
@ApiModel("管理后台 - OAuth2 客户端更新 Request VO")
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
@ToString(callSuper = true)
|
@ToString(callSuper = true)
|
||||||
public class OAuth2ClientUpdateReqVO extends OAuth2ClientBaseVO {
|
public class OAuth2ClientUpdateReqVO extends OAuth2ClientBaseVO {
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "编号", required = true)
|
||||||
|
@NotNull(message = "编号不能为空")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,6 @@ public class OAuth2AccessTokenPageReqVO extends PageParam {
|
|||||||
private Integer userType;
|
private Integer userType;
|
||||||
|
|
||||||
@ApiModelProperty(value = "客户端编号", required = true, example = "2")
|
@ApiModelProperty(value = "客户端编号", required = true, example = "2")
|
||||||
private Long clientId;
|
private String clientId;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ public class OAuth2AccessTokenRespVO {
|
|||||||
private Integer userType;
|
private Integer userType;
|
||||||
|
|
||||||
@ApiModelProperty(value = "客户端编号", required = true, example = "2")
|
@ApiModelProperty(value = "客户端编号", required = true, example = "2")
|
||||||
private Long clientId;
|
private String clientId;
|
||||||
|
|
||||||
@ApiModelProperty(value = "创建时间", required = true)
|
@ApiModelProperty(value = "创建时间", required = true)
|
||||||
private Date createTime;
|
private Date createTime;
|
||||||
|
@ -52,7 +52,7 @@ public class OAuth2AccessTokenDO extends TenantBaseDO {
|
|||||||
*
|
*
|
||||||
* 关联 {@link OAuth2ClientDO#getId()}
|
* 关联 {@link OAuth2ClientDO#getId()}
|
||||||
*/
|
*/
|
||||||
private Long clientId;
|
private String clientId;
|
||||||
/**
|
/**
|
||||||
* 过期时间
|
* 过期时间
|
||||||
*/
|
*/
|
||||||
|
@ -2,38 +2,37 @@ package cn.iocoder.yudao.module.system.dal.dataobject.auth;
|
|||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||||
import com.baomidou.mybatisplus.annotation.IdType;
|
import cn.iocoder.yudao.module.system.enums.auth.OAuth2GrantTypeEnum;
|
||||||
import com.baomidou.mybatisplus.annotation.TableField;
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.experimental.Accessors;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OAuth2 客户端 DO
|
* OAuth2 客户端 DO
|
||||||
*
|
*
|
||||||
* 如下字段,考虑到使用相对不是很高频,主要是一些开关,暂时不支持:
|
|
||||||
* authorized_grant_types、authorities、additional_information、autoapprove、resource_ids、scope
|
|
||||||
*
|
|
||||||
* @author 芋道源码
|
* @author 芋道源码
|
||||||
*/
|
*/
|
||||||
@TableName(value = "system_oauth2_client", autoResultMap = true)
|
@TableName(value = "system_oauth2_client", autoResultMap = true)
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
@Accessors(chain = true)
|
|
||||||
public class OAuth2ClientDO extends BaseDO {
|
public class OAuth2ClientDO extends BaseDO {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 客户端编号
|
* 编号,数据库自增
|
||||||
*
|
*
|
||||||
* 由于 SQL Server 在存储 String 主键有点问题,所以暂时使用 Long 类型
|
* 由于 SQL Server 在存储 String 主键有点问题,所以暂时使用 Long 类型
|
||||||
*/
|
*/
|
||||||
@TableId(type = IdType.INPUT)
|
@TableId
|
||||||
private Long id;
|
private Long id;
|
||||||
|
/**
|
||||||
|
* 客户端编号
|
||||||
|
*/
|
||||||
|
private String clientId;
|
||||||
/**
|
/**
|
||||||
* 客户端密钥
|
* 客户端密钥
|
||||||
*/
|
*/
|
||||||
@ -69,5 +68,35 @@ public class OAuth2ClientDO extends BaseDO {
|
|||||||
*/
|
*/
|
||||||
@TableField(typeHandler = JacksonTypeHandler.class)
|
@TableField(typeHandler = JacksonTypeHandler.class)
|
||||||
private List<String> redirectUris;
|
private List<String> redirectUris;
|
||||||
|
/**
|
||||||
|
* 是否自动授权
|
||||||
|
*/
|
||||||
|
private Boolean autoApprove;
|
||||||
|
/**
|
||||||
|
* 授权类型(模式)
|
||||||
|
*
|
||||||
|
* 枚举 {@link OAuth2GrantTypeEnum}
|
||||||
|
*/
|
||||||
|
@TableField(typeHandler = JacksonTypeHandler.class)
|
||||||
|
private List<String> authorizedGrantTypes;
|
||||||
|
/**
|
||||||
|
* 授权范围
|
||||||
|
*/
|
||||||
|
@TableField(typeHandler = JacksonTypeHandler.class)
|
||||||
|
private List<String> scopes;
|
||||||
|
/**
|
||||||
|
* 权限
|
||||||
|
*/
|
||||||
|
@TableField(typeHandler = JacksonTypeHandler.class)
|
||||||
|
private List<String> authorities;
|
||||||
|
/**
|
||||||
|
* 资源
|
||||||
|
*/
|
||||||
|
@TableField(typeHandler = JacksonTypeHandler.class)
|
||||||
|
private List<String> resourceIds;
|
||||||
|
/**
|
||||||
|
* 附加信息,JSON 格式
|
||||||
|
*/
|
||||||
|
private String additionalInformation;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
|||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.experimental.Accessors;
|
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
@ -17,7 +16,6 @@ import java.util.Date;
|
|||||||
@TableName("system_oauth2_code")
|
@TableName("system_oauth2_code")
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
@Accessors(chain = true)
|
|
||||||
public class OAuth2CodeDO extends BaseDO {
|
public class OAuth2CodeDO extends BaseDO {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -43,7 +41,7 @@ public class OAuth2CodeDO extends BaseDO {
|
|||||||
*
|
*
|
||||||
* 关联 {@link OAuth2ClientDO#getId()}
|
* 关联 {@link OAuth2ClientDO#getId()}
|
||||||
*/
|
*/
|
||||||
private Long clientId;
|
private String clientId;
|
||||||
/**
|
/**
|
||||||
* 刷新令牌
|
* 刷新令牌
|
||||||
*
|
*
|
||||||
|
@ -43,7 +43,7 @@ public class OAuth2RefreshTokenDO extends BaseDO {
|
|||||||
*
|
*
|
||||||
* 关联 {@link OAuth2ClientDO#getId()}
|
* 关联 {@link OAuth2ClientDO#getId()}
|
||||||
*/
|
*/
|
||||||
private Long clientId;
|
private String clientId;
|
||||||
/**
|
/**
|
||||||
* 过期时间
|
* 过期时间
|
||||||
*/
|
*/
|
||||||
|
@ -6,6 +6,9 @@ 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.controller.admin.auth.vo.client.OAuth2ClientPageReqVO;
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO;
|
import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Select;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OAuth2 客户端 Mapper
|
* OAuth2 客户端 Mapper
|
||||||
@ -22,4 +25,11 @@ public interface OAuth2ClientMapper extends BaseMapperX<OAuth2ClientDO> {
|
|||||||
.orderByDesc(OAuth2ClientDO::getId));
|
.orderByDesc(OAuth2ClientDO::getId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default OAuth2ClientDO selectByClientId(String clientId) {
|
||||||
|
return selectOne(OAuth2ClientDO::getClientId, clientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Select("SELECT COUNT(*) FROM system_oauth2_client WHERE update_time > #{maxUpdateTime}")
|
||||||
|
int selectCountByUpdateTimeGt(Date maxUpdateTime);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
package cn.iocoder.yudao.module.system.mq.consumer.auth;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener;
|
||||||
|
import cn.iocoder.yudao.module.system.mq.message.auth.OAuth2ClientRefreshMessage;
|
||||||
|
import cn.iocoder.yudao.module.system.service.auth.OAuth2ClientService;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 针对 {@link OAuth2ClientRefreshMessage} 的消费者
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
public class OAuth2ClientRefreshConsumer extends AbstractChannelMessageListener<OAuth2ClientRefreshMessage> {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private OAuth2ClientService oauth2ClientService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMessage(OAuth2ClientRefreshMessage message) {
|
||||||
|
log.info("[onMessage][收到 OAuth2Client 刷新消息]");
|
||||||
|
oauth2ClientService.initLocalCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package cn.iocoder.yudao.module.system.mq.message.auth;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OAuth 2.0 客户端的数据刷新 Message
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class OAuth2ClientRefreshMessage extends AbstractChannelMessage {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getChannel() {
|
||||||
|
return "system.oauth2-client.refresh";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package cn.iocoder.yudao.module.system.mq.producer.auth;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate;
|
||||||
|
import cn.iocoder.yudao.module.system.mq.message.auth.OAuth2ClientRefreshMessage;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OAuth 2.0 客户端相关消息的 Producer
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class OAuth2ClientProducer {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private RedisMQTemplate redisMQTemplate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送 {@link OAuth2ClientRefreshMessage} 消息
|
||||||
|
*/
|
||||||
|
public void sendOAuth2ClientRefreshMessage() {
|
||||||
|
OAuth2ClientRefreshMessage message = new OAuth2ClientRefreshMessage();
|
||||||
|
redisMQTemplate.send(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -12,7 +12,7 @@ import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.*;
|
|||||||
import cn.iocoder.yudao.module.system.convert.auth.AuthConvert;
|
import cn.iocoder.yudao.module.system.convert.auth.AuthConvert;
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO;
|
import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO;
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
|
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
|
||||||
import cn.iocoder.yudao.module.system.enums.auth.OAuth2ClientIdEnum;
|
import cn.iocoder.yudao.module.system.enums.auth.OAuth2ClientConstants;
|
||||||
import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum;
|
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.logger.LoginResultEnum;
|
||||||
import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum;
|
import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum;
|
||||||
@ -197,7 +197,7 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AuthLoginRespVO refreshToken(String refreshToken) {
|
public AuthLoginRespVO refreshToken(String refreshToken) {
|
||||||
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, OAuth2ClientIdEnum.DEFAULT.getId());
|
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, OAuth2ClientConstants.CLIENT_ID_DEFAULT);
|
||||||
return AuthConvert.INSTANCE.convert(accessTokenDO);
|
return AuthConvert.INSTANCE.convert(accessTokenDO);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,7 +206,7 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
|||||||
createLoginLog(userId, username, logType, LoginResultEnum.SUCCESS);
|
createLoginLog(userId, username, logType, LoginResultEnum.SUCCESS);
|
||||||
// 创建访问令牌
|
// 创建访问令牌
|
||||||
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken(userId, getUserType().getValue(),
|
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken(userId, getUserType().getValue(),
|
||||||
OAuth2ClientIdEnum.DEFAULT.getId());
|
OAuth2ClientConstants.CLIENT_ID_DEFAULT);
|
||||||
// 构建返回结果
|
// 构建返回结果
|
||||||
return AuthConvert.INSTANCE.convert(accessTokenDO);
|
return AuthConvert.INSTANCE.convert(accessTokenDO);
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,11 @@ import javax.validation.Valid;
|
|||||||
*/
|
*/
|
||||||
public interface OAuth2ClientService {
|
public interface OAuth2ClientService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化 OAuth2Client 的本地缓存
|
||||||
|
*/
|
||||||
|
void initLocalCache();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建 OAuth2 客户端
|
* 创建 OAuth2 客户端
|
||||||
*
|
*
|
||||||
@ -58,9 +63,9 @@ public interface OAuth2ClientService {
|
|||||||
/**
|
/**
|
||||||
* 从缓存中,校验客户端是否合法
|
* 从缓存中,校验客户端是否合法
|
||||||
*
|
*
|
||||||
* @param id 客户端编号
|
* @param clientId 客户端编号
|
||||||
* @return 客户端
|
* @return 客户端
|
||||||
*/
|
*/
|
||||||
OAuth2ClientDO validOAuthClientFromCache(Long id);
|
OAuth2ClientDO validOAuthClientFromCache(String clientId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package cn.iocoder.yudao.module.system.service.auth;
|
package cn.iocoder.yudao.module.system.service.auth;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
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.OAuth2ClientCreateReqVO;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2ClientPageReqVO;
|
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2ClientPageReqVO;
|
||||||
@ -7,11 +8,24 @@ import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2Clie
|
|||||||
import cn.iocoder.yudao.module.system.convert.auth.OAuth2ClientConvert;
|
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.dataobject.auth.OAuth2ClientDO;
|
||||||
import cn.iocoder.yudao.module.system.dal.mysql.auth.OAuth2ClientMapper;
|
import cn.iocoder.yudao.module.system.dal.mysql.auth.OAuth2ClientMapper;
|
||||||
|
import cn.iocoder.yudao.module.system.mq.producer.auth.OAuth2ClientProducer;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||||
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
||||||
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getMaxValue;
|
||||||
|
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.OAUTH2_CLIENT_EXISTS;
|
||||||
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.OAUTH2_CLIENT_NOT_EXISTS;
|
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.OAUTH2_CLIENT_NOT_EXISTS;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -20,35 +34,113 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.OAUTH2_CLI
|
|||||||
* @author 芋道源码
|
* @author 芋道源码
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
|
@Validated
|
||||||
|
@Slf4j
|
||||||
public class OAuth2ClientServiceImpl implements OAuth2ClientService {
|
public class OAuth2ClientServiceImpl implements OAuth2ClientService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 定时执行 {@link #schedulePeriodicRefresh()} 的周期
|
||||||
|
* 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高
|
||||||
|
*/
|
||||||
|
private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户端缓存
|
||||||
|
* key:客户端编号 {@link OAuth2ClientDO#getClientId()} ()}
|
||||||
|
*
|
||||||
|
* 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
private volatile Map<String, OAuth2ClientDO> clientCache;
|
||||||
|
/**
|
||||||
|
* 缓存角色的最大更新时间,用于后续的增量轮询,判断是否有更新
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
private volatile Date maxUpdateTime;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private OAuth2ClientMapper oauth2ClientMapper;
|
private OAuth2ClientMapper oauth2ClientMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private OAuth2ClientProducer oauth2ClientProducer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化 {@link #clientCache} 缓存
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@PostConstruct
|
||||||
|
public void initLocalCache() {
|
||||||
|
// 获取客户端列表,如果有更新
|
||||||
|
List<OAuth2ClientDO> tenantList = loadOAuth2ClientIfUpdate(maxUpdateTime);
|
||||||
|
if (CollUtil.isEmpty(tenantList)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 写入缓存
|
||||||
|
clientCache = convertMap(tenantList, OAuth2ClientDO::getClientId);
|
||||||
|
maxUpdateTime = getMaxValue(tenantList, OAuth2ClientDO::getUpdateTime);
|
||||||
|
log.info("[initLocalCache][初始化 OAuth2Client 数量为 {}]", tenantList.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)
|
||||||
|
public void schedulePeriodicRefresh() {
|
||||||
|
initLocalCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 如果客户端发生变化,从数据库中获取最新的全量客户端。
|
||||||
|
* 如果未发生变化,则返回空
|
||||||
|
*
|
||||||
|
* @param maxUpdateTime 当前客户端的最大更新时间
|
||||||
|
* @return 客户端列表
|
||||||
|
*/
|
||||||
|
private List<OAuth2ClientDO> loadOAuth2ClientIfUpdate(Date maxUpdateTime) {
|
||||||
|
// 第一步,判断是否要更新。
|
||||||
|
if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据
|
||||||
|
log.info("[loadOAuth2ClientIfUpdate][首次加载全量客户端]");
|
||||||
|
} else { // 判断数据库中是否有更新的客户端
|
||||||
|
if (oauth2ClientMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
log.info("[loadOAuth2ClientIfUpdate][增量加载全量客户端]");
|
||||||
|
}
|
||||||
|
// 第二步,如果有更新,则从数据库加载所有客户端
|
||||||
|
return oauth2ClientMapper.selectList();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long createOAuth2Client(OAuth2ClientCreateReqVO createReqVO) {
|
public Long createOAuth2Client(OAuth2ClientCreateReqVO createReqVO) {
|
||||||
|
validateClientIdExists(null, createReqVO.getClientId());
|
||||||
// 插入
|
// 插入
|
||||||
OAuth2ClientDO oAuth2Client = OAuth2ClientConvert.INSTANCE.convert(createReqVO);
|
OAuth2ClientDO oauth2Client = OAuth2ClientConvert.INSTANCE.convert(createReqVO);
|
||||||
oauth2ClientMapper.insert(oAuth2Client);
|
oauth2ClientMapper.insert(oauth2Client);
|
||||||
// 返回
|
// 发送刷新消息
|
||||||
return oAuth2Client.getId();
|
oauth2ClientProducer.sendOAuth2ClientRefreshMessage();
|
||||||
|
return oauth2Client.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateOAuth2Client(OAuth2ClientUpdateReqVO updateReqVO) {
|
public void updateOAuth2Client(OAuth2ClientUpdateReqVO updateReqVO) {
|
||||||
// 校验存在
|
// 校验存在
|
||||||
this.validateOAuth2ClientExists(updateReqVO.getId());
|
validateOAuth2ClientExists(updateReqVO.getId());
|
||||||
|
// 校验 Client 未被占用
|
||||||
|
validateClientIdExists(updateReqVO.getId(), updateReqVO.getClientId());
|
||||||
|
|
||||||
// 更新
|
// 更新
|
||||||
OAuth2ClientDO updateObj = OAuth2ClientConvert.INSTANCE.convert(updateReqVO);
|
OAuth2ClientDO updateObj = OAuth2ClientConvert.INSTANCE.convert(updateReqVO);
|
||||||
oauth2ClientMapper.updateById(updateObj);
|
oauth2ClientMapper.updateById(updateObj);
|
||||||
|
// 发送刷新消息
|
||||||
|
oauth2ClientProducer.sendOAuth2ClientRefreshMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteOAuth2Client(Long id) {
|
public void deleteOAuth2Client(Long id) {
|
||||||
// 校验存在
|
// 校验存在
|
||||||
this.validateOAuth2ClientExists(id);
|
validateOAuth2ClientExists(id);
|
||||||
// 删除
|
// 删除
|
||||||
oauth2ClientMapper.deleteById(id);
|
oauth2ClientMapper.deleteById(id);
|
||||||
|
// 发送刷新消息
|
||||||
|
oauth2ClientProducer.sendOAuth2ClientRefreshMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateOAuth2ClientExists(Long id) {
|
private void validateOAuth2ClientExists(Long id) {
|
||||||
@ -57,6 +149,21 @@ public class OAuth2ClientServiceImpl implements OAuth2ClientService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public void validateClientIdExists(Long id, String clientId) {
|
||||||
|
OAuth2ClientDO client = oauth2ClientMapper.selectByClientId(clientId);
|
||||||
|
if (client == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 如果 id 为空,说明不用比较是否为相同 id 的客户端
|
||||||
|
if (id == null) {
|
||||||
|
throw exception(OAUTH2_CLIENT_EXISTS);
|
||||||
|
}
|
||||||
|
if (!client.getClientId().equals(clientId)) {
|
||||||
|
throw exception(OAUTH2_CLIENT_EXISTS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OAuth2ClientDO getOAuth2Client(Long id) {
|
public OAuth2ClientDO getOAuth2Client(Long id) {
|
||||||
return oauth2ClientMapper.selectById(id);
|
return oauth2ClientMapper.selectById(id);
|
||||||
@ -68,10 +175,8 @@ public class OAuth2ClientServiceImpl implements OAuth2ClientService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OAuth2ClientDO validOAuthClientFromCache(Long id) {
|
public OAuth2ClientDO validOAuthClientFromCache(String clientId) {
|
||||||
return new OAuth2ClientDO().setId(id)
|
return clientCache.get(clientId);
|
||||||
.setAccessTokenValiditySeconds(60 * 30)
|
|
||||||
.setRefreshTokenValiditySeconds(60 * 60 * 24 * 30);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ public interface OAuth2TokenService {
|
|||||||
* @param clientId 客户端编号
|
* @param clientId 客户端编号
|
||||||
* @return 访问令牌的信息
|
* @return 访问令牌的信息
|
||||||
*/
|
*/
|
||||||
OAuth2AccessTokenDO createAccessToken(Long userId, Integer userType, Long clientId);
|
OAuth2AccessTokenDO createAccessToken(Long userId, Integer userType, String clientId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 刷新访问令牌
|
* 刷新访问令牌
|
||||||
@ -35,7 +35,7 @@ public interface OAuth2TokenService {
|
|||||||
* @param clientId 客户端编号
|
* @param clientId 客户端编号
|
||||||
* @return 访问令牌的信息
|
* @return 访问令牌的信息
|
||||||
*/
|
*/
|
||||||
OAuth2AccessTokenDO refreshAccessToken(String refreshToken, Long clientId);
|
OAuth2AccessTokenDO refreshAccessToken(String refreshToken, String clientId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得访问令牌
|
* 获得访问令牌
|
||||||
|
@ -45,7 +45,7 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public OAuth2AccessTokenDO createAccessToken(Long userId, Integer userType, Long clientId) {
|
public OAuth2AccessTokenDO createAccessToken(Long userId, Integer userType, String clientId) {
|
||||||
OAuth2ClientDO clientDO = oauth2ClientService.validOAuthClientFromCache(clientId);
|
OAuth2ClientDO clientDO = oauth2ClientService.validOAuthClientFromCache(clientId);
|
||||||
// 创建刷新令牌
|
// 创建刷新令牌
|
||||||
OAuth2RefreshTokenDO refreshTokenDO = createOAuth2RefreshToken(userId, userType, clientDO);
|
OAuth2RefreshTokenDO refreshTokenDO = createOAuth2RefreshToken(userId, userType, clientDO);
|
||||||
@ -54,7 +54,7 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OAuth2AccessTokenDO refreshAccessToken(String refreshToken, Long clientId) {
|
public OAuth2AccessTokenDO refreshAccessToken(String refreshToken, String clientId) {
|
||||||
// 查询访问令牌
|
// 查询访问令牌
|
||||||
OAuth2RefreshTokenDO refreshTokenDO = oauth2RefreshTokenMapper.selectByRefreshToken(refreshToken);
|
OAuth2RefreshTokenDO refreshTokenDO = oauth2RefreshTokenMapper.selectByRefreshToken(refreshToken);
|
||||||
if (refreshTokenDO == null) {
|
if (refreshTokenDO == null) {
|
||||||
@ -134,7 +134,7 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
|
|||||||
|
|
||||||
private OAuth2AccessTokenDO createOAuth2AccessToken(OAuth2RefreshTokenDO refreshTokenDO, OAuth2ClientDO clientDO) {
|
private OAuth2AccessTokenDO createOAuth2AccessToken(OAuth2RefreshTokenDO refreshTokenDO, OAuth2ClientDO clientDO) {
|
||||||
OAuth2AccessTokenDO accessTokenDO = new OAuth2AccessTokenDO().setAccessToken(generateAccessToken())
|
OAuth2AccessTokenDO accessTokenDO = new OAuth2AccessTokenDO().setAccessToken(generateAccessToken())
|
||||||
.setUserId(refreshTokenDO.getUserId()).setUserType(refreshTokenDO.getUserType()).setClientId(clientDO.getId())
|
.setUserId(refreshTokenDO.getUserId()).setUserType(refreshTokenDO.getUserType()).setClientId(clientDO.getClientId())
|
||||||
.setRefreshToken(refreshTokenDO.getRefreshToken())
|
.setRefreshToken(refreshTokenDO.getRefreshToken())
|
||||||
.setExpiresTime(DateUtils.addDate(Calendar.SECOND, clientDO.getAccessTokenValiditySeconds()));
|
.setExpiresTime(DateUtils.addDate(Calendar.SECOND, clientDO.getAccessTokenValiditySeconds()));
|
||||||
accessTokenDO.setTenantId(TenantContextHolder.getTenantId()); // 手动设置租户编号,避免缓存到 Redis 的时候,无对应的租户编号
|
accessTokenDO.setTenantId(TenantContextHolder.getTenantId()); // 手动设置租户编号,避免缓存到 Redis 的时候,无对应的租户编号
|
||||||
@ -146,7 +146,7 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
|
|||||||
|
|
||||||
private OAuth2RefreshTokenDO createOAuth2RefreshToken(Long userId, Integer userType, OAuth2ClientDO clientDO) {
|
private OAuth2RefreshTokenDO createOAuth2RefreshToken(Long userId, Integer userType, OAuth2ClientDO clientDO) {
|
||||||
OAuth2RefreshTokenDO refreshToken = new OAuth2RefreshTokenDO().setRefreshToken(generateRefreshToken())
|
OAuth2RefreshTokenDO refreshToken = new OAuth2RefreshTokenDO().setRefreshToken(generateRefreshToken())
|
||||||
.setUserId(userId).setUserType(userType).setClientId(clientDO.getId())
|
.setUserId(userId).setUserType(userType).setClientId(clientDO.getClientId())
|
||||||
.setExpiresTime(DateUtils.addDate(Calendar.SECOND, clientDO.getRefreshTokenValiditySeconds()));
|
.setExpiresTime(DateUtils.addDate(Calendar.SECOND, clientDO.getRefreshTokenValiditySeconds()));
|
||||||
oauth2RefreshTokenMapper.insert(refreshToken);
|
oauth2RefreshTokenMapper.insert(refreshToken);
|
||||||
return refreshToken;
|
return refreshToken;
|
||||||
|
@ -197,7 +197,7 @@ public class AuthServiceImplTest extends BaseDbUnitTest {
|
|||||||
// mock 缓存登录用户到 Redis
|
// mock 缓存登录用户到 Redis
|
||||||
OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class, o -> o.setUserId(1L)
|
OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class, o -> o.setUserId(1L)
|
||||||
.setUserType(UserTypeEnum.ADMIN.getValue()));
|
.setUserType(UserTypeEnum.ADMIN.getValue()));
|
||||||
when(oauth2TokenService.createAccessToken(eq(1L), eq(UserTypeEnum.ADMIN.getValue()), eq(1L)))
|
when(oauth2TokenService.createAccessToken(eq(1L), eq(UserTypeEnum.ADMIN.getValue()), eq("default")))
|
||||||
.thenReturn(accessTokenDO);
|
.thenReturn(accessTokenDO);
|
||||||
|
|
||||||
// 调用, 并断言异常
|
// 调用, 并断言异常
|
||||||
|
@ -8,19 +8,23 @@ import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2Clie
|
|||||||
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2ClientUpdateReqVO;
|
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.dataobject.auth.OAuth2ClientDO;
|
||||||
import cn.iocoder.yudao.module.system.dal.mysql.auth.OAuth2ClientMapper;
|
import cn.iocoder.yudao.module.system.dal.mysql.auth.OAuth2ClientMapper;
|
||||||
|
import cn.iocoder.yudao.module.system.mq.producer.auth.OAuth2ClientProducer;
|
||||||
import org.junit.jupiter.api.Disabled;
|
import org.junit.jupiter.api.Disabled;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
|
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
|
||||||
|
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.max;
|
||||||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
|
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.AssertUtils.assertServiceException;
|
||||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
|
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
|
||||||
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 cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.OAUTH2_CLIENT_NOT_EXISTS;
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link OAuth2ClientServiceImpl} 的单元测试类
|
* {@link OAuth2ClientServiceImpl} 的单元测试类
|
||||||
@ -31,40 +35,66 @@ import static org.junit.jupiter.api.Assertions.*;
|
|||||||
public class OAuth2ClientServiceImplTest extends BaseDbUnitTest {
|
public class OAuth2ClientServiceImplTest extends BaseDbUnitTest {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private OAuth2ClientServiceImpl oAuth2ClientService;
|
private OAuth2ClientServiceImpl oauth2ClientService;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private OAuth2ClientMapper oAuth2ClientMapper;
|
private OAuth2ClientMapper oauth2ClientMapper;
|
||||||
|
|
||||||
|
@MockBean
|
||||||
|
private OAuth2ClientProducer oauth2ClientProducer;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInitLocalCache() {
|
||||||
|
// mock 数据
|
||||||
|
OAuth2ClientDO clientDO1 = randomPojo(OAuth2ClientDO.class);
|
||||||
|
oauth2ClientMapper.insert(clientDO1);
|
||||||
|
OAuth2ClientDO clientDO2 = randomPojo(OAuth2ClientDO.class);
|
||||||
|
oauth2ClientMapper.insert(clientDO2);
|
||||||
|
|
||||||
|
// 调用
|
||||||
|
oauth2ClientService.initLocalCache();
|
||||||
|
// 断言 clientCache 缓存
|
||||||
|
Map<String, OAuth2ClientDO> clientCache = oauth2ClientService.getClientCache();
|
||||||
|
assertEquals(2, clientCache.size());
|
||||||
|
assertPojoEquals(clientDO1, clientCache.get(clientDO1.getClientId()));
|
||||||
|
assertPojoEquals(clientDO2, clientCache.get(clientDO2.getClientId()));
|
||||||
|
// 断言 maxUpdateTime 缓存
|
||||||
|
assertEquals(max(clientDO1.getUpdateTime(), clientDO2.getUpdateTime()), oauth2ClientService.getMaxUpdateTime());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreateOAuth2Client_success() {
|
public void testCreateOAuth2Client_success() {
|
||||||
// 准备参数
|
// 准备参数
|
||||||
OAuth2ClientCreateReqVO reqVO = randomPojo(OAuth2ClientCreateReqVO.class);
|
OAuth2ClientCreateReqVO reqVO = randomPojo(OAuth2ClientCreateReqVO.class,
|
||||||
|
o -> o.setLogo(randomString()));
|
||||||
|
|
||||||
// 调用
|
// 调用
|
||||||
Long oauth2ClientId = oAuth2ClientService.createOAuth2Client(reqVO);
|
Long oauth2ClientId = oauth2ClientService.createOAuth2Client(reqVO);
|
||||||
// 断言
|
// 断言
|
||||||
assertNotNull(oauth2ClientId);
|
assertNotNull(oauth2ClientId);
|
||||||
// 校验记录的属性是否正确
|
// 校验记录的属性是否正确
|
||||||
OAuth2ClientDO oAuth2Client = oAuth2ClientMapper.selectById(oauth2ClientId);
|
OAuth2ClientDO oAuth2Client = oauth2ClientMapper.selectById(oauth2ClientId);
|
||||||
assertPojoEquals(reqVO, oAuth2Client);
|
assertPojoEquals(reqVO, oAuth2Client);
|
||||||
|
verify(oauth2ClientProducer).sendOAuth2ClientRefreshMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUpdateOAuth2Client_success() {
|
public void testUpdateOAuth2Client_success() {
|
||||||
// mock 数据
|
// mock 数据
|
||||||
OAuth2ClientDO dbOAuth2Client = randomPojo(OAuth2ClientDO.class);
|
OAuth2ClientDO dbOAuth2Client = randomPojo(OAuth2ClientDO.class);
|
||||||
oAuth2ClientMapper.insert(dbOAuth2Client);// @Sql: 先插入出一条存在的数据
|
oauth2ClientMapper.insert(dbOAuth2Client);// @Sql: 先插入出一条存在的数据
|
||||||
// 准备参数
|
// 准备参数
|
||||||
OAuth2ClientUpdateReqVO reqVO = randomPojo(OAuth2ClientUpdateReqVO.class, o -> {
|
OAuth2ClientUpdateReqVO reqVO = randomPojo(OAuth2ClientUpdateReqVO.class, o -> {
|
||||||
o.setId(dbOAuth2Client.getId()); // 设置更新的 ID
|
o.setId(dbOAuth2Client.getId()); // 设置更新的 ID
|
||||||
|
o.setLogo(randomString());
|
||||||
});
|
});
|
||||||
|
|
||||||
// 调用
|
// 调用
|
||||||
oAuth2ClientService.updateOAuth2Client(reqVO);
|
oauth2ClientService.updateOAuth2Client(reqVO);
|
||||||
// 校验是否更新正确
|
// 校验是否更新正确
|
||||||
OAuth2ClientDO oAuth2Client = oAuth2ClientMapper.selectById(reqVO.getId()); // 获取最新的
|
OAuth2ClientDO oAuth2Client = oauth2ClientMapper.selectById(reqVO.getId()); // 获取最新的
|
||||||
assertPojoEquals(reqVO, oAuth2Client);
|
assertPojoEquals(reqVO, oAuth2Client);
|
||||||
|
verify(oauth2ClientProducer).sendOAuth2ClientRefreshMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -73,21 +103,22 @@ public class OAuth2ClientServiceImplTest extends BaseDbUnitTest {
|
|||||||
OAuth2ClientUpdateReqVO reqVO = randomPojo(OAuth2ClientUpdateReqVO.class);
|
OAuth2ClientUpdateReqVO reqVO = randomPojo(OAuth2ClientUpdateReqVO.class);
|
||||||
|
|
||||||
// 调用, 并断言异常
|
// 调用, 并断言异常
|
||||||
assertServiceException(() -> oAuth2ClientService.updateOAuth2Client(reqVO), OAUTH2_CLIENT_NOT_EXISTS);
|
assertServiceException(() -> oauth2ClientService.updateOAuth2Client(reqVO), OAUTH2_CLIENT_NOT_EXISTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDeleteOAuth2Client_success() {
|
public void testDeleteOAuth2Client_success() {
|
||||||
// mock 数据
|
// mock 数据
|
||||||
OAuth2ClientDO dbOAuth2Client = randomPojo(OAuth2ClientDO.class);
|
OAuth2ClientDO dbOAuth2Client = randomPojo(OAuth2ClientDO.class);
|
||||||
oAuth2ClientMapper.insert(dbOAuth2Client);// @Sql: 先插入出一条存在的数据
|
oauth2ClientMapper.insert(dbOAuth2Client);// @Sql: 先插入出一条存在的数据
|
||||||
// 准备参数
|
// 准备参数
|
||||||
Long id = dbOAuth2Client.getId();
|
Long id = dbOAuth2Client.getId();
|
||||||
|
|
||||||
// 调用
|
// 调用
|
||||||
oAuth2ClientService.deleteOAuth2Client(id);
|
oauth2ClientService.deleteOAuth2Client(id);
|
||||||
// 校验数据不存在了
|
// 校验数据不存在了
|
||||||
assertNull(oAuth2ClientMapper.selectById(id));
|
assertNull(oauth2ClientMapper.selectById(id));
|
||||||
|
verify(oauth2ClientProducer).sendOAuth2ClientRefreshMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -96,7 +127,7 @@ public class OAuth2ClientServiceImplTest extends BaseDbUnitTest {
|
|||||||
Long id = randomLongId();
|
Long id = randomLongId();
|
||||||
|
|
||||||
// 调用, 并断言异常
|
// 调用, 并断言异常
|
||||||
assertServiceException(() -> oAuth2ClientService.deleteOAuth2Client(id), OAUTH2_CLIENT_NOT_EXISTS);
|
assertServiceException(() -> oauth2ClientService.deleteOAuth2Client(id), OAUTH2_CLIENT_NOT_EXISTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -107,18 +138,18 @@ public class OAuth2ClientServiceImplTest extends BaseDbUnitTest {
|
|||||||
o.setName("潜龙");
|
o.setName("潜龙");
|
||||||
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||||
});
|
});
|
||||||
oAuth2ClientMapper.insert(dbOAuth2Client);
|
oauth2ClientMapper.insert(dbOAuth2Client);
|
||||||
// 测试 name 不匹配
|
// 测试 name 不匹配
|
||||||
oAuth2ClientMapper.insert(cloneIgnoreId(dbOAuth2Client, o -> o.setName("凤凰")));
|
oauth2ClientMapper.insert(cloneIgnoreId(dbOAuth2Client, o -> o.setName("凤凰")));
|
||||||
// 测试 status 不匹配
|
// 测试 status 不匹配
|
||||||
oAuth2ClientMapper.insert(cloneIgnoreId(dbOAuth2Client, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())));
|
oauth2ClientMapper.insert(cloneIgnoreId(dbOAuth2Client, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())));
|
||||||
// 准备参数
|
// 准备参数
|
||||||
OAuth2ClientPageReqVO reqVO = new OAuth2ClientPageReqVO();
|
OAuth2ClientPageReqVO reqVO = new OAuth2ClientPageReqVO();
|
||||||
reqVO.setName("long");
|
reqVO.setName("long");
|
||||||
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||||
|
|
||||||
// 调用
|
// 调用
|
||||||
PageResult<OAuth2ClientDO> pageResult = oAuth2ClientService.getOAuth2ClientPage(reqVO);
|
PageResult<OAuth2ClientDO> pageResult = oauth2ClientService.getOAuth2ClientPage(reqVO);
|
||||||
// 断言
|
// 断言
|
||||||
assertEquals(1, pageResult.getTotal());
|
assertEquals(1, pageResult.getTotal());
|
||||||
assertEquals(1, pageResult.getList().size());
|
assertEquals(1, pageResult.getList().size());
|
||||||
|
@ -473,6 +473,7 @@ CREATE TABLE IF NOT EXISTS "system_sensitive_word" (
|
|||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS "system_oauth2_client" (
|
CREATE TABLE IF NOT EXISTS "system_oauth2_client" (
|
||||||
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||||
|
"client_id" varchar NOT NULL,
|
||||||
"secret" varchar NOT NULL,
|
"secret" varchar NOT NULL,
|
||||||
"name" varchar NOT NULL,
|
"name" varchar NOT NULL,
|
||||||
"logo" varchar NOT NULL,
|
"logo" varchar NOT NULL,
|
||||||
@ -481,6 +482,12 @@ CREATE TABLE IF NOT EXISTS "system_oauth2_client" (
|
|||||||
"access_token_validity_seconds" int NOT NULL,
|
"access_token_validity_seconds" int NOT NULL,
|
||||||
"refresh_token_validity_seconds" int NOT NULL,
|
"refresh_token_validity_seconds" int NOT NULL,
|
||||||
"redirect_uris" varchar NOT NULL,
|
"redirect_uris" varchar NOT NULL,
|
||||||
|
"auto_approve" bit NOT NULL DEFAULT FALSE,
|
||||||
|
"authorized_grant_types" varchar NOT NULL,
|
||||||
|
"scopes" varchar NOT NULL DEFAULT '',
|
||||||
|
"authorities" varchar NOT NULL DEFAULT '',
|
||||||
|
"resource_ids" varchar NOT NULL DEFAULT '',
|
||||||
|
"additional_information" varchar NOT NULL DEFAULT '',
|
||||||
"creator" varchar DEFAULT '',
|
"creator" varchar DEFAULT '',
|
||||||
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
"updater" varchar DEFAULT '',
|
"updater" varchar DEFAULT '',
|
||||||
|
@ -499,7 +499,7 @@ export const selectComponents = [
|
|||||||
__slot__: {
|
__slot__: {
|
||||||
'list-type': true
|
'list-type': true
|
||||||
},
|
},
|
||||||
action: 'https://jsonplaceholder.typicode.com/posts/',
|
action: process.env.VUE_APP_BASE_API + "/admin-api/infra/file/upload", // 请求地址
|
||||||
disabled: false,
|
disabled: false,
|
||||||
accept: '',
|
accept: '',
|
||||||
name: 'file',
|
name: 'file',
|
||||||
|
@ -23,6 +23,7 @@ export const DICT_TYPE = {
|
|||||||
SYSTEM_SMS_SEND_STATUS: 'system_sms_send_status',
|
SYSTEM_SMS_SEND_STATUS: 'system_sms_send_status',
|
||||||
SYSTEM_SMS_RECEIVE_STATUS: 'system_sms_receive_status',
|
SYSTEM_SMS_RECEIVE_STATUS: 'system_sms_receive_status',
|
||||||
SYSTEM_ERROR_CODE_TYPE: 'system_error_code_type',
|
SYSTEM_ERROR_CODE_TYPE: 'system_error_code_type',
|
||||||
|
SYSTEM_OAUTH2_GRANT_TYPE: 'system_oauth2_grant_type',
|
||||||
|
|
||||||
// ========== INFRA 模块 ==========
|
// ========== INFRA 模块 ==========
|
||||||
INFRA_BOOLEAN_STRING: 'infra_boolean_string',
|
INFRA_BOOLEAN_STRING: 'infra_boolean_string',
|
||||||
|
@ -29,19 +29,32 @@
|
|||||||
|
|
||||||
<!-- 列表 -->
|
<!-- 列表 -->
|
||||||
<el-table v-loading="loading" :data="list">
|
<el-table v-loading="loading" :data="list">
|
||||||
<el-table-column label="客户端编号" align="center" prop="id" />
|
<el-table-column label="客户端编号" align="center" prop="clientId" />
|
||||||
<el-table-column label="客户端密钥" align="center" prop="secret" />
|
<el-table-column label="客户端密钥" align="center" prop="secret" />
|
||||||
<el-table-column label="应用名" align="center" prop="name" />
|
<el-table-column label="应用名" align="center" prop="name" />
|
||||||
<el-table-column label="应用图标" align="center" prop="logo" />
|
<el-table-column label="应用图标" align="center" prop="logo">
|
||||||
<el-table-column label="应用描述" align="center" prop="description" />
|
<template slot-scope="scope">
|
||||||
|
<img width="40px" height="40px" :src="scope.row.logo">
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="状态" align="center" prop="status">
|
<el-table-column label="状态" align="center" prop="status">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
|
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="访问令牌的有效期" align="center" prop="accessTokenValiditySeconds" />
|
<el-table-column label="访问令牌的有效期" align="center" prop="accessTokenValiditySeconds">
|
||||||
<el-table-column label="刷新令牌的有效期" align="center" prop="refreshTokenValiditySeconds" />
|
<template slot-scope="scope">{{ scope.row.accessTokenValiditySeconds }} 秒</template>
|
||||||
<el-table-column label="可重定向的 URI 地址" align="center" prop="redirectUris" />
|
</el-table-column>
|
||||||
|
<el-table-column label="刷新令牌的有效期" align="center" prop="refreshTokenValiditySeconds">
|
||||||
|
<template slot-scope="scope">{{ scope.row.refreshTokenValiditySeconds }} 秒</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="授权类型" align="center" prop="authorizedGrantTypes">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-tag :disable-transitions="true" v-for="(authorizedGrantType, index) in scope.row.authorizedGrantTypes" :index="index">
|
||||||
|
{{ authorizedGrantType }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||||
@ -63,6 +76,9 @@
|
|||||||
<!-- 对话框(添加 / 修改) -->
|
<!-- 对话框(添加 / 修改) -->
|
||||||
<el-dialog :title="title" :visible.sync="open" width="700px" append-to-body>
|
<el-dialog :title="title" :visible.sync="open" width="700px" append-to-body>
|
||||||
<el-form ref="form" :model="form" :rules="rules" label-width="160px">
|
<el-form ref="form" :model="form" :rules="rules" label-width="160px">
|
||||||
|
<el-form-item label="客户端编号" prop="secret">
|
||||||
|
<el-input v-model="form.clientId" placeholder="请输入客户端编号" />
|
||||||
|
</el-form-item>
|
||||||
<el-form-item label="客户端密钥" prop="secret">
|
<el-form-item label="客户端密钥" prop="secret">
|
||||||
<el-input v-model="form.secret" placeholder="请输入客户端密钥" />
|
<el-input v-model="form.secret" placeholder="请输入客户端密钥" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@ -70,8 +86,7 @@
|
|||||||
<el-input v-model="form.name" placeholder="请输入应用名" />
|
<el-input v-model="form.name" placeholder="请输入应用名" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="应用图标">
|
<el-form-item label="应用图标">
|
||||||
<!-- <imageUpload v-model="form.logo" :limit="1"/>-->
|
<imageUpload v-model="form.logo" :limit="1"/>
|
||||||
<file-upload v-model="form.logo" :limit="1"/>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="应用描述">
|
<el-form-item label="应用描述">
|
||||||
<el-input type="textarea" v-model="form.description" placeholder="请输入应用名" />
|
<el-input type="textarea" v-model="form.description" placeholder="请输入应用名" />
|
||||||
@ -89,7 +104,39 @@
|
|||||||
<el-input-number v-model="form.refreshTokenValiditySeconds" placeholder="单位:秒" />
|
<el-input-number v-model="form.refreshTokenValiditySeconds" placeholder="单位:秒" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="可重定向的 URI 地址" prop="redirectUris">
|
<el-form-item label="可重定向的 URI 地址" prop="redirectUris">
|
||||||
<el-input v-model="form.redirectUris" placeholder="请输入可重定向的 URI 地址" />
|
<el-select v-model="form.redirectUris" multiple filterable allow-create placeholder="请输入可重定向的 URI 地址" style="width: 500px" >
|
||||||
|
<el-option v-for="redirectUri in form.redirectUris" :key="redirectUri" :label="redirectUri" :value="redirectUri"/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="是否自动授权" prop="autoApprove">
|
||||||
|
<el-radio-group v-model="form.autoApprove">
|
||||||
|
<el-radio :key="true" :label="true">自动登录</el-radio>
|
||||||
|
<el-radio :key="false" :label="false">手动登录</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="授权类型" prop="authorizedGrantTypes">
|
||||||
|
<el-select v-model="form.authorizedGrantTypes" multiple filterable placeholder="请输入授权类型" style="width: 500px" >
|
||||||
|
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_OAUTH2_GRANT_TYPE)"
|
||||||
|
:key="dict.value" :label="dict.label" :value="dict.value"/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="授权范围" prop="scopes">
|
||||||
|
<el-select v-model="form.scopes" multiple filterable allow-create placeholder="请输入授权范围" style="width: 500px" >
|
||||||
|
<el-option v-for="scope in form.scopes" :key="scope" :label="scope" :value="scope"/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="权限" prop="authorities">
|
||||||
|
<el-select v-model="form.authorities" multiple filterable allow-create placeholder="请输入权限" style="width: 500px" >
|
||||||
|
<el-option v-for="authority in form.authorities" :key="authority" :label="authority" :value="authority"/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="资源" prop="resourceIds">
|
||||||
|
<el-select v-model="form.resourceIds" multiple filterable allow-create placeholder="请输入资源" style="width: 500px" >
|
||||||
|
<el-option v-for="resourceId in form.resourceIds" :key="resourceId" :label="resourceId" :value="resourceId"/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="附加信息" prop="additionalInformation">
|
||||||
|
<el-input type="textarea" v-model="form.additionalInformation" placeholder="请输入附加信息,JSON 格式数据" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<div slot="footer" class="dialog-footer">
|
<div slot="footer" class="dialog-footer">
|
||||||
@ -141,6 +188,7 @@ export default {
|
|||||||
form: {},
|
form: {},
|
||||||
// 表单校验
|
// 表单校验
|
||||||
rules: {
|
rules: {
|
||||||
|
clientId: [{ required: true, message: "客户端编号不能为空", trigger: "blur" }],
|
||||||
secret: [{ required: true, message: "客户端密钥不能为空", trigger: "blur" }],
|
secret: [{ required: true, message: "客户端密钥不能为空", trigger: "blur" }],
|
||||||
name: [{ required: true, message: "应用名不能为空", trigger: "blur" }],
|
name: [{ required: true, message: "应用名不能为空", trigger: "blur" }],
|
||||||
logo: [{ required: true, message: "应用图标不能为空", trigger: "blur" }],
|
logo: [{ required: true, message: "应用图标不能为空", trigger: "blur" }],
|
||||||
@ -148,6 +196,8 @@ export default {
|
|||||||
accessTokenValiditySeconds: [{ required: true, message: "访问令牌的有效期不能为空", trigger: "blur" }],
|
accessTokenValiditySeconds: [{ required: true, message: "访问令牌的有效期不能为空", trigger: "blur" }],
|
||||||
refreshTokenValiditySeconds: [{ required: true, message: "刷新令牌的有效期不能为空", trigger: "blur" }],
|
refreshTokenValiditySeconds: [{ required: true, message: "刷新令牌的有效期不能为空", trigger: "blur" }],
|
||||||
redirectUris: [{ required: true, message: "可重定向的 URI 地址不能为空", trigger: "blur" }],
|
redirectUris: [{ required: true, message: "可重定向的 URI 地址不能为空", trigger: "blur" }],
|
||||||
|
autoApprove: [{ required: true, message: "是否自动授权不能为空", trigger: "blur" }],
|
||||||
|
authorizedGrantTypes: [{ required: true, message: "授权类型不能为空", trigger: "blur" }],
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@ -176,14 +226,21 @@ export default {
|
|||||||
reset() {
|
reset() {
|
||||||
this.form = {
|
this.form = {
|
||||||
id: undefined,
|
id: undefined,
|
||||||
|
clientId: undefined,
|
||||||
secret: undefined,
|
secret: undefined,
|
||||||
name: undefined,
|
name: undefined,
|
||||||
logo: undefined,
|
logo: undefined,
|
||||||
description: undefined,
|
description: undefined,
|
||||||
status: CommonStatusEnum.ENABLE,
|
status: CommonStatusEnum.ENABLE,
|
||||||
accessTokenValiditySeconds: undefined,
|
accessTokenValiditySeconds: 30 * 60,
|
||||||
refreshTokenValiditySeconds: undefined,
|
refreshTokenValiditySeconds: 30 * 24 * 60,
|
||||||
redirectUris: undefined,
|
redirectUris: [],
|
||||||
|
autoApprove: true,
|
||||||
|
authorizedGrantTypes: [],
|
||||||
|
scopes: [],
|
||||||
|
authorities: [],
|
||||||
|
resourceIds: [],
|
||||||
|
additionalInformation: undefined,
|
||||||
};
|
};
|
||||||
this.resetForm("form");
|
this.resetForm("form");
|
||||||
},
|
},
|
||||||
@ -239,7 +296,7 @@ export default {
|
|||||||
/** 删除按钮操作 */
|
/** 删除按钮操作 */
|
||||||
handleDelete(row) {
|
handleDelete(row) {
|
||||||
const id = row.id;
|
const id = row.id;
|
||||||
this.$modal.confirm('是否确认删除 OAuth2 客户端编号为"' + id + '"的数据项?').then(function() {
|
this.$modal.confirm('是否确认删除客户端编号为"' + row.clientId + '"的数据项?').then(function() {
|
||||||
return deleteOAuth2Client(id);
|
return deleteOAuth2Client(id);
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
this.getList();
|
this.getList();
|
||||||
|
Loading…
Reference in New Issue
Block a user