增加 auth 授权相关处理(未完成)
This commit is contained in:
parent
6f37500f62
commit
a545d673ab
@ -12,6 +12,10 @@ public class CollectionUtil {
|
|||||||
return collection == null || collection.isEmpty();
|
return collection == null || collection.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isEmpty(Object[] arrays) {
|
||||||
|
return arrays == null || arrays.length == 0;
|
||||||
|
}
|
||||||
|
|
||||||
public static <T> Set<T> asSet(T... objs) {
|
public static <T> Set<T> asSet(T... objs) {
|
||||||
return new HashSet<>(Arrays.asList(objs));
|
return new HashSet<>(Arrays.asList(objs));
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ import cn.iocoder.mall.web.core.constant.CommonMallConstants;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
@ -22,16 +21,22 @@ public class CommonSecurityAutoConfiguration implements WebMvcConfigurer {
|
|||||||
|
|
||||||
// ========== 拦截器相关 ==========
|
// ========== 拦截器相关 ==========
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean(AccountAuthInterceptor.class)
|
public AccountAuthInterceptor adminAccountAuthInterceptor() {
|
||||||
public AccountAuthInterceptor accountAuthInterceptor() {
|
return new AccountAuthInterceptor(true);
|
||||||
return new AccountAuthInterceptor();
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public AccountAuthInterceptor userAccountAuthInterceptor() {
|
||||||
|
return new AccountAuthInterceptor(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addInterceptors(InterceptorRegistry registry) {
|
public void addInterceptors(InterceptorRegistry registry) {
|
||||||
// AccountAuthInterceptor 拦截器
|
// AccountAuthInterceptor 拦截器
|
||||||
registry.addInterceptor(this.accountAuthInterceptor())
|
registry.addInterceptor(this.userAccountAuthInterceptor())
|
||||||
.addPathPatterns(CommonMallConstants.ROOT_PATH_ADMIN + "/**", CommonMallConstants.ROOT_PATH_USER + "/**");
|
.addPathPatterns(CommonMallConstants.ROOT_PATH_USER + "/**");
|
||||||
|
registry.addInterceptor(this.adminAccountAuthInterceptor())
|
||||||
|
.addPathPatterns(CommonMallConstants.ROOT_PATH_ADMIN + "/**");
|
||||||
logger.info("[addInterceptors][加载 AccountAuthInterceptor 拦截器完成]");
|
logger.info("[addInterceptors][加载 AccountAuthInterceptor 拦截器完成]");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,14 +3,16 @@ package cn.iocoder.mall.security.core.annotation;
|
|||||||
import java.lang.annotation.*;
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 要求用户登录注解。通过将该注解添加到 Controller 上,会自动校验用户是否登陆。
|
* 要求用户认证(登陆)注解。通过将该注解添加到 Controller 上,会自动校验用户是否登陆。
|
||||||
*
|
*
|
||||||
* 默认请求下,用户访问的 API 接口,无需登陆。主要的考虑是,
|
* 默认请求下,用户访问的 API 接口,无需登陆。主要的考虑是,
|
||||||
* 1. 需要用户登陆的接口,本身会获取在线用户的编号。如果不添加 @RequiresLogin 注解就会报错。
|
* 1. 需要用户登陆的接口,本身会获取在线用户的编号。如果不添加 @RequiresLogin 注解就会报错。
|
||||||
* 2. 大多数情况下,用户的 API 接口无需登陆。
|
* 2. 大多数情况下,用户的 API 接口无需登陆。
|
||||||
|
*
|
||||||
|
* ps:同样适用于管理员 Admin
|
||||||
*/
|
*/
|
||||||
@Documented
|
@Documented
|
||||||
@Target({ElementType.METHOD}) // 暂时不支持 ElementType.TYPE ,因为没有场景
|
@Target({ElementType.METHOD}) // 暂时不支持 ElementType.TYPE ,因为没有场景
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
public @interface RequiresLogin {
|
public @interface RequiresAuthenticate {
|
||||||
}
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package cn.iocoder.mall.security.core.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过将该注解添加到 Controller 的方法上,声明无需进行登陆
|
||||||
|
*/
|
||||||
|
@Documented
|
||||||
|
@Target({ElementType.METHOD}) // 暂时不支持 ElementType.TYPE ,因为没有场景
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface RequiresNone {
|
||||||
|
}
|
@ -6,6 +6,8 @@ import java.lang.annotation.*;
|
|||||||
* 参考 Shiro @RequiresPermissions 设计 http://shiro.apache.org/static/1.3.2/apidocs/org/apache/shiro/authz/annotation/RequiresPermissions.html
|
* 参考 Shiro @RequiresPermissions 设计 http://shiro.apache.org/static/1.3.2/apidocs/org/apache/shiro/authz/annotation/RequiresPermissions.html
|
||||||
*
|
*
|
||||||
* 通过将该注解添加到 Controller 的方法上,进行授权鉴定
|
* 通过将该注解添加到 Controller 的方法上,进行授权鉴定
|
||||||
|
*
|
||||||
|
* ps:目前暂时只有管理员 Admin 使用到
|
||||||
*/
|
*/
|
||||||
@Documented
|
@Documented
|
||||||
@Target({ElementType.METHOD}) // 暂时不支持 ElementType.TYPE ,因为没有场景
|
@Target({ElementType.METHOD}) // 暂时不支持 ElementType.TYPE ,因为没有场景
|
||||||
|
@ -3,10 +3,8 @@ package cn.iocoder.mall.security.core.context;
|
|||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Security 上下文
|
* Admin Security 上下文
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@Accessors(chain = true)
|
@Accessors(chain = true)
|
||||||
@ -20,9 +18,5 @@ public class AdminSecurityContext {
|
|||||||
* 管理员账号
|
* 管理员账号
|
||||||
*/
|
*/
|
||||||
private String username;
|
private String username;
|
||||||
/**
|
|
||||||
* 拥有的角色编号
|
|
||||||
*/
|
|
||||||
private Set<Integer> roleIds;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,13 @@
|
|||||||
package cn.iocoder.mall.security.core.interceptor;
|
package cn.iocoder.mall.security.core.interceptor;
|
||||||
|
|
||||||
|
import cn.iocoder.common.framework.util.CollectionUtil;
|
||||||
import cn.iocoder.common.framework.util.HttpUtil;
|
import cn.iocoder.common.framework.util.HttpUtil;
|
||||||
import cn.iocoder.common.framework.util.ServiceExceptionUtil;
|
import cn.iocoder.common.framework.util.ServiceExceptionUtil;
|
||||||
import cn.iocoder.common.framework.vo.CommonResult;
|
import cn.iocoder.common.framework.vo.CommonResult;
|
||||||
|
import cn.iocoder.mall.security.core.annotation.RequiresAuthenticate;
|
||||||
|
import cn.iocoder.mall.security.core.annotation.RequiresNone;
|
||||||
|
import cn.iocoder.mall.security.core.annotation.RequiresPermissions;
|
||||||
|
import cn.iocoder.mall.system.biz.enums.SystemErrorCodeEnum;
|
||||||
import cn.iocoder.mall.system.rpc.api.oauth2.OAuth2RPC;
|
import cn.iocoder.mall.system.rpc.api.oauth2.OAuth2RPC;
|
||||||
import cn.iocoder.mall.system.rpc.request.oauth2.OAuth2AccessTokenAuthenticateRequest;
|
import cn.iocoder.mall.system.rpc.request.oauth2.OAuth2AccessTokenAuthenticateRequest;
|
||||||
import cn.iocoder.mall.system.rpc.response.oauth2.OAuth2AccessTokenResponse;
|
import cn.iocoder.mall.system.rpc.response.oauth2.OAuth2AccessTokenResponse;
|
||||||
@ -11,6 +16,7 @@ import org.apache.dubbo.config.annotation.Reference;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.web.method.HandlerMethod;
|
||||||
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
|
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
@ -23,13 +29,37 @@ public class AccountAuthInterceptor extends HandlerInterceptorAdapter {
|
|||||||
@Reference(validation = "true", version = "${dubbo.consumer.OAuth2RPC.version}")
|
@Reference(validation = "true", version = "${dubbo.consumer.OAuth2RPC.version}")
|
||||||
private OAuth2RPC oauth2RPC;
|
private OAuth2RPC oauth2RPC;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否默认要求认证
|
||||||
|
*
|
||||||
|
* 针对 /users/** 接口,一般默认不要求认证,因为面向用户的接口,往往不需要登陆即可访问
|
||||||
|
* 针对 /admins/** 接口,一般默认要求认证,因为面向管理员的接口,往往是内部需要更严格的安全控制
|
||||||
|
*/
|
||||||
|
private final boolean defaultRequiresAuthenticate;
|
||||||
|
|
||||||
|
public AccountAuthInterceptor(boolean defaultRequiresAuthenticate) {
|
||||||
|
this.defaultRequiresAuthenticate = defaultRequiresAuthenticate;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
||||||
// 获得访问令牌
|
// 1. 进行认证
|
||||||
String accessToken = HttpUtil.obtainAuthorization(request);
|
Integer accountId = this.obtainAccount(request);
|
||||||
if (StringUtils.hasText(accessToken)) { // 如果未传递,则不进行认证
|
// 2. 进行鉴权
|
||||||
|
HandlerMethod handlerMethod = (HandlerMethod) handler;
|
||||||
|
// 判断是否需要认证
|
||||||
|
this.checkAuthenticate(handlerMethod, accountId);
|
||||||
|
// 判断是否需要权限
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Integer obtainAccount(HttpServletRequest request) {
|
||||||
|
String accessToken = HttpUtil.obtainAuthorization(request); // 获得访问令牌
|
||||||
|
if (!StringUtils.hasText(accessToken)) { // 如果未传递,则不进行认证
|
||||||
|
return null;
|
||||||
|
}
|
||||||
// 执行认证
|
// 执行认证
|
||||||
OAuth2AccessTokenAuthenticateRequest oauth2AccessTokenAuthenticateRequest = new OAuth2AccessTokenAuthenticateRequest()
|
OAuth2AccessTokenAuthenticateRequest oauth2AccessTokenAuthenticateRequest = new OAuth2AccessTokenAuthenticateRequest()
|
||||||
.setAccessToken(accessToken).setIp(HttpUtil.getIp(request));
|
.setAccessToken(accessToken).setIp(HttpUtil.getIp(request));
|
||||||
@ -38,8 +68,35 @@ public class AccountAuthInterceptor extends HandlerInterceptorAdapter {
|
|||||||
throw ServiceExceptionUtil.exception(oauth2AccessTokenResponseResult);
|
throw ServiceExceptionUtil.exception(oauth2AccessTokenResponseResult);
|
||||||
}
|
}
|
||||||
// 设置账号编号
|
// 设置账号编号
|
||||||
CommonWebUtil.setAccountId(request, oauth2AccessTokenResponseResult.getData().getAccountId());
|
Integer accountId = oauth2AccessTokenResponseResult.getData().getAccountId();
|
||||||
return true;
|
CommonWebUtil.setAccountId(request, accountId);
|
||||||
|
return accountId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkAuthenticate(HandlerMethod handlerMethod, Integer accountId) {
|
||||||
|
boolean requiresAuthenticate = defaultRequiresAuthenticate;
|
||||||
|
if (handlerMethod.hasMethodAnnotation(RequiresAuthenticate.class)
|
||||||
|
|| handlerMethod.hasMethodAnnotation(RequiresPermissions.class)) { // 如果需要权限验证,也认为需要认证
|
||||||
|
requiresAuthenticate = true;
|
||||||
|
} else if (handlerMethod.hasMethodAnnotation(RequiresNone.class)) {
|
||||||
|
requiresAuthenticate = false;
|
||||||
|
}
|
||||||
|
if (requiresAuthenticate && accountId == null) {
|
||||||
|
throw ServiceExceptionUtil.exception(SystemErrorCodeEnum.OAUTH2_NOT_AUTHENTICATE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkPermission(HandlerMethod handlerMethod, Integer accountId) {
|
||||||
|
RequiresPermissions requiresPermissions = handlerMethod.getMethodAnnotation(RequiresPermissions.class);
|
||||||
|
if (requiresPermissions == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String[] permissions = requiresPermissions.value();
|
||||||
|
if (CollectionUtil.isEmpty(permissions)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 权限验证
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
package cn.iocoder.mall.security.core.interceptor;
|
||||||
|
|
||||||
|
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
public class AdminSecurityInterceptor extends HandlerInterceptorAdapter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||||
|
// 获得 Admin 信息
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package cn.iocoder.mall.security.core.interceptor;
|
||||||
|
|
||||||
|
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
public class UserSecurityInterceptor extends HandlerInterceptorAdapter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||||
|
// 获得用户信息
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
|
||||||
|
super.afterCompletion(request, response, handler, ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -46,13 +46,4 @@ public class AdminMVCAutoConfiguration implements WebMvcConfigurer {
|
|||||||
registry.addInterceptor(adminDemoInterceptor()).addPathPatterns(MallConstants.ROOT_PATH_ADMIN + "/**");
|
registry.addInterceptor(adminDemoInterceptor()).addPathPatterns(MallConstants.ROOT_PATH_ADMIN + "/**");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
|
||||||
@ConditionalOnMissingBean
|
|
||||||
public FilterRegistrationBean<CorsFilter> corsFilter() {
|
|
||||||
FilterRegistrationBean<CorsFilter> registrationBean = new FilterRegistrationBean<>();
|
|
||||||
registrationBean.setFilter(new CorsFilter());
|
|
||||||
registrationBean.addUrlPatterns("/*");
|
|
||||||
return registrationBean;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
<module>system-rpc</module>
|
<module>system-rpc</module>
|
||||||
<module>system-rest</module>
|
<module>system-rest</module>
|
||||||
<module>system-biz</module>
|
<module>system-biz</module>
|
||||||
|
<module>system-biz-api</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
|
23
system/system-biz-api/pom.xml
Normal file
23
system/system-biz-api/pom.xml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>system</artifactId>
|
||||||
|
<groupId>cn.iocoder.mall</groupId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>system-biz-api</artifactId>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- Mall 相关 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.iocoder.mall</groupId>
|
||||||
|
<artifactId>common-framework</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
@ -11,24 +11,22 @@ public enum SystemErrorCodeEnum implements ServiceExceptionUtil.Enumerable {
|
|||||||
|
|
||||||
// ========== OAUTH2 模块 ==========
|
// ========== OAUTH2 模块 ==========
|
||||||
OAUTH2_UNKNOWN(1001001000, "未知错误"), // 预留
|
OAUTH2_UNKNOWN(1001001000, "未知错误"), // 预留
|
||||||
OAUTH2_ACCOUNT_NOT_FOUND(1001001001, "账号不存在"),
|
// 预留 1001001001 ~ 1001001099 错误码,方便前端
|
||||||
OAUTH2_ACCOUNT_PASSWORD_ERROR(1001001002, "密码不正确"),
|
OAUTH2_ACCESS_TOKEN_NOT_FOUND(1001001001, "访问令牌不存在"),
|
||||||
OAUTH2_INVALID_TOKEN_NOT_FOUND(1002001011, "访问令牌不存在"),
|
OAUTH2_ACCESS_TOKEN_TOKEN_EXPIRED(1001001002, "访问令牌已过期"),
|
||||||
OAUTH2_INVALID_TOKEN_EXPIRED(1002001012, "访问令牌已过期"),
|
OAUTH2_ACCESS_TOKEN_INVALID(1001001003, "访问令牌已失效"),
|
||||||
OAUTH2_INVALID_TOKEN_INVALID(1002001013, "访问令牌已失效"),
|
OAUTH2_NOT_AUTHENTICATE(1001001004, "账号未登陆"),
|
||||||
// OAUTH2_NOT_LOGIN(1002001015, "账号未登陆"),
|
// 其它 1001001100 开始
|
||||||
// OAUTH_INVALID_REFRESH_TOKEN_NOT_FOUND(1002001017, "刷新令牌不存在"),
|
OAUTH2_ACCOUNT_NOT_FOUND(1001001100, "账号不存在"),
|
||||||
// OAUTH_INVALID_REFRESH_TOKEN_EXPIRED(1002001018, "访问令牌已过期"),
|
OAUTH2_ACCOUNT_PASSWORD_ERROR(1001001101, "密码不正确"),
|
||||||
|
|
||||||
|
|
||||||
// ========== OAuth 手机验证码模块 ==========
|
// ========== OAuth 手机验证码模块 ==========
|
||||||
OAUTH2_MOBILE_CODE_NOT_FOUND(1001001100, "验证码不存在"),
|
OAUTH2_MOBILE_CODE_NOT_FOUND(1001001200, "验证码不存在"),
|
||||||
OAUTH2_MOBILE_CODE_EXPIRED(1001001101, "验证码已过期"),
|
OAUTH2_MOBILE_CODE_EXPIRED(1001001201, "验证码已过期"),
|
||||||
OAUTH2_MOBILE_CODE_USED(1001001102, "验证码已使用"),
|
OAUTH2_MOBILE_CODE_USED(1001001202, "验证码已使用"),
|
||||||
OAUTH2_MOBILE_CODE_NOT_CORRECT(1001001104, "验证码不正确"),
|
OAUTH2_MOBILE_CODE_NOT_CORRECT(1001001203, "验证码不正确"),
|
||||||
OAUTH2_MOBILE_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY(1001001105, "超过每日短信发送数量"),
|
OAUTH2_MOBILE_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY(1001001204, "超过每日短信发送数量"),
|
||||||
OAUTH2_MOBILE_CODE_SEND_TOO_FAST(1001001106, "短信发送过于频率"),
|
OAUTH2_MOBILE_CODE_SEND_TOO_FAST(1001001205, "短信发送过于频率"),
|
||||||
|
|
||||||
|
|
||||||
// ========== 管理员模块 1002002000 ==========
|
// ========== 管理员模块 1002002000 ==========
|
||||||
ADMIN_NOT_FOUND(1002002000, "管理员不存在"),
|
ADMIN_NOT_FOUND(1002002000, "管理员不存在"),
|
@ -0,0 +1,6 @@
|
|||||||
|
/**
|
||||||
|
* 该项目,主要用于暴露一些共享的枚举类等。
|
||||||
|
*
|
||||||
|
* 例如说,RPC 接口提供错误码给调用方
|
||||||
|
*/
|
||||||
|
package cn.iocoder.mall.system.biz;
|
@ -15,7 +15,7 @@
|
|||||||
<!-- Mall 相关 -->
|
<!-- Mall 相关 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cn.iocoder.mall</groupId>
|
<groupId>cn.iocoder.mall</groupId>
|
||||||
<artifactId>common-framework</artifactId>
|
<artifactId>system-biz-api</artifactId>
|
||||||
<version>1.0-SNAPSHOT</version>
|
<version>1.0-SNAPSHOT</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
package cn.iocoder.mall.system.biz.bo.authorization;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 授权模块 - 授权信息 BO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class AuthorizationBO {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
package cn.iocoder.mall.system.biz.dataobject.authorization;
|
||||||
|
|
||||||
|
import cn.iocoder.common.framework.dataobject.DeletableDO;
|
||||||
|
import cn.iocoder.mall.system.biz.dataobject.account.AccountDO;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link AccountDO} 和 {@link RoleDO} 的关联表
|
||||||
|
*/
|
||||||
|
@TableName("admin_role")
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class AccountRoleDO extends DeletableDO {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编号
|
||||||
|
*/
|
||||||
|
private Integer id;
|
||||||
|
/**
|
||||||
|
* 账号编号
|
||||||
|
*
|
||||||
|
* 关联 {@link AccountDO#getId()}
|
||||||
|
*/
|
||||||
|
private Integer accountId;
|
||||||
|
/**
|
||||||
|
* 角色编号
|
||||||
|
*
|
||||||
|
* 关联 {@link RoleDO#getId()}
|
||||||
|
*/
|
||||||
|
private Integer roleId;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,73 @@
|
|||||||
|
package cn.iocoder.mall.system.biz.dataobject.authorization;
|
||||||
|
|
||||||
|
import cn.iocoder.common.framework.dataobject.DeletableDO;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 资源实体
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@TableName(value = "resource")
|
||||||
|
public class ResourceDO extends DeletableDO {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 资源编号
|
||||||
|
*/
|
||||||
|
private Integer id;
|
||||||
|
/**
|
||||||
|
* 菜单名
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
/**
|
||||||
|
* 权限标识
|
||||||
|
*
|
||||||
|
* 一般格式为:${系统}:${模块}:${操作}
|
||||||
|
* 例如说:system:admin:add,即 system 服务的添加管理员。
|
||||||
|
*
|
||||||
|
* 当我们把该 ResourceDO 赋予给角色后,意味着该角色有该资源:
|
||||||
|
* - 对于后端,配合 @RequiresPermissions 注解,配置 API 接口需要该权限,从而对 API 接口进行权限控制。
|
||||||
|
* - 对于前端,配合前端标签,配置按钮是否展示,避免用户没有该权限时,结果可以看到该操作。
|
||||||
|
*/
|
||||||
|
private String permission;
|
||||||
|
/**
|
||||||
|
* 资源类型
|
||||||
|
*
|
||||||
|
* 关联 {@link Resource}
|
||||||
|
*/
|
||||||
|
private Integer type;
|
||||||
|
/**
|
||||||
|
* 排序
|
||||||
|
*/
|
||||||
|
private Integer sort;
|
||||||
|
/**
|
||||||
|
* 父级资源编号
|
||||||
|
*
|
||||||
|
* 关联:{@link ResourceDO#getId()}
|
||||||
|
*/
|
||||||
|
private Integer pid;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* 目前当且仅当资源类型为【菜单】时,才会生效,即 handler 配置为界面 URL ,或者前端组件名,或者前端的路由。
|
||||||
|
*/
|
||||||
|
private String handler;
|
||||||
|
/**
|
||||||
|
* 图标
|
||||||
|
*
|
||||||
|
* 目前当且仅当资源类型为【菜单】时,才会生效
|
||||||
|
*/
|
||||||
|
private String icon;
|
||||||
|
/**
|
||||||
|
* 权限标识数组,使用逗号分隔。
|
||||||
|
*
|
||||||
|
* 例如:system:admin:add
|
||||||
|
* 推荐格式为 ${系统}:${模块}:${操作}
|
||||||
|
*/
|
||||||
|
private String permissions;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package cn.iocoder.mall.system.biz.dataobject.authorization;
|
||||||
|
|
||||||
|
import cn.iocoder.common.framework.dataobject.DeletableDO;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色实体
|
||||||
|
*/
|
||||||
|
@TableName("role")
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class RoleDO extends DeletableDO {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色编号
|
||||||
|
*/
|
||||||
|
private Integer id;
|
||||||
|
/**
|
||||||
|
* 角色名
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package cn.iocoder.mall.system.biz.dataobject.authorization;
|
||||||
|
|
||||||
|
import cn.iocoder.common.framework.dataobject.DeletableDO;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link RoleDO} 和 {@link ResourceDO} 的关联表
|
||||||
|
*/
|
||||||
|
@TableName("role_resource")
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class RoleResourceDO extends DeletableDO {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编号
|
||||||
|
*/
|
||||||
|
private Integer id;
|
||||||
|
/**
|
||||||
|
* 角色编号(外键:{@link RoleDO}
|
||||||
|
*/
|
||||||
|
private Integer roleId;
|
||||||
|
/**
|
||||||
|
* 资源编号(外键:{@link ResourceDO}
|
||||||
|
*/
|
||||||
|
private Integer resourceId;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package cn.iocoder.mall.system.biz.dto.authorization;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OAuth2 模块 - 访问令牌认证 Request
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class AuthorizationCheckPermissionsDTO {
|
||||||
|
|
||||||
|
@NotNull(message = "访问令牌不能为空")
|
||||||
|
private String accessToken;
|
||||||
|
@NotNull(message = "IP 不能为空")
|
||||||
|
private String ip;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
package cn.iocoder.mall.system.biz.enums.authorization;
|
||||||
|
|
||||||
|
import cn.iocoder.common.framework.core.IntArrayValuable;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 资源类型枚举
|
||||||
|
*/
|
||||||
|
public enum ResourceTypeEnum implements IntArrayValuable {
|
||||||
|
|
||||||
|
MENU(1, "菜单"),
|
||||||
|
BUTTON(2, "按钮");
|
||||||
|
|
||||||
|
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ResourceTypeEnum::getValue).toArray();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 资源类型
|
||||||
|
*/
|
||||||
|
private final Integer value;
|
||||||
|
/**
|
||||||
|
* 资源类型名
|
||||||
|
*/
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
ResourceTypeEnum(Integer value, String name) {
|
||||||
|
this.value = value;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[] array() {
|
||||||
|
return ARRAYS;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package cn.iocoder.mall.system.biz.service.admin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 授权 Service 接口
|
||||||
|
*/
|
||||||
|
public class AuthorizationService {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package cn.iocoder.mall.system.biz.service.authorization;
|
||||||
|
|
||||||
|
import cn.iocoder.mall.system.biz.dto.authorization.AuthorizationCheckPermissionsDTO;
|
||||||
|
|
||||||
|
public interface AuthorizationService {
|
||||||
|
|
||||||
|
void checkPermissions(AuthorizationCheckPermissionsDTO checkPermissionsDTO);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package cn.iocoder.mall.system.biz.service.authorization;
|
||||||
|
|
||||||
|
import cn.iocoder.mall.system.biz.dto.authorization.AuthorizationCheckPermissionsDTO;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class AuthorizationServiceImpl implements AuthorizationService {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkPermissions(AuthorizationCheckPermissionsDTO checkPermissionsDTO) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
package cn.iocoder.mall.system.biz.service.authorization;
|
||||||
|
|
||||||
|
public interface ResourceService {
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
package cn.iocoder.mall.system.biz.service.authorization;
|
||||||
|
|
||||||
|
public interface RoleService {
|
||||||
|
}
|
@ -101,13 +101,13 @@ public class OAuth2ServiceImpl implements OAuth2Service {
|
|||||||
public OAuth2AccessTokenBO authenticate(OAuth2AccessTokenAuthenticateDTO authenticateDTO) {
|
public OAuth2AccessTokenBO authenticate(OAuth2AccessTokenAuthenticateDTO authenticateDTO) {
|
||||||
OAuth2AccessTokenDO oauth2AccessTokenDO = oauth2AccessTokenMapper.selectById(authenticateDTO.getAccessToken());
|
OAuth2AccessTokenDO oauth2AccessTokenDO = oauth2AccessTokenMapper.selectById(authenticateDTO.getAccessToken());
|
||||||
if (oauth2AccessTokenDO == null) { // 不存在
|
if (oauth2AccessTokenDO == null) { // 不存在
|
||||||
throw ServiceExceptionUtil.exception(SystemErrorCodeEnum.OAUTH2_INVALID_TOKEN_NOT_FOUND.getCode());
|
throw ServiceExceptionUtil.exception(SystemErrorCodeEnum.OAUTH2_ACCESS_TOKEN_NOT_FOUND.getCode());
|
||||||
}
|
}
|
||||||
if (oauth2AccessTokenDO.getExpiresTime().getTime() < System.currentTimeMillis()) { // 已过期
|
if (oauth2AccessTokenDO.getExpiresTime().getTime() < System.currentTimeMillis()) { // 已过期
|
||||||
throw ServiceExceptionUtil.exception(SystemErrorCodeEnum.OAUTH2_INVALID_TOKEN_EXPIRED.getCode());
|
throw ServiceExceptionUtil.exception(SystemErrorCodeEnum.OAUTH2_ACCESS_TOKEN_TOKEN_EXPIRED.getCode());
|
||||||
}
|
}
|
||||||
if (!oauth2AccessTokenDO.getValid()) { // 无效
|
if (!oauth2AccessTokenDO.getValid()) { // 无效
|
||||||
throw ServiceExceptionUtil.exception(SystemErrorCodeEnum.OAUTH2_INVALID_TOKEN_INVALID.getCode());
|
throw ServiceExceptionUtil.exception(SystemErrorCodeEnum.OAUTH2_ACCESS_TOKEN_INVALID.getCode());
|
||||||
}
|
}
|
||||||
// 转换返回
|
// 转换返回
|
||||||
return OAuth2Convert.INSTANCE.convert(oauth2AccessTokenDO);
|
return OAuth2Convert.INSTANCE.convert(oauth2AccessTokenDO);
|
||||||
|
@ -3,6 +3,7 @@ package cn.iocoder.mall.system.rest.controller.oauth2;
|
|||||||
import cn.iocoder.common.framework.constant.MallConstants;
|
import cn.iocoder.common.framework.constant.MallConstants;
|
||||||
import cn.iocoder.common.framework.util.ServiceExceptionUtil;
|
import cn.iocoder.common.framework.util.ServiceExceptionUtil;
|
||||||
import cn.iocoder.common.framework.vo.CommonResult;
|
import cn.iocoder.common.framework.vo.CommonResult;
|
||||||
|
import cn.iocoder.mall.security.core.annotation.RequiresNone;
|
||||||
import cn.iocoder.mall.system.biz.bo.admin.AdminBO;
|
import cn.iocoder.mall.system.biz.bo.admin.AdminBO;
|
||||||
import cn.iocoder.mall.system.biz.bo.ouath2.OAuth2AccessTokenBO;
|
import cn.iocoder.mall.system.biz.bo.ouath2.OAuth2AccessTokenBO;
|
||||||
import cn.iocoder.mall.system.biz.dto.oatuh2.OAuth2UsernameAuthenticateDTO;
|
import cn.iocoder.mall.system.biz.dto.oatuh2.OAuth2UsernameAuthenticateDTO;
|
||||||
@ -31,7 +32,8 @@ public class AdminsOAuth2Controller {
|
|||||||
private AdminService adminService;
|
private AdminService adminService;
|
||||||
|
|
||||||
@PostMapping("/username-authenticate")
|
@PostMapping("/username-authenticate")
|
||||||
@ApiOperation("用户名认证")
|
@ApiOperation("用户名认证(登陆)")
|
||||||
|
@RequiresNone
|
||||||
public CommonResult<AdminsOAuth2AuthenticateResponse> usernameAuthenticate(AdminsOAuth2UsernameAuthenticateRequest request) {
|
public CommonResult<AdminsOAuth2AuthenticateResponse> usernameAuthenticate(AdminsOAuth2UsernameAuthenticateRequest request) {
|
||||||
// 执行认证
|
// 执行认证
|
||||||
OAuth2UsernameAuthenticateDTO authenticateDTO = AdminsOAuth2Convert.INSTANCE.convert(request);
|
OAuth2UsernameAuthenticateDTO authenticateDTO = AdminsOAuth2Convert.INSTANCE.convert(request);
|
||||||
|
@ -3,6 +3,7 @@ package cn.iocoder.mall.system.rest.controller.oauth2;
|
|||||||
import cn.iocoder.common.framework.constant.MallConstants;
|
import cn.iocoder.common.framework.constant.MallConstants;
|
||||||
import cn.iocoder.common.framework.util.HttpUtil;
|
import cn.iocoder.common.framework.util.HttpUtil;
|
||||||
import cn.iocoder.common.framework.vo.CommonResult;
|
import cn.iocoder.common.framework.vo.CommonResult;
|
||||||
|
import cn.iocoder.mall.security.core.annotation.RequiresNone;
|
||||||
import cn.iocoder.mall.system.biz.bo.user.UserAuthenticateBO;
|
import cn.iocoder.mall.system.biz.bo.user.UserAuthenticateBO;
|
||||||
import cn.iocoder.mall.system.biz.dto.oatuh2.OAuth2MobileCodeAuthenticateDTO;
|
import cn.iocoder.mall.system.biz.dto.oatuh2.OAuth2MobileCodeAuthenticateDTO;
|
||||||
import cn.iocoder.mall.system.biz.dto.oatuh2.OAuth2MobileCodeSendDTO;
|
import cn.iocoder.mall.system.biz.dto.oatuh2.OAuth2MobileCodeSendDTO;
|
||||||
@ -37,6 +38,7 @@ public class UsersOAuth2Controller {
|
|||||||
|
|
||||||
@PostMapping("/mobile-code-authenticate")
|
@PostMapping("/mobile-code-authenticate")
|
||||||
@ApiOperation("手机验证码认证")
|
@ApiOperation("手机验证码认证")
|
||||||
|
@RequiresNone
|
||||||
public CommonResult<UsersOAuth2AuthenticateResponse> mobileCodeAuthenticate(UsersOAuth2MobileCodeAuthenticateRequest request,
|
public CommonResult<UsersOAuth2AuthenticateResponse> mobileCodeAuthenticate(UsersOAuth2MobileCodeAuthenticateRequest request,
|
||||||
HttpServletRequest httpRequest) {
|
HttpServletRequest httpRequest) {
|
||||||
// 执行认证
|
// 执行认证
|
||||||
@ -52,6 +54,7 @@ public class UsersOAuth2Controller {
|
|||||||
@PostMapping("/send-mobile-code")
|
@PostMapping("/send-mobile-code")
|
||||||
@ApiOperation("发送手机验证码")
|
@ApiOperation("发送手机验证码")
|
||||||
@ApiImplicitParam(name = "mobile", value = "手机号", required = true, example = "15601691234")
|
@ApiImplicitParam(name = "mobile", value = "手机号", required = true, example = "15601691234")
|
||||||
|
@RequiresNone
|
||||||
public CommonResult<Boolean> sendMobileCode(@RequestParam("mobile") String mobile,
|
public CommonResult<Boolean> sendMobileCode(@RequestParam("mobile") String mobile,
|
||||||
HttpServletRequest request) {
|
HttpServletRequest request) {
|
||||||
// 执行发送验证码
|
// 执行发送验证码
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
<!-- Mall 相关 -->
|
<!-- Mall 相关 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cn.iocoder.mall</groupId>
|
<groupId>cn.iocoder.mall</groupId>
|
||||||
<artifactId>common-framework</artifactId>
|
<artifactId>system-biz-api</artifactId>
|
||||||
<version>1.0-SNAPSHOT</version>
|
<version>1.0-SNAPSHOT</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
package cn.iocoder.mall.system.rpc.api.admin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Admin RPC 接口
|
||||||
|
*/
|
||||||
|
public interface AdminRPC {
|
||||||
|
}
|
@ -4,6 +4,9 @@ import cn.iocoder.common.framework.vo.CommonResult;
|
|||||||
import cn.iocoder.mall.system.rpc.request.oauth2.OAuth2AccessTokenAuthenticateRequest;
|
import cn.iocoder.mall.system.rpc.request.oauth2.OAuth2AccessTokenAuthenticateRequest;
|
||||||
import cn.iocoder.mall.system.rpc.response.oauth2.OAuth2AccessTokenResponse;
|
import cn.iocoder.mall.system.rpc.response.oauth2.OAuth2AccessTokenResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OAuth2 RPC 接口
|
||||||
|
*/
|
||||||
public interface OAuth2RPC {
|
public interface OAuth2RPC {
|
||||||
|
|
||||||
CommonResult<OAuth2AccessTokenResponse> authenticate(OAuth2AccessTokenAuthenticateRequest request);
|
CommonResult<OAuth2AccessTokenResponse> authenticate(OAuth2AccessTokenAuthenticateRequest request);
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
package cn.iocoder.mall.system.rpc.request.authorization;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 鉴权模块 - 校验账号是否有权限 Request
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class AuthorizationCheckPermissionsRequest {
|
||||||
|
|
||||||
|
@NotNull(message = "账号不能为空")
|
||||||
|
private Integer accountId;
|
||||||
|
@NotNull(message = "校验的权限不能为空")
|
||||||
|
private List<String> permissions;
|
||||||
|
|
||||||
|
}
|
@ -6,7 +6,7 @@ import lombok.experimental.Accessors;
|
|||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OAuth2 访问令牌认证 Request
|
* OAuth2 模块 - 访问令牌认证 Request
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@Accessors(chain = true)
|
@Accessors(chain = true)
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
package cn.iocoder.mall.system.rpc.response.admin;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Admin 信息 Response
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class AdminResponse {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 管理员编号
|
||||||
|
*/
|
||||||
|
private Integer id;
|
||||||
|
// private String
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user