parent
a545d673ab
commit
eb86ae7cbc
|
@ -28,12 +28,17 @@ public class CollectionUtil {
|
|||
return from.stream().map(func).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public static <T, K> Map<K, T> convertMap(List<T> from, Function<T, K> keyFunc) {
|
||||
return from.stream().collect(Collectors.toMap(keyFunc, item -> item));
|
||||
}
|
||||
|
||||
public static <T, K, V> Map<K, V> convertMap(List<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) {
|
||||
return from.stream().collect(Collectors.toMap(keyFunc, valueFunc));
|
||||
}
|
||||
|
||||
public static <T, K> Map<K, T> convertMap(List<T> from, Function<T, K> keyFunc) {
|
||||
return from.stream().collect(Collectors.toMap(keyFunc, item -> item));
|
||||
public static <T, K, V> Map<K, List<V>> convertMultiMap(List<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) {
|
||||
return from.stream().collect(Collectors.groupingBy(keyFunc,
|
||||
Collectors.mapping(valueFunc, Collectors.toList())));
|
||||
}
|
||||
|
||||
public static boolean containsAny(Collection<?> source, Collection<?> candidates) {
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package cn.iocoder.mall.security.config;
|
||||
|
||||
import cn.iocoder.mall.security.core.interceptor.AccountAuthInterceptor;
|
||||
import cn.iocoder.mall.security.core.interceptor.AdminDemoInterceptor;
|
||||
import cn.iocoder.mall.security.core.interceptor.AdminSecurityInterceptor;
|
||||
import cn.iocoder.mall.security.core.interceptor.UserSecurityInterceptor;
|
||||
import cn.iocoder.mall.web.config.CommonWebAutoConfiguration;
|
||||
import cn.iocoder.mall.web.core.constant.CommonMallConstants;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -30,6 +33,21 @@ public class CommonSecurityAutoConfiguration implements WebMvcConfigurer {
|
|||
return new AccountAuthInterceptor(false);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AdminSecurityInterceptor adminSecurityInterceptor() {
|
||||
return new AdminSecurityInterceptor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public UserSecurityInterceptor userSecurityInterceptor() {
|
||||
return new UserSecurityInterceptor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AdminDemoInterceptor adminDemoInterceptor() {
|
||||
return new AdminDemoInterceptor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
// AccountAuthInterceptor 拦截器
|
||||
|
@ -38,6 +56,18 @@ public class CommonSecurityAutoConfiguration implements WebMvcConfigurer {
|
|||
registry.addInterceptor(this.adminAccountAuthInterceptor())
|
||||
.addPathPatterns(CommonMallConstants.ROOT_PATH_ADMIN + "/**");
|
||||
logger.info("[addInterceptors][加载 AccountAuthInterceptor 拦截器完成]");
|
||||
// AdminSecurityInterceptor 拦截器
|
||||
registry.addInterceptor(this.adminSecurityInterceptor())
|
||||
.addPathPatterns(CommonMallConstants.ROOT_PATH_ADMIN + "/**");
|
||||
logger.info("[addInterceptors][加载 AdminSecurityInterceptor 拦截器完成]");
|
||||
// UserSecurityInterceptor 拦截器
|
||||
registry.addInterceptor(this.userAccountAuthInterceptor())
|
||||
.addPathPatterns(CommonMallConstants.ROOT_PATH_USER + "/**");
|
||||
logger.info("[addInterceptors][加载 UserSecurityInterceptor 拦截器完成]");
|
||||
// AdminDemoInterceptor 拦截器
|
||||
registry.addInterceptor(this.adminDemoInterceptor())
|
||||
.addPathPatterns(CommonMallConstants.ROOT_PATH_ADMIN + "/**");
|
||||
logger.info("[addInterceptors][加载 AdminDemoInterceptor 拦截器完成]");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,6 +19,6 @@ public @interface RequiresPermissions {
|
|||
*
|
||||
* @return 权限标识数组
|
||||
*/
|
||||
String[] value();
|
||||
String[] value() default {};
|
||||
|
||||
}
|
||||
|
|
|
@ -14,9 +14,5 @@ public class AdminSecurityContext {
|
|||
* 管理员编号
|
||||
*/
|
||||
private Integer adminId;
|
||||
/**
|
||||
* 管理员账号
|
||||
*/
|
||||
private String username;
|
||||
|
||||
}
|
||||
|
|
|
@ -8,7 +8,9 @@ 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.authorization.AuthorizationRPC;
|
||||
import cn.iocoder.mall.system.rpc.api.oauth2.OAuth2RPC;
|
||||
import cn.iocoder.mall.system.rpc.request.authorization.AuthorizationCheckPermissionsRequest;
|
||||
import cn.iocoder.mall.system.rpc.request.oauth2.OAuth2AccessTokenAuthenticateRequest;
|
||||
import cn.iocoder.mall.system.rpc.response.oauth2.OAuth2AccessTokenResponse;
|
||||
import cn.iocoder.mall.web.core.util.CommonWebUtil;
|
||||
|
@ -21,6 +23,7 @@ import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
|
|||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class AccountAuthInterceptor extends HandlerInterceptorAdapter {
|
||||
|
||||
|
@ -28,7 +31,8 @@ public class AccountAuthInterceptor extends HandlerInterceptorAdapter {
|
|||
|
||||
@Reference(validation = "true", version = "${dubbo.consumer.OAuth2RPC.version}")
|
||||
private OAuth2RPC oauth2RPC;
|
||||
|
||||
@Reference(validation = "true", version = "${dubbo.consumer.AuthorizationRPC.version}")
|
||||
private AuthorizationRPC authorizationRPC;
|
||||
|
||||
/**
|
||||
* 是否默认要求认证
|
||||
|
@ -51,7 +55,7 @@ public class AccountAuthInterceptor extends HandlerInterceptorAdapter {
|
|||
// 判断是否需要认证
|
||||
this.checkAuthenticate(handlerMethod, accountId);
|
||||
// 判断是否需要权限
|
||||
|
||||
this.checkPermission(handlerMethod, accountId);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -63,12 +67,12 @@ public class AccountAuthInterceptor extends HandlerInterceptorAdapter {
|
|||
// 执行认证
|
||||
OAuth2AccessTokenAuthenticateRequest oauth2AccessTokenAuthenticateRequest = new OAuth2AccessTokenAuthenticateRequest()
|
||||
.setAccessToken(accessToken).setIp(HttpUtil.getIp(request));
|
||||
CommonResult<OAuth2AccessTokenResponse> oauth2AccessTokenResponseResult = oauth2RPC.authenticate(oauth2AccessTokenAuthenticateRequest);
|
||||
if (oauth2AccessTokenResponseResult.isError()) { // TODO 有一个问题点,假设 token 认证失败,但是该 url 是无需认证的,是不是一样能够执行过去?
|
||||
throw ServiceExceptionUtil.exception(oauth2AccessTokenResponseResult);
|
||||
CommonResult<OAuth2AccessTokenResponse> oauth2AccessTokenResult = oauth2RPC.authenticate(oauth2AccessTokenAuthenticateRequest);
|
||||
if (oauth2AccessTokenResult.isError()) { // TODO 有一个问题点,假设 token 认证失败,但是该 url 是无需认证的,是不是一样能够执行过去?
|
||||
throw ServiceExceptionUtil.exception(oauth2AccessTokenResult);
|
||||
}
|
||||
// 设置账号编号
|
||||
Integer accountId = oauth2AccessTokenResponseResult.getData().getAccountId();
|
||||
Integer accountId = oauth2AccessTokenResult.getData().getAccountId();
|
||||
CommonWebUtil.setAccountId(request, accountId);
|
||||
return accountId;
|
||||
}
|
||||
|
@ -96,7 +100,12 @@ public class AccountAuthInterceptor extends HandlerInterceptorAdapter {
|
|||
return;
|
||||
}
|
||||
// 权限验证
|
||||
|
||||
AuthorizationCheckPermissionsRequest authorizationCheckPermissionsRequest = new AuthorizationCheckPermissionsRequest()
|
||||
.setAccountId(accountId).setPermissions(Arrays.asList(permissions));
|
||||
CommonResult<Boolean> authorizationCheckPermissionsResult = authorizationRPC.checkPermissions(authorizationCheckPermissionsRequest);
|
||||
if (authorizationCheckPermissionsResult.isError()) { // TODO 有一个问题点,假设 token 认证失败,但是该 url 是无需认证的,是不是一样能够执行过去?
|
||||
throw ServiceExceptionUtil.exception(authorizationCheckPermissionsResult);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,29 +1,28 @@
|
|||
package cn.iocoder.mall.system.sdk.interceptor;
|
||||
package cn.iocoder.mall.security.core.interceptor;
|
||||
|
||||
import cn.iocoder.common.framework.util.ServiceExceptionUtil;
|
||||
import cn.iocoder.mall.system.api.constant.AdminConstants;
|
||||
import cn.iocoder.mall.system.api.constant.AdminErrorCodeEnum;
|
||||
import cn.iocoder.mall.system.sdk.context.AdminSecurityContextHolder;
|
||||
import cn.iocoder.mall.security.core.context.AdminSecurityContextHolder;
|
||||
import cn.iocoder.mall.system.biz.enums.SystemErrorCodeEnum;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Admin 演示拦截器
|
||||
*
|
||||
* 这是个比较“奇怪”的拦截器,用于演示的管理员账号,禁止使用 POST 请求,从而实现即达到阉割版的演示的效果,又避免影响了数据
|
||||
*/
|
||||
@Component
|
||||
public class AdminDemoInterceptor extends HandlerInterceptorAdapter {
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
||||
if (AdminConstants.USERNAME_DEMO.equals(AdminSecurityContextHolder.getContext().getUsername())
|
||||
// 当 Admin 编号等于 0 时,约定为演示账号
|
||||
if (Objects.equals(AdminSecurityContextHolder.getContext().getAdminId(), 0)
|
||||
&& request.getMethod().equalsIgnoreCase(HttpMethod.POST.toString())) {
|
||||
throw ServiceExceptionUtil.exception(AdminErrorCodeEnum.ADMIN_DEMO_CAN_NOT_WRITE.getCode());
|
||||
throw ServiceExceptionUtil.exception(SystemErrorCodeEnum.AUTHORIZATION_DEMO_PERMISSION_DENY.getCode());
|
||||
}
|
||||
return true;
|
||||
}
|
|
@ -1,5 +1,12 @@
|
|||
package cn.iocoder.mall.security.core.interceptor;
|
||||
|
||||
import cn.iocoder.common.framework.util.ServiceExceptionUtil;
|
||||
import cn.iocoder.common.framework.vo.CommonResult;
|
||||
import cn.iocoder.mall.security.core.context.AdminSecurityContext;
|
||||
import cn.iocoder.mall.security.core.context.AdminSecurityContextHolder;
|
||||
import cn.iocoder.mall.system.rpc.api.admin.AdminRPC;
|
||||
import cn.iocoder.mall.system.rpc.response.admin.AdminResponse;
|
||||
import org.apache.dubbo.config.annotation.Reference;
|
||||
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
@ -7,16 +14,30 @@ import javax.servlet.http.HttpServletResponse;
|
|||
|
||||
public class AdminSecurityInterceptor extends HandlerInterceptorAdapter {
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||
// 获得 Admin 信息
|
||||
@Reference(validation = "true", version = "${dubbo.consumer.AdminRPC.version}")
|
||||
private AdminRPC adminRPC;
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
||||
Integer accountId = AdminSecurityContextHolder.getContext().getAdminId();
|
||||
if (accountId != null) {
|
||||
// 获得 Admin 信息
|
||||
CommonResult<AdminResponse> adminResult = adminRPC.getAdminByAccountId(accountId);
|
||||
if (adminResult.isError()) {
|
||||
throw ServiceExceptionUtil.exception(adminResult);
|
||||
}
|
||||
// 设置到 SecurityContext 中
|
||||
AdminResponse adminResponse = adminResult.getData();
|
||||
AdminSecurityContext context = new AdminSecurityContext().setAdminId(adminResponse.getId());
|
||||
AdminSecurityContextHolder.setContext(context);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
|
||||
|
||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
|
||||
// 清空 SecurityContext
|
||||
AdminSecurityContextHolder.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
package cn.iocoder.mall.security.core.interceptor;
|
||||
|
||||
import cn.iocoder.common.framework.util.ServiceExceptionUtil;
|
||||
import cn.iocoder.common.framework.vo.CommonResult;
|
||||
import cn.iocoder.mall.security.core.context.AdminSecurityContextHolder;
|
||||
import cn.iocoder.mall.security.core.context.UserSecurityContext;
|
||||
import cn.iocoder.mall.security.core.context.UserSecurityContextHolder;
|
||||
import cn.iocoder.mall.system.rpc.api.user.UserRPC;
|
||||
import cn.iocoder.mall.system.rpc.response.user.UserResponse;
|
||||
import org.apache.dubbo.config.annotation.Reference;
|
||||
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
@ -7,15 +15,30 @@ import javax.servlet.http.HttpServletResponse;
|
|||
|
||||
public class UserSecurityInterceptor extends HandlerInterceptorAdapter {
|
||||
|
||||
@Reference(validation = "true", version = "${dubbo.consumer.UserRPC.version}")
|
||||
private UserRPC userRPC;
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||
// 获得用户信息
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
||||
Integer accountId = AdminSecurityContextHolder.getContext().getAdminId();
|
||||
if (accountId != null) {
|
||||
// 获得 Admin 信息
|
||||
CommonResult<UserResponse> userResult = userRPC.getUserByAccountId(accountId);
|
||||
if (userResult.isError()) {
|
||||
throw ServiceExceptionUtil.exception(userResult);
|
||||
}
|
||||
// 设置到 SecurityContext 中
|
||||
UserResponse userResponse = userResult.getData();
|
||||
UserSecurityContext context = new UserSecurityContext().setUserId(userResponse.getId());
|
||||
UserSecurityContextHolder.setContext(context);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
|
||||
super.afterCompletion(request, response, handler, ex);
|
||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
|
||||
// 清空 SecurityContext
|
||||
UserSecurityContextHolder.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
package cn.iocoder.mall.security.core;
|
|
@ -18,18 +18,6 @@
|
|||
<artifactId>common-framework</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.mall</groupId>
|
||||
<artifactId>system-sdk</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.mall</groupId>
|
||||
<artifactId>user-sdk</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring 核心 -->
|
||||
<dependency>
|
||||
|
@ -49,17 +37,6 @@
|
|||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-swagger2</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>swagger-bootstrap-ui</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- 监控相关 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
package cn.iocoder.mall.spring.boot.web;
|
||||
|
||||
import cn.iocoder.common.framework.constant.MallConstants;
|
||||
import cn.iocoder.common.framework.servlet.CorsFilter;
|
||||
import cn.iocoder.mall.system.sdk.interceptor.AdminDemoInterceptor;
|
||||
import cn.iocoder.mall.spring.boot.web.interceptor.AccessLogInterceptor;
|
||||
import cn.iocoder.mall.system.sdk.interceptor.AdminSecurityInterceptor;
|
||||
import cn.iocoder.mall.spring.boot.web.handler.GlobalExceptionHandler;
|
||||
import cn.iocoder.mall.spring.boot.web.handler.GlobalResponseBodyHandler;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.DispatcherServlet;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) // TODO 芋艿,未来可能考虑 REACTIVE
|
||||
@ConditionalOnClass({
|
||||
DispatcherServlet.class,
|
||||
WebMvcConfigurer.class, // 有 Spring MVC 容器
|
||||
AdminSecurityInterceptor.class,
|
||||
AccessLogInterceptor.class
|
||||
}) // 有引入 system-sdk
|
||||
public class AdminMVCAutoConfiguration implements WebMvcConfigurer {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(AdminSecurityInterceptor.class)
|
||||
public AdminSecurityInterceptor adminSecurityInterceptor() {
|
||||
return new AdminSecurityInterceptor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(AdminDemoInterceptor.class)
|
||||
public AdminDemoInterceptor adminDemoInterceptor() {
|
||||
return new AdminDemoInterceptor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(adminAccessLogInterceptor()).addPathPatterns(MallConstants.ROOT_PATH_ADMIN + "/**");
|
||||
registry.addInterceptor(adminSecurityInterceptor()).addPathPatterns(MallConstants.ROOT_PATH_ADMIN + "/**");
|
||||
registry.addInterceptor(adminDemoInterceptor()).addPathPatterns(MallConstants.ROOT_PATH_ADMIN + "/**");
|
||||
}
|
||||
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
package cn.iocoder.mall.spring.boot.web;
|
||||
|
||||
import cn.iocoder.common.framework.constant.MallConstants;
|
||||
import cn.iocoder.common.framework.servlet.CorsFilter;
|
||||
import cn.iocoder.mall.spring.boot.web.interceptor.AccessLogInterceptor;
|
||||
import cn.iocoder.mall.spring.boot.web.handler.GlobalExceptionHandler;
|
||||
import cn.iocoder.mall.spring.boot.web.handler.GlobalResponseBodyHandler;
|
||||
import cn.iocoder.mall.user.sdk.interceptor.UserSecurityInterceptor;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.DispatcherServlet;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) // TODO 芋艿,未来可能考虑 REACTIVE
|
||||
@ConditionalOnClass({DispatcherServlet.class, WebMvcConfigurer.class, // 有 Spring MVC 容器
|
||||
UserSecurityInterceptor.class, // 有引入 user-sdk
|
||||
AccessLogInterceptor.class}) // 有引入 system-sdk
|
||||
public class UserMVCAutoConfiguration implements WebMvcConfigurer {
|
||||
|
||||
@Bean
|
||||
// @ConditionalOnMissingBean(AccessLogInterceptor.class)
|
||||
public AccessLogInterceptor userAccessLogInterceptor() {
|
||||
return new AccessLogInterceptor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(UserSecurityInterceptor.class)
|
||||
public UserSecurityInterceptor userSecurityInterceptor() {
|
||||
return new UserSecurityInterceptor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(GlobalResponseBodyHandler.class)
|
||||
public GlobalResponseBodyHandler globalReturnValueHandler() {
|
||||
return new GlobalResponseBodyHandler();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(GlobalExceptionHandler.class)
|
||||
public GlobalExceptionHandler globalExceptionHandler() {
|
||||
return new GlobalExceptionHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(userAccessLogInterceptor()).addPathPatterns(MallConstants.ROOT_PATH_USER + "/**");
|
||||
registry.addInterceptor(userSecurityInterceptor()).addPathPatterns(MallConstants.ROOT_PATH_USER + "/**");
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -1,4 +1,2 @@
|
|||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
cn.iocoder.mall.spring.boot.web.AdminMVCAutoConfiguration, \
|
||||
cn.iocoder.mall.spring.boot.web.UserMVCAutoConfiguration, \
|
||||
cn.iocoder.mall.spring.boot.metrics.MetricsAutoConfiguration
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
<modules>
|
||||
<module>system-application</module>
|
||||
<module>system-sdk</module>
|
||||
<!-- <module>system-sdk</module>-->
|
||||
<!-- <module>system-service-api</module>-->
|
||||
<!-- <module>system-service-impl</module>-->
|
||||
<module>system-rpc-api</module>
|
||||
|
|
|
@ -38,9 +38,7 @@ public enum SystemErrorCodeEnum implements ServiceExceptionUtil.Enumerable {
|
|||
// ADMIN_DELETE_ONLY_DISABLE(1002002004, "只有关闭的账号才可以删除"),
|
||||
// ADMIN_ADMIN_STATUS_CAN_NOT_UPDATE(1002002005, "管理员的账号状态不允许变更"),
|
||||
// ADMIN_ASSIGN_ROLE_NOT_EXISTS(1002002006, "分配员工角色时,有角色不存在"),
|
||||
// ADMIN_INVALID_PERMISSION(1002002007, "没有该操作权限"),
|
||||
// ADMIN_ADMIN_CAN_NOT_UPDATE(1002002008, "管理员的账号不允许变更"),
|
||||
// ADMIN_DEMO_CAN_NOT_WRITE(1002002009, "演示账号,暂不允许写操作。欢迎加入我们的交流群:http://t.cn/EKEr5WE"),
|
||||
|
||||
// ========== 资源模块 1002003000 ==========
|
||||
// RESOURCE_NAME_DUPLICATE(1002003000, "已经存在该名字的资源"),
|
||||
|
@ -72,8 +70,15 @@ public enum SystemErrorCodeEnum implements ServiceExceptionUtil.Enumerable {
|
|||
// DEPT_NOT_EXITS(1002007003, "当前部门不存在"),
|
||||
// DEPT_EXITS_CHILDREN(1002007004, "当前部门存在子部门"),
|
||||
// DEPT_PARENT_NOT_LEGAL(1002007005, "父级部门不合法"),
|
||||
|
||||
|
||||
// ========== 授权模块 1002008000 ==========
|
||||
AUTHORIZATION_PERMISSION_DENY(1002008001, "没有该操作权限"),
|
||||
AUTHORIZATION_DEMO_PERMISSION_DENY(1002008002, "演示账号,暂不允许写操作。欢迎加入我们的交流群:http://t.cn/EKEr5WE"),
|
||||
|
||||
;
|
||||
|
||||
|
||||
private final int code;
|
||||
private final String message;
|
||||
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
package cn.iocoder.mall.system.biz.bo.authorization;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 授权模块 - 资源信息 BO
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class ResourceBO {
|
||||
|
||||
/**
|
||||
* 资源编号
|
||||
*/
|
||||
private Integer id;
|
||||
/**
|
||||
* 菜单名
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 权限标识
|
||||
*/
|
||||
private String permission;
|
||||
/**
|
||||
* 资源类型
|
||||
*/
|
||||
private Integer type;
|
||||
/**
|
||||
* 排序
|
||||
*/
|
||||
private Integer sort;
|
||||
/**
|
||||
* 父级资源编号
|
||||
*/
|
||||
private Integer pid;
|
||||
/**
|
||||
* 前端路由
|
||||
*/
|
||||
private String route;
|
||||
/**
|
||||
* 菜单图标
|
||||
*/
|
||||
private String icon;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date createTime;
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package cn.iocoder.mall.system.biz.bo.authorization;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 授权模块 - 角色信息 BO
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class RoleBO {
|
||||
|
||||
/**
|
||||
* 角色编号
|
||||
*/
|
||||
private Integer id;
|
||||
/**
|
||||
* 角色名字
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 角色编码
|
||||
*/
|
||||
private String code;
|
||||
/**
|
||||
* 添加时间
|
||||
*/
|
||||
private Date createTime;
|
||||
|
||||
}
|
|
@ -4,7 +4,7 @@ import lombok.Data;
|
|||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* TODO 注释
|
||||
* User 模块 - User 信息 BO
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
|
|
|
@ -21,9 +21,9 @@ public interface SmsSignConvert {
|
|||
SmsSignConvert INSTANCE = Mappers.getMapper(SmsSignConvert.class);
|
||||
|
||||
@Mappings({})
|
||||
SmsSignBO convert(SmsSignDO smsSignDO);
|
||||
SmsSignBO convert(SmsSignDO bean);
|
||||
|
||||
@Mappings({})
|
||||
List<ListSmsSignBO> convert(List<SmsSignDO> smsSignDOList);
|
||||
List<ListSmsSignBO> convert(List<SmsSignDO> beans);
|
||||
|
||||
}
|
||||
|
|
|
@ -11,8 +11,8 @@ public interface AccountConvert {
|
|||
|
||||
AccountConvert INSTANCE = Mappers.getMapper(AccountConvert.class);
|
||||
|
||||
AccountBO convert(AccountDO accountDO);
|
||||
AccountBO convert(AccountDO bean);
|
||||
|
||||
AccountDO convert(AccountCreateDTO accountCreateDTO);
|
||||
AccountDO convert(AccountCreateDTO bean);
|
||||
|
||||
}
|
||||
|
|
|
@ -10,6 +10,6 @@ public interface AdminConvert {
|
|||
|
||||
AdminConvert INSTANCE = Mappers.getMapper(AdminConvert.class);
|
||||
|
||||
AdminBO convert(AdminDO adminDO);
|
||||
AdminBO convert(AdminDO bean);
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
package cn.iocoder.mall.system.biz.convert.authorization;
|
||||
|
||||
import cn.iocoder.mall.system.biz.bo.authorization.ResourceBO;
|
||||
import cn.iocoder.mall.system.biz.dataobject.authorization.ResourceDO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface ResourceConvert {
|
||||
|
||||
ResourceConvert INSTANCE = Mappers.getMapper(ResourceConvert.class);
|
||||
|
||||
ResourceBO convert(ResourceDO bean);
|
||||
|
||||
List<ResourceBO> convertList(List<ResourceDO> beans);
|
||||
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package cn.iocoder.mall.system.biz.convert.authorization;
|
||||
|
||||
import cn.iocoder.mall.system.biz.bo.authorization.RoleBO;
|
||||
import cn.iocoder.mall.system.biz.dataobject.authorization.RoleDO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface RoleConvert {
|
||||
|
||||
RoleConvert INSTANCE = Mappers.getMapper(RoleConvert.class);
|
||||
|
||||
RoleBO convert(RoleDO bean);
|
||||
|
||||
List<RoleBO> convertList(List<RoleDO> beans);
|
||||
|
||||
}
|
|
@ -10,6 +10,6 @@ public interface OAuth2Convert {
|
|||
|
||||
OAuth2Convert INSTANCE = Mappers.getMapper(OAuth2Convert.class);
|
||||
|
||||
OAuth2AccessTokenBO convert(OAuth2AccessTokenDO accessTokenDO);
|
||||
OAuth2AccessTokenBO convert(OAuth2AccessTokenDO bean);
|
||||
|
||||
}
|
||||
|
|
|
@ -12,8 +12,8 @@ public interface SystemLogConvert {
|
|||
|
||||
SystemLogConvert INSTANCE = Mappers.getMapper(SystemLogConvert.class);
|
||||
|
||||
AccessLogDO convert(AccessLogAddDTO accessLogAddDTO);
|
||||
AccessLogDO convert(AccessLogAddDTO bean);
|
||||
|
||||
ExceptionLogDO convert(ExceptionLogAddDTO exceptionLogAddDTO);
|
||||
ExceptionLogDO convert(ExceptionLogAddDTO bean);
|
||||
|
||||
}
|
||||
|
|
|
@ -17,6 +17,6 @@ public interface UserConvert {
|
|||
@Mapping(source = "accessTokenBO", target = "token")
|
||||
UserAuthenticateBO convert(UserBO userBO, OAuth2AccessTokenBO accessTokenBO);
|
||||
|
||||
UserBO convert(UserDO userDO);
|
||||
UserBO convert(UserDO bean);
|
||||
|
||||
}
|
||||
|
|
|
@ -1,10 +1,17 @@
|
|||
package cn.iocoder.mall.system.biz.dao.admin;
|
||||
|
||||
import cn.iocoder.mall.system.biz.dataobject.admin.AdminDO;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface AdminMapper extends BaseMapper<AdminDO> {
|
||||
|
||||
default AdminDO selectByAccountId(Integer accountId) {
|
||||
return selectOne(new QueryWrapper<AdminDO>()
|
||||
.eq("account_id", accountId)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
package cn.iocoder.mall.system.biz.dao.authorization;
|
||||
|
||||
import cn.iocoder.mall.system.biz.dataobject.authorization.AccountRoleDO;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public interface AccountRoleMapper extends BaseMapper<AccountRoleDO> {
|
||||
|
||||
default List<AccountRoleDO> selectByAccountId( Integer accountId) {
|
||||
return selectList(new QueryWrapper<AccountRoleDO>().eq("account_id", accountId));
|
||||
}
|
||||
|
||||
default List<AccountRoleDO> selectListByAccountIds(Collection<Integer> accountIds) {
|
||||
return selectList(new QueryWrapper<AccountRoleDO>().in("account_id", accountIds));
|
||||
}
|
||||
|
||||
default int deleteByAccountId(Integer accountId) {
|
||||
return delete(new QueryWrapper<AccountRoleDO>().eq("account_id", accountId));
|
||||
}
|
||||
|
||||
default int deleteByRoleId(Integer roleId) {
|
||||
return delete(new QueryWrapper<AccountRoleDO>().eq("role_id", roleId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量插入。因为 MyBaits Plus 的批量插入是基于 Service 实现,所以只好写 XML
|
||||
*
|
||||
* @param accountRoleDOs 数组
|
||||
*/
|
||||
int insertList(@Param("accountRoleDOs") List<AccountRoleDO> accountRoleDOs);
|
||||
|
||||
}
|
|
@ -1,12 +1,13 @@
|
|||
package cn.iocoder.mall.admin.dao;
|
||||
package cn.iocoder.mall.system.biz.dao.authorization;
|
||||
|
||||
import cn.iocoder.common.framework.mybatis.QueryWrapperX;
|
||||
import cn.iocoder.mall.admin.dataobject.ResourceDO;
|
||||
import cn.iocoder.mall.system.biz.dataobject.authorization.ResourceDO;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -17,18 +18,18 @@ public interface ResourceMapper extends BaseMapper<ResourceDO> {
|
|||
List<ResourceDO> selectListByTypeAndRoleIds(@Param("type") Integer type,
|
||||
@Param("roleIds") Set<Integer> roleIds);
|
||||
|
||||
default List<ResourceDO> selectListByPermission(String permission) {
|
||||
return selectList(new QueryWrapperX<ResourceDO>().like("permissions", permission));
|
||||
default ResourceDO selectByPermission(String permission) {
|
||||
return selectOne(new QueryWrapper<ResourceDO>().eq("permission", permission));
|
||||
}
|
||||
|
||||
default List<ResourceDO> selectListByPermissions(Collection<String> permissions) {
|
||||
return selectList(new QueryWrapper<ResourceDO>().in("permission", permissions));
|
||||
}
|
||||
|
||||
default List<ResourceDO> selectListByType(Integer type) {
|
||||
return selectList(new QueryWrapperX<ResourceDO>().eqIfPresent("type", type));
|
||||
}
|
||||
|
||||
default List<ResourceDO> selectListByIds(Set<Integer> ids) {
|
||||
return selectList(new QueryWrapper<ResourceDO>().in("id", ids));
|
||||
}
|
||||
|
||||
default int selectCountByPid(Integer pid) {
|
||||
return selectCount(new QueryWrapper<ResourceDO>().eq("pid", pid));
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
package cn.iocoder.mall.admin.dao;
|
||||
package cn.iocoder.mall.system.biz.dao.authorization;
|
||||
|
||||
import cn.iocoder.common.framework.mybatis.QueryWrapperX;
|
||||
import cn.iocoder.mall.system.api.dto.role.RolePageDTO;
|
||||
import cn.iocoder.mall.admin.dataobject.RoleDO;
|
||||
import cn.iocoder.mall.system.biz.dataobject.authorization.RoleDO;
|
||||
import cn.iocoder.mall.system.biz.dto.authorization.RolePageDTO;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
|
@ -1,6 +1,6 @@
|
|||
package cn.iocoder.mall.admin.dao;
|
||||
package cn.iocoder.mall.system.biz.dao.authorization;
|
||||
|
||||
import cn.iocoder.mall.admin.dataobject.RoleResourceDO;
|
||||
import cn.iocoder.mall.system.biz.dataobject.authorization.RoleResourceDO;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
@ -23,7 +23,7 @@ public interface RoleResourceMapper extends BaseMapper<RoleResourceDO> {
|
|||
return selectList(new QueryWrapper<RoleResourceDO>().eq("resource_id", resourceId));
|
||||
}
|
||||
|
||||
default List<RoleResourceDO> selectListByResourceId(Collection<Integer> resourceIds) {
|
||||
default List<RoleResourceDO> selectListByResourceIds(Collection<Integer> resourceIds) {
|
||||
return selectList(new QueryWrapper<RoleResourceDO>().in("resource_id", resourceIds));
|
||||
}
|
||||
|
|
@ -4,14 +4,16 @@ 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.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* {@link AccountDO} 和 {@link RoleDO} 的关联表
|
||||
*/
|
||||
@TableName("admin_role")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Accessors(chain = true)
|
||||
@TableName("account_role")
|
||||
public class AccountRoleDO extends DeletableDO {
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package cn.iocoder.mall.system.biz.dataobject.authorization;
|
||||
|
||||
import cn.iocoder.common.framework.dataobject.DeletableDO;
|
||||
import cn.iocoder.mall.system.biz.enums.authorization.ResourceTypeEnum;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
@ -37,7 +38,7 @@ public class ResourceDO extends DeletableDO {
|
|||
/**
|
||||
* 资源类型
|
||||
*
|
||||
* 关联 {@link Resource}
|
||||
* 关联 {@link ResourceTypeEnum}
|
||||
*/
|
||||
private Integer type;
|
||||
/**
|
||||
|
@ -51,23 +52,16 @@ public class ResourceDO extends DeletableDO {
|
|||
*/
|
||||
private Integer pid;
|
||||
/**
|
||||
* 前端路由
|
||||
*
|
||||
*
|
||||
* 目前当且仅当资源类型为【菜单】时,才会生效,即 handler 配置为界面 URL ,或者前端组件名,或者前端的路由。
|
||||
* 目前当且仅当资源类型为 {@link ResourceTypeEnum#MENU} 时,才会生效
|
||||
*/
|
||||
private String handler;
|
||||
private String route;
|
||||
/**
|
||||
* 图标
|
||||
* 菜单图标
|
||||
*
|
||||
* 目前当且仅当资源类型为【菜单】时,才会生效
|
||||
* 目前当且仅当资源类型为 {@link ResourceTypeEnum#MENU} 时,才会生效
|
||||
*/
|
||||
private String icon;
|
||||
/**
|
||||
* 权限标识数组,使用逗号分隔。
|
||||
*
|
||||
* 例如:system:admin:add
|
||||
* 推荐格式为 ${系统}:${模块}:${操作}
|
||||
*/
|
||||
private String permissions;
|
||||
|
||||
}
|
||||
|
|
|
@ -3,14 +3,16 @@ 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;
|
||||
|
||||
/**
|
||||
* 角色实体
|
||||
*/
|
||||
@TableName("role")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Accessors(chain = true)
|
||||
@TableName("role")
|
||||
public class RoleDO extends DeletableDO {
|
||||
|
||||
/**
|
||||
|
@ -21,5 +23,9 @@ public class RoleDO extends DeletableDO {
|
|||
* 角色名
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 角色编码
|
||||
*/
|
||||
private String code;
|
||||
|
||||
}
|
||||
|
|
|
@ -3,14 +3,16 @@ 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;
|
||||
|
||||
/**
|
||||
* {@link RoleDO} 和 {@link ResourceDO} 的关联表
|
||||
*/
|
||||
@TableName("role_resource")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Accessors(chain = true)
|
||||
@TableName("role_resource")
|
||||
public class RoleResourceDO extends DeletableDO {
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package cn.iocoder.mall.system.biz.dataobject.user;
|
||||
|
||||
import cn.iocoder.common.framework.dataobject.BaseDO;
|
||||
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;
|
||||
|
@ -14,7 +14,7 @@ import lombok.experimental.Accessors;
|
|||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Accessors(chain = true)
|
||||
public class UserDO extends BaseDO {
|
||||
public class UserDO extends DeletableDO {
|
||||
|
||||
/**
|
||||
* 用户编号
|
||||
|
|
|
@ -4,17 +4,18 @@ import lombok.Data;
|
|||
import lombok.experimental.Accessors;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* OAuth2 模块 - 访问令牌认证 Request
|
||||
* 授权模块 - 校验账号是否有权限 DTO
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class AuthorizationCheckPermissionsDTO {
|
||||
|
||||
@NotNull(message = "访问令牌不能为空")
|
||||
private String accessToken;
|
||||
@NotNull(message = "IP 不能为空")
|
||||
private String ip;
|
||||
@NotNull(message = "账号编号不能为空")
|
||||
private Integer accountId;
|
||||
@NotNull(message = "权限不能为空")
|
||||
private Collection<String> permissions;
|
||||
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package cn.iocoder.mall.system.api.dto.role;
|
||||
package cn.iocoder.mall.system.biz.dto.authorization;
|
||||
|
||||
import cn.iocoder.common.framework.vo.PageParam;
|
||||
import io.swagger.annotations.ApiModel;
|
|
@ -0,0 +1,21 @@
|
|||
package cn.iocoder.mall.system.biz.enums.authorization;
|
||||
|
||||
public enum RoleCodeEnum {
|
||||
|
||||
SUPER_ADMIN("SUPER_ADMIN"), // 超级管理员
|
||||
;
|
||||
|
||||
/**
|
||||
* 角色编码
|
||||
*/
|
||||
private final String code;
|
||||
|
||||
RoleCodeEnum(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
}
|
|
@ -7,6 +7,8 @@ import cn.iocoder.mall.system.biz.bo.admin.AdminBO;
|
|||
*/
|
||||
public interface AdminService {
|
||||
|
||||
AdminBO get(Integer id);
|
||||
AdminBO getAdmin(Integer id);
|
||||
|
||||
AdminBO getAdminByAccountId(Integer accountId);
|
||||
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import cn.iocoder.mall.system.biz.bo.admin.AdminBO;
|
|||
import cn.iocoder.mall.system.biz.convert.admin.AdminConvert;
|
||||
import cn.iocoder.mall.system.biz.dao.admin.AdminMapper;
|
||||
import cn.iocoder.mall.system.biz.dataobject.admin.AdminDO;
|
||||
import cn.iocoder.mall.system.biz.service.admin.AdminService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
|
@ -15,9 +14,15 @@ public class AdminServiceImpl implements AdminService {
|
|||
private AdminMapper adminMapper;
|
||||
|
||||
@Override
|
||||
public AdminBO get(Integer id) {
|
||||
public AdminBO getAdmin(Integer id) {
|
||||
AdminDO adminDO = adminMapper.selectById(id);
|
||||
return AdminConvert.INSTANCE.convert(adminDO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminBO getAdminByAccountId(Integer accountId) {
|
||||
AdminDO adminDO = adminMapper.selectByAccountId(accountId);
|
||||
return AdminConvert.INSTANCE.convert(adminDO);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
package cn.iocoder.mall.system.biz.service.admin;
|
||||
|
||||
/**
|
||||
* 授权 Service 接口
|
||||
*/
|
||||
public class AuthorizationService {
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -1,14 +1,68 @@
|
|||
package cn.iocoder.mall.system.biz.service.authorization;
|
||||
|
||||
import cn.iocoder.common.framework.util.CollectionUtil;
|
||||
import cn.iocoder.common.framework.util.ServiceExceptionUtil;
|
||||
import cn.iocoder.mall.system.biz.bo.authorization.ResourceBO;
|
||||
import cn.iocoder.mall.system.biz.dao.authorization.AccountRoleMapper;
|
||||
import cn.iocoder.mall.system.biz.dao.authorization.RoleResourceMapper;
|
||||
import cn.iocoder.mall.system.biz.dataobject.authorization.AccountRoleDO;
|
||||
import cn.iocoder.mall.system.biz.dataobject.authorization.RoleResourceDO;
|
||||
import cn.iocoder.mall.system.biz.dto.authorization.AuthorizationCheckPermissionsDTO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.mall.system.biz.enums.SystemErrorCodeEnum.AUTHORIZATION_PERMISSION_DENY;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class AuthorizationServiceImpl implements AuthorizationService {
|
||||
|
||||
@Autowired
|
||||
private AccountRoleMapper accountRoleMapper;
|
||||
@Autowired
|
||||
private RoleResourceMapper roleResourceMapper;
|
||||
|
||||
@Autowired
|
||||
private RoleService roleService;
|
||||
@Autowired
|
||||
private ResourceService resourceService;
|
||||
|
||||
@Override
|
||||
public void checkPermissions(AuthorizationCheckPermissionsDTO checkPermissionsDTO) {
|
||||
|
||||
// 查询管理员拥有的角色关联数据
|
||||
List<AccountRoleDO> accountRoleDOs = accountRoleMapper.selectByAccountId(checkPermissionsDTO.getAccountId());
|
||||
if (CollectionUtil.isEmpty(accountRoleDOs)) { // 如果没有角色,默认无法访问
|
||||
throw ServiceExceptionUtil.exception(AUTHORIZATION_PERMISSION_DENY);
|
||||
}
|
||||
Set<Integer> roleIds = CollectionUtil.convertSet(accountRoleDOs, AccountRoleDO::getRoleId);
|
||||
// 判断是否为超管。若是超管,默认有所有权限
|
||||
if (roleService.hasSuperAdmin(roleIds)) {
|
||||
return;
|
||||
}
|
||||
// 查询权限对应资源
|
||||
List<ResourceBO> resourceBOs = resourceService.getListByPermissions(checkPermissionsDTO.getPermissions());
|
||||
if (CollectionUtil.isEmpty(resourceBOs)) { // 无对应资源,则认为无需权限验证
|
||||
log.warn("[checkPermissions][permission({}) 未配置对应资源]", checkPermissionsDTO.getPermissions());
|
||||
return;
|
||||
}
|
||||
Set<Integer> permissionIds = CollectionUtil.convertSet(resourceBOs, ResourceBO::getId);
|
||||
// 权限验证
|
||||
List<RoleResourceDO> roleResourceDOs = roleResourceMapper.selectListByResourceIds(permissionIds);
|
||||
if (CollectionUtil.isEmpty(roleResourceDOs)) { // 资源未授予任何角色,必然权限验证不通过
|
||||
throw ServiceExceptionUtil.exception(AUTHORIZATION_PERMISSION_DENY);
|
||||
}
|
||||
Map<Integer, List<Integer>> resourceRoleMap = CollectionUtil.convertMultiMap(roleResourceDOs,
|
||||
RoleResourceDO::getResourceId, RoleResourceDO::getRoleId);
|
||||
for (Map.Entry<Integer, List<Integer>> entry : resourceRoleMap.entrySet()) {
|
||||
if (!CollectionUtil.containsAny(roleIds, entry.getValue())) { // 所以有任一不满足,就验证失败,抛出异常
|
||||
throw ServiceExceptionUtil.exception(AUTHORIZATION_PERMISSION_DENY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,4 +1,12 @@
|
|||
package cn.iocoder.mall.system.biz.service.authorization;
|
||||
|
||||
import cn.iocoder.mall.system.biz.bo.authorization.ResourceBO;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public interface ResourceService {
|
||||
|
||||
List<ResourceBO> getListByPermissions(Collection<String> permissions);
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package cn.iocoder.mall.system.biz.service.authorization;
|
||||
|
||||
import cn.iocoder.mall.system.biz.bo.authorization.ResourceBO;
|
||||
import cn.iocoder.mall.system.biz.convert.authorization.ResourceConvert;
|
||||
import cn.iocoder.mall.system.biz.dao.authorization.ResourceMapper;
|
||||
import cn.iocoder.mall.system.biz.dataobject.authorization.ResourceDO;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class ResourceServiceImpl implements ResourceService {
|
||||
|
||||
@Autowired
|
||||
private ResourceMapper resourceMapper;
|
||||
|
||||
@Override
|
||||
public List<ResourceBO> getListByPermissions(Collection<String> permissions) {
|
||||
List<ResourceDO> resourceDOs = resourceMapper.selectListByPermissions(permissions);
|
||||
return ResourceConvert.INSTANCE.convertList(resourceDOs);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,4 +1,20 @@
|
|||
package cn.iocoder.mall.system.biz.service.authorization;
|
||||
|
||||
import cn.iocoder.mall.system.biz.bo.authorization.RoleBO;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public interface RoleService {
|
||||
|
||||
List<RoleBO> getRoleList(Collection<Integer> ids);
|
||||
|
||||
/**
|
||||
* 判断指定角色是否包含超级管理员角色
|
||||
*
|
||||
* @param ids 角色编号数组
|
||||
* @return 是否有超级管理员角色
|
||||
*/
|
||||
boolean hasSuperAdmin(Collection<Integer> ids);
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
package cn.iocoder.mall.system.biz.service.authorization;
|
||||
|
||||
import cn.iocoder.mall.system.biz.bo.authorization.RoleBO;
|
||||
import cn.iocoder.mall.system.biz.convert.authorization.RoleConvert;
|
||||
import cn.iocoder.mall.system.biz.dao.authorization.RoleMapper;
|
||||
import cn.iocoder.mall.system.biz.dataobject.authorization.RoleDO;
|
||||
import cn.iocoder.mall.system.biz.enums.authorization.RoleCodeEnum;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class RoleServiceImpl implements RoleService {
|
||||
|
||||
@Autowired
|
||||
private RoleMapper roleMapper;
|
||||
|
||||
@Override
|
||||
public List<RoleBO> getRoleList(Collection<Integer> ids) {
|
||||
List<RoleDO> roleDOs = roleMapper.selectBatchIds(ids);
|
||||
return RoleConvert.INSTANCE.convertList(roleDOs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSuperAdmin(Collection<Integer> ids) {
|
||||
List<RoleDO> roleDOs = roleMapper.selectBatchIds(ids);
|
||||
for (RoleDO roleDO : roleDOs) {
|
||||
if (RoleCodeEnum.SUPER_ADMIN.getCode().equals(roleDO.getCode())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package cn.iocoder.mall.system.biz.service.user;
|
||||
|
||||
import cn.iocoder.mall.system.biz.bo.user.UserAuthenticateBO;
|
||||
import cn.iocoder.mall.system.biz.bo.user.UserBO;
|
||||
import cn.iocoder.mall.system.biz.dto.oatuh2.OAuth2MobileCodeAuthenticateDTO;
|
||||
|
||||
/**
|
||||
|
@ -10,4 +11,6 @@ public interface UserService {
|
|||
|
||||
UserAuthenticateBO authenticate(OAuth2MobileCodeAuthenticateDTO authenticateDTO);
|
||||
|
||||
UserBO getUserByAccountId(Integer accountId);
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package cn.iocoder.mall.system.biz.service.user;
|
||||
|
||||
import cn.iocoder.common.framework.constant.DeletedStatusEnum;
|
||||
import cn.iocoder.mall.system.biz.bo.ouath2.OAuth2AccessTokenBO;
|
||||
import cn.iocoder.mall.system.biz.bo.user.UserAuthenticateBO;
|
||||
import cn.iocoder.mall.system.biz.bo.user.UserBO;
|
||||
|
@ -36,9 +37,16 @@ public class UserServiceImpl implements UserService {
|
|||
return UserConvert.INSTANCE.convert(userBO, accessTokenBO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserBO getUserByAccountId(Integer accountId) {
|
||||
UserDO userDO = userMapper.selectById(accountId);
|
||||
return UserConvert.INSTANCE.convert(userDO);
|
||||
}
|
||||
|
||||
private UserDO creatUser(Integer accountId) {
|
||||
UserDO user = new UserDO();
|
||||
user.setAccountId(accountId);
|
||||
user.setDeleted(DeletedStatusEnum.DELETED_NO.getValue());
|
||||
userMapper.insert(user);
|
||||
return user;
|
||||
}
|
||||
|
|
|
@ -17,4 +17,3 @@ mybatis-plus:
|
|||
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
|
||||
mapper-locations: classpath*:mapper/*.xml
|
||||
type-aliases-package: cn.iocoder.mall.system.biz.dataobject
|
||||
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="cn.iocoder.mall.system.biz.dao.authorization.AccountRoleMapper">
|
||||
|
||||
<insert id="insertList">
|
||||
INSERT INTO account_role (
|
||||
account_id, role_id, create_time, deleted
|
||||
) VALUES
|
||||
<foreach collection="accountRoleDOs" item="accountRole" separator=",">
|
||||
(#{accountRole.accountId}, #{accountRole.roleId}, #{accountRole.createTime}, #{accountRole.deleted})
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
</mapper>
|
|
@ -0,0 +1,74 @@
|
|||
package cn.iocoder.mall.system.rest.controller.datadict;
|
||||
|
||||
import cn.iocoder.common.framework.constant.MallConstants;
|
||||
import cn.iocoder.common.framework.vo.CommonResult;
|
||||
import cn.iocoder.mall.security.core.annotation.RequiresPermissions;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping(MallConstants.ROOT_PATH_ADMIN + "/data-dict")
|
||||
@Api(tags = "管理员 - 数据字典 API")
|
||||
public class AdminsDataDictController {
|
||||
|
||||
// @Reference(validation = "true", version = "${dubbo.provider.DataDictService.version}")
|
||||
// private DataDictService dataDictService;
|
||||
|
||||
@GetMapping("/demo")
|
||||
@ApiOperation(value = "数据字典全列表")
|
||||
@RequiresPermissions("system.dataDict.list")
|
||||
public CommonResult<Boolean> list() {
|
||||
return CommonResult.success(true);
|
||||
}
|
||||
|
||||
// @GetMapping("/list")
|
||||
// @ApiOperation(value = "数据字典全列表")
|
||||
// @RequiresPermissions("system.dataDict.list")
|
||||
// public CommonResult<List<DataDictBO>> list() {
|
||||
// return success( dataDictService.selectDataDictList());
|
||||
// }
|
||||
//
|
||||
// @GetMapping("/tree")
|
||||
// @RequiresPermissions // 因为是通用的接口,所以无需权限标识
|
||||
// @ApiOperation(value = "数据字典树结构", notes = "该接口返回的信息更为精简。一般用于前端缓存数据字典到本地。")
|
||||
// public CommonResult<List<DataDictEnumVO>> tree() {
|
||||
// // 查询数据字典全列表
|
||||
// List<DataDictBO> dataDicts = dataDictService.selectDataDictList();
|
||||
// // 构建基于 enumValue 聚合的 Multimap
|
||||
// ImmutableListMultimap<String, DataDictBO> dataDictMap = Multimaps.index(dataDicts, DataDictBO::getEnumValue); // KEY 是 enumValue ,VALUE 是 DataDictBO 数组
|
||||
// // 构建返回结果
|
||||
// List<DataDictEnumVO> dataDictEnumVOs = new ArrayList<>(dataDictMap.size());
|
||||
// dataDictMap.keys().forEach(enumValue -> {
|
||||
// DataDictEnumVO dataDictEnumVO = new DataDictEnumVO().setEnumValue(enumValue)
|
||||
// .setValues(DataDictConvert.INSTANCE.convert2(dataDictMap.get(enumValue)));
|
||||
// dataDictEnumVOs.add(dataDictEnumVO);
|
||||
// });
|
||||
// return success(dataDictEnumVOs);
|
||||
// }
|
||||
//
|
||||
// @PostMapping("/add")
|
||||
// @RequiresPermissions("system.dataDict.add")
|
||||
// @ApiOperation(value = "创建数据字典")
|
||||
// public CommonResult<DataDictBO> add(DataDictAddDTO dataDictAddDTO) {
|
||||
// return success(dataDictService.addDataDict(AdminSecurityContextHolder.getContext().getAdminId(), dataDictAddDTO));
|
||||
// }
|
||||
//
|
||||
// @PostMapping("/update")
|
||||
// @RequiresPermissions("system.dataDict.update")
|
||||
// @ApiOperation(value = "更新数据字典")
|
||||
// public CommonResult<Boolean> update(DataDictUpdateDTO dataDictUpdateDTO) {
|
||||
// return success(dataDictService.updateDataDict(AdminSecurityContextHolder.getContext().getAdminId(), dataDictUpdateDTO));
|
||||
// }
|
||||
//
|
||||
// @PostMapping("/delete")
|
||||
// @RequiresPermissions("system.dataDict.delete")
|
||||
// @ApiOperation(value = "删除数据字典")
|
||||
// @ApiImplicitParam(name = "id", value = "编号", required = true, example = "100")
|
||||
// public CommonResult<Boolean> delete(@RequestParam("id") Integer id) {
|
||||
// return success(dataDictService.deleteDataDict(AdminSecurityContextHolder.getContext().getAdminId(), id));
|
||||
// }
|
||||
|
||||
}
|
|
@ -39,7 +39,7 @@ public class AdminsOAuth2Controller {
|
|||
OAuth2UsernameAuthenticateDTO authenticateDTO = AdminsOAuth2Convert.INSTANCE.convert(request);
|
||||
OAuth2AccessTokenBO accessTokenBO = oauth2Service.authenticate(authenticateDTO);
|
||||
// 获得 Admin 信息
|
||||
AdminBO adminBO = adminService.get(accessTokenBO.getAccountId());
|
||||
AdminBO adminBO = adminService.getAdmin(accessTokenBO.getAccountId());
|
||||
if (adminBO == null) {
|
||||
throw ServiceExceptionUtil.exception(ADMIN_NOT_FOUND);
|
||||
}
|
||||
|
|
|
@ -10,6 +10,6 @@ public interface AdminsAdminConvert {
|
|||
|
||||
AdminsAdminConvert INSTANCE = Mappers.getMapper(AdminsAdminConvert.class);
|
||||
|
||||
AccountUsernameAuthorizeBO convert(AdminsOAuth2UsernameAuthenticateRequest request);
|
||||
AccountUsernameAuthorizeBO convert(AdminsOAuth2UsernameAuthenticateRequest bean);
|
||||
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ public interface AdminsOAuth2Convert {
|
|||
|
||||
AdminsOAuth2Convert INSTANCE = Mappers.getMapper(AdminsOAuth2Convert.class);
|
||||
|
||||
OAuth2UsernameAuthenticateDTO convert(AdminsOAuth2UsernameAuthenticateRequest request);
|
||||
OAuth2UsernameAuthenticateDTO convert(AdminsOAuth2UsernameAuthenticateRequest bean);
|
||||
|
||||
@Mapping(source = "adminBO", target = "admin")
|
||||
@Mapping(source = "accessTokenBO.id", target = "token.accessToken")
|
||||
|
|
|
@ -13,9 +13,9 @@ public interface UsersOAuth2Convert {
|
|||
|
||||
UsersOAuth2Convert INSTANCE = Mappers.getMapper(UsersOAuth2Convert.class);
|
||||
|
||||
OAuth2MobileCodeAuthenticateDTO convert(UsersOAuth2MobileCodeAuthenticateRequest request);
|
||||
OAuth2MobileCodeAuthenticateDTO convert(UsersOAuth2MobileCodeAuthenticateRequest bean);
|
||||
|
||||
@Mapping(source = "token.id", target = "token.accessToken")
|
||||
UsersOAuth2AuthenticateResponse convert(UserAuthenticateBO userAuthenticateBO);
|
||||
UsersOAuth2AuthenticateResponse convert(UserAuthenticateBO bean);
|
||||
|
||||
}
|
||||
|
|
|
@ -22,11 +22,11 @@ public interface AdminsSmsConvert {
|
|||
AdminsSmsConvert INSTANCE = Mappers.getMapper(AdminsSmsConvert.class);
|
||||
|
||||
@Mappings({})
|
||||
AddSignDTO convert(AddSignRequest addSignRequest);
|
||||
AddSignDTO convert(AddSignRequest bean);
|
||||
|
||||
@Mappings({})
|
||||
UpdateSignDTO convert(UpdateSignRequest updateSignRequest);
|
||||
UpdateSignDTO convert(UpdateSignRequest bean);
|
||||
|
||||
@Mappings({})
|
||||
ListSmsTemplateDTO convert(ListSmsTemplateRequest listSmsTemplateRequest);
|
||||
ListSmsTemplateDTO convert(ListSmsTemplateRequest bean);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,15 @@
|
|||
package cn.iocoder.mall.system.rpc.api.admin;
|
||||
|
||||
import cn.iocoder.common.framework.vo.CommonResult;
|
||||
import cn.iocoder.mall.system.rpc.response.admin.AdminResponse;
|
||||
|
||||
/**
|
||||
* Admin RPC 接口
|
||||
*/
|
||||
public interface AdminRPC {
|
||||
|
||||
CommonResult<AdminResponse> getAdmin(Integer id);
|
||||
|
||||
CommonResult<AdminResponse> getAdminByAccountId(Integer accountId);
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
package cn.iocoder.mall.system.rpc.api.authorization;
|
||||
|
||||
import cn.iocoder.common.framework.vo.CommonResult;
|
||||
import cn.iocoder.mall.system.rpc.request.authorization.AuthorizationCheckPermissionsRequest;
|
||||
|
||||
public interface AuthorizationRPC {
|
||||
|
||||
CommonResult<Boolean> checkPermissions(AuthorizationCheckPermissionsRequest checkPermissionsRequest);
|
||||
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package cn.iocoder.mall.system.rpc.api.user;
|
||||
|
||||
import cn.iocoder.common.framework.vo.CommonResult;
|
||||
import cn.iocoder.mall.system.rpc.response.user.UserResponse;
|
||||
|
||||
/**
|
||||
* User RPC 接口
|
||||
*/
|
||||
public interface UserRPC {
|
||||
|
||||
// CommonResult<UserResponse> getUser(Integer id);
|
||||
|
||||
CommonResult<UserResponse> getUserByAccountId(Integer accountId);
|
||||
|
||||
}
|
|
@ -7,7 +7,7 @@ import javax.validation.constraints.NotNull;
|
|||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 鉴权模块 - 校验账号是否有权限 Request
|
||||
* 授权模块 - 校验账号是否有权限 Request
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
|
|
|
@ -4,7 +4,7 @@ import lombok.Data;
|
|||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* Admin 信息 Response
|
||||
* Admin 模块 - Admin 信息 Response
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
|
@ -14,6 +14,5 @@ public class AdminResponse {
|
|||
* 管理员编号
|
||||
*/
|
||||
private Integer id;
|
||||
// private String
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package cn.iocoder.mall.system.rpc.response.user;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* User 模块 - User 信息 Response
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class UserResponse {
|
||||
|
||||
/**
|
||||
* 用户编号
|
||||
*/
|
||||
private Integer id;
|
||||
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package cn.iocoder.mall.system.rpc.convert.admn;
|
||||
|
||||
import cn.iocoder.mall.system.biz.bo.admin.AdminBO;
|
||||
import cn.iocoder.mall.system.rpc.response.admin.AdminResponse;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper
|
||||
public interface AdminCovert {
|
||||
|
||||
AdminCovert INSTANCE = Mappers.getMapper(AdminCovert.class);
|
||||
|
||||
AdminResponse convert(AdminBO bean);
|
||||
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package cn.iocoder.mall.system.rpc.convert.authorization;
|
||||
|
||||
import cn.iocoder.mall.system.biz.dto.authorization.AuthorizationCheckPermissionsDTO;
|
||||
import cn.iocoder.mall.system.rpc.request.authorization.AuthorizationCheckPermissionsRequest;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper
|
||||
public interface AuthorizationConvert {
|
||||
|
||||
AuthorizationConvert INSTANCE = Mappers.getMapper(AuthorizationConvert.class);
|
||||
|
||||
AuthorizationCheckPermissionsDTO convert(AuthorizationCheckPermissionsRequest bean);
|
||||
|
||||
}
|
|
@ -14,6 +14,6 @@ public interface OAuth2Convert {
|
|||
|
||||
OAuth2AccessTokenAuthenticateDTO convert(OAuth2AccessTokenAuthenticateRequest authenticateRequest);
|
||||
|
||||
OAuth2AccessTokenResponse convert(OAuth2AccessTokenBO accessTokenBO);
|
||||
OAuth2AccessTokenResponse convert(OAuth2AccessTokenBO bean);
|
||||
|
||||
}
|
||||
|
|
|
@ -12,8 +12,8 @@ public interface SystemLogConvert {
|
|||
|
||||
SystemLogConvert INSTANCE = Mappers.getMapper(SystemLogConvert.class);
|
||||
|
||||
AccessLogAddDTO convert(AccessLogAddRequest accessLogAddRequest);
|
||||
AccessLogAddDTO convert(AccessLogAddRequest bean);
|
||||
|
||||
ExceptionLogAddDTO convert(ExceptionLogAddRequest exceptionLogAddRequest);
|
||||
ExceptionLogAddDTO convert(ExceptionLogAddRequest bean);
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
package cn.iocoder.mall.system.rpc.convert.user;
|
||||
|
||||
import cn.iocoder.mall.system.biz.bo.user.UserBO;
|
||||
import cn.iocoder.mall.system.rpc.response.user.UserResponse;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper
|
||||
public interface UserConvert {
|
||||
|
||||
UserConvert INSTANCE = Mappers.getMapper(UserConvert.class);
|
||||
|
||||
UserResponse convert(UserBO bean);
|
||||
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package cn.iocoder.mall.system.rpc.rpc.admin;
|
||||
|
||||
import cn.iocoder.common.framework.vo.CommonResult;
|
||||
import cn.iocoder.mall.system.biz.bo.admin.AdminBO;
|
||||
import cn.iocoder.mall.system.biz.service.admin.AdminService;
|
||||
import cn.iocoder.mall.system.rpc.api.admin.AdminRPC;
|
||||
import cn.iocoder.mall.system.rpc.convert.admn.AdminCovert;
|
||||
import cn.iocoder.mall.system.rpc.response.admin.AdminResponse;
|
||||
import org.apache.dubbo.config.annotation.Service;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
@Service(version = "${dubbo.provider.AdminRPC.version}", validation = "true")
|
||||
public class AdminRPCImpl implements AdminRPC {
|
||||
|
||||
@Autowired
|
||||
private AdminService adminService;
|
||||
|
||||
@Override
|
||||
public CommonResult<AdminResponse> getAdmin(Integer id) {
|
||||
AdminBO adminBO = adminService.getAdmin(id);
|
||||
return CommonResult.success(AdminCovert.INSTANCE.convert(adminBO));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<AdminResponse> getAdminByAccountId(Integer accountId) {
|
||||
AdminBO adminBO = adminService.getAdminByAccountId(accountId);
|
||||
return CommonResult.success(AdminCovert.INSTANCE.convert(adminBO));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package cn.iocoder.mall.system.rpc.rpc.authorization;
|
||||
|
||||
import cn.iocoder.common.framework.vo.CommonResult;
|
||||
import cn.iocoder.mall.system.biz.dto.authorization.AuthorizationCheckPermissionsDTO;
|
||||
import cn.iocoder.mall.system.biz.service.authorization.AuthorizationService;
|
||||
import cn.iocoder.mall.system.rpc.api.authorization.AuthorizationRPC;
|
||||
import cn.iocoder.mall.system.rpc.convert.authorization.AuthorizationConvert;
|
||||
import cn.iocoder.mall.system.rpc.request.authorization.AuthorizationCheckPermissionsRequest;
|
||||
import org.apache.dubbo.config.annotation.Service;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
@Service(version = "${dubbo.provider.AuthorizationRPC.version}", validation = "true")
|
||||
public class AuthorizationRPCImpl implements AuthorizationRPC {
|
||||
|
||||
@Autowired
|
||||
private AuthorizationService authorizationService;
|
||||
|
||||
@Override
|
||||
public CommonResult<Boolean> checkPermissions(AuthorizationCheckPermissionsRequest checkPermissionsRequest) {
|
||||
AuthorizationCheckPermissionsDTO checkPermissionsDTO = AuthorizationConvert.INSTANCE.convert(checkPermissionsRequest);
|
||||
authorizationService.checkPermissions(checkPermissionsDTO);
|
||||
return CommonResult.success(true);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package cn.iocoder.mall.system.rpc.rpc.user;
|
||||
|
||||
import cn.iocoder.common.framework.vo.CommonResult;
|
||||
import cn.iocoder.mall.system.biz.bo.user.UserBO;
|
||||
import cn.iocoder.mall.system.biz.service.user.UserService;
|
||||
import cn.iocoder.mall.system.rpc.api.user.UserRPC;
|
||||
import cn.iocoder.mall.system.rpc.convert.user.UserConvert;
|
||||
import cn.iocoder.mall.system.rpc.response.user.UserResponse;
|
||||
import org.apache.dubbo.config.annotation.Service;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
@Service(version = "${dubbo.provider.UserRPC.version}", validation = "true")
|
||||
public class UserRPCImpl implements UserRPC {
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
@Override
|
||||
public CommonResult<UserResponse> getUserByAccountId(Integer accountId) {
|
||||
UserBO userBO = userService.getUserByAccountId(accountId);
|
||||
return CommonResult.success(UserConvert.INSTANCE.convert(userBO));
|
||||
}
|
||||
|
||||
}
|
|
@ -17,9 +17,21 @@ dubbo:
|
|||
version: 1.0.0
|
||||
OAuth2RPC:
|
||||
version: 1.0.0
|
||||
AuthorizationRPC:
|
||||
version: 1.0.0
|
||||
AdminRPC:
|
||||
version: 1.0.0
|
||||
UserRPC:
|
||||
version: 1.0.0
|
||||
# Dubbo 服务消费者的配置
|
||||
consumer:
|
||||
SystemLogRPC: # 用于 AccessLogInterceptor 等拦截器,记录 HTTP API 请求的访问日志
|
||||
version: 1.0.0
|
||||
OAuth2RPC:
|
||||
OAuth2RPC: # 用于 AccountAuthInterceptor 拦截器,执行认证
|
||||
version: 1.0.0
|
||||
AuthorizationRPC: # 用于 AccountAuthInterceptor 拦截器,执行鉴权(权限验证)
|
||||
version: 1.0.0
|
||||
AdminRPC:
|
||||
version: 1.0.0
|
||||
UserRPC:
|
||||
version: 1.0.0
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
<?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>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<artifactId>system-sdk</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<!-- Mall 相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.mall</groupId>
|
||||
<artifactId>common-framework</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.mall</groupId>
|
||||
<artifactId>system-service-api</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Web 相关 -->
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>servlet-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webmvc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- RPC 相关 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
|
@ -1,22 +0,0 @@
|
|||
package cn.iocoder.mall.system.sdk.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 参考 Shiro @RequiresPermissions 设计 http://shiro.apache.org/static/1.3.2/apidocs/org/apache/shiro/authz/annotation/RequiresPermissions.html
|
||||
*
|
||||
* 通过将该注解添加到 Controller 的方法上,进行授权鉴定
|
||||
*/
|
||||
@Documented
|
||||
@Target({ElementType.METHOD}) // 暂时不支持 ElementType.TYPE ,因为没有场景
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface RequiresPermissions {
|
||||
|
||||
/**
|
||||
* 当有多个标识时,必须全部拥有权限,才可以操作
|
||||
*
|
||||
* @return 权限标识数组
|
||||
*/
|
||||
String[] value();
|
||||
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package cn.iocoder.mall.system.sdk.constant;
|
||||
|
||||
/**
|
||||
* 逻辑类型枚举
|
||||
*/
|
||||
public enum LogicalEnum {
|
||||
|
||||
/**
|
||||
* 并且
|
||||
*/
|
||||
AND,
|
||||
/**
|
||||
* 或者
|
||||
*/
|
||||
OR,
|
||||
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
package cn.iocoder.mall.system.sdk.context;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Security 上下文
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class AdminSecurityContext {
|
||||
|
||||
/**
|
||||
* 管理员编号
|
||||
*/
|
||||
private Integer adminId;
|
||||
/**
|
||||
* 管理员账号
|
||||
*/
|
||||
private String username;
|
||||
/**
|
||||
* 拥有的角色编号
|
||||
*/
|
||||
private Set<Integer> roleIds;
|
||||
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
package cn.iocoder.mall.system.sdk.context;
|
||||
|
||||
/**
|
||||
* {@link AdminSecurityContext} Holder
|
||||
*
|
||||
* 参考 spring security 的 ThreadLocalSecurityContextHolderStrategy 类,简单实现。
|
||||
*/
|
||||
public class AdminSecurityContextHolder {
|
||||
|
||||
private static final ThreadLocal<AdminSecurityContext> SECURITY_CONTEXT = new ThreadLocal<>();
|
||||
|
||||
public static void setContext(AdminSecurityContext context) {
|
||||
SECURITY_CONTEXT.set(context);
|
||||
}
|
||||
|
||||
public static AdminSecurityContext getContext() {
|
||||
AdminSecurityContext ctx = SECURITY_CONTEXT.get();
|
||||
// 为空时,设置一个空的进去
|
||||
if (ctx == null) {
|
||||
ctx = new AdminSecurityContext();
|
||||
SECURITY_CONTEXT.set(ctx);
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
SECURITY_CONTEXT.remove();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
package cn.iocoder.mall.system.sdk.interceptor;
|
||||
|
||||
import cn.iocoder.common.framework.constant.UserTypeEnum;
|
||||
import cn.iocoder.common.framework.exception.ServiceException;
|
||||
import cn.iocoder.common.framework.util.HttpUtil;
|
||||
import cn.iocoder.common.framework.util.MallUtil;
|
||||
import cn.iocoder.common.framework.util.StringUtil;
|
||||
import cn.iocoder.mall.system.api.AdminService;
|
||||
import cn.iocoder.mall.system.api.OAuth2Service;
|
||||
import cn.iocoder.mall.system.api.bo.admin.AdminAuthorizationBO;
|
||||
import cn.iocoder.mall.system.api.bo.oauth2.OAuth2AuthenticationBO;
|
||||
import cn.iocoder.mall.system.api.constant.AdminErrorCodeEnum;
|
||||
import cn.iocoder.mall.system.api.dto.oauth2.OAuth2GetTokenDTO;
|
||||
import cn.iocoder.mall.system.sdk.annotation.RequiresPermissions;
|
||||
import cn.iocoder.mall.system.sdk.context.AdminSecurityContext;
|
||||
import cn.iocoder.mall.system.sdk.context.AdminSecurityContextHolder;
|
||||
import org.apache.dubbo.config.annotation.Reference;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Admin 安全拦截器
|
||||
*/
|
||||
@Component
|
||||
public class AdminSecurityInterceptor extends HandlerInterceptorAdapter {
|
||||
|
||||
@Reference(validation = "true", version = "${dubbo.consumer.OAuth2Service.version:1.0.0}")
|
||||
private OAuth2Service oauth2Service;
|
||||
@Reference(validation = "true", version = "${dubbo.consumer.AdminService.version:1.0.0}")
|
||||
private AdminService adminService;
|
||||
|
||||
/**
|
||||
* 忽略的 URL 集合,即无需经过认证
|
||||
*
|
||||
* 对于 Admin 的系统,默认所有接口都需要进行认证
|
||||
*/
|
||||
@Value("${admins.security.ignore_urls:#{null}}")
|
||||
private Set<String> ignoreUrls;
|
||||
|
||||
public AdminSecurityInterceptor setIgnoreUrls(Set<String> ignoreUrls) {
|
||||
this.ignoreUrls = ignoreUrls;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||
// 设置当前访问的用户类型。注意,即使未登陆,我们也认为是管理员
|
||||
MallUtil.setUserType(request, UserTypeEnum.ADMIN.getValue());
|
||||
|
||||
// 根据 accessToken 获得认证信息,判断是谁
|
||||
String accessToken = HttpUtil.obtainAuthorization(request);
|
||||
OAuth2AuthenticationBO authentication = null;
|
||||
ServiceException serviceException = null;
|
||||
if (StringUtil.hasText(accessToken)) {
|
||||
try {
|
||||
authentication = oauth2Service.getAuthentication(new OAuth2GetTokenDTO().setAccessToken(accessToken)
|
||||
.setUserType(UserTypeEnum.ADMIN.getValue()));
|
||||
} catch (ServiceException e) {
|
||||
serviceException = e;
|
||||
}
|
||||
}
|
||||
|
||||
// 进行鉴权
|
||||
String url = request.getRequestURI();
|
||||
boolean needAuthentication = ignoreUrls == null || !ignoreUrls.contains(url);
|
||||
AdminAuthorizationBO authorization = null;
|
||||
if (needAuthentication) {
|
||||
if (serviceException != null) { // 认证失败,抛出上面认证失败的 ServiceException 异常
|
||||
throw serviceException;
|
||||
}
|
||||
if (authentication == null) { // 无认证信息,抛出未登陆 ServiceException 异常
|
||||
throw new ServiceException(AdminErrorCodeEnum.OAUTH2_NOT_LOGIN.getCode(), AdminErrorCodeEnum.OAUTH2_NOT_LOGIN.getMessage());
|
||||
}
|
||||
authorization = checkPermission(handler, authentication);
|
||||
}
|
||||
|
||||
// 鉴权完成,初始化 AdminSecurityContext 上下文
|
||||
AdminSecurityContext context = new AdminSecurityContext();
|
||||
AdminSecurityContextHolder.setContext(context);
|
||||
if (authentication != null) {
|
||||
context.setAdminId(authentication.getUserId());
|
||||
MallUtil.setUserId(request, authentication.getUserId()); // 记录到 request 中,避免 AdminSecurityContext 后续清理掉后,其它地方需要用到 userId
|
||||
if (authorization != null) {
|
||||
context.setUsername(authorization.getUsername());
|
||||
context.setRoleIds(authorization.getRoleIds());
|
||||
}
|
||||
}
|
||||
|
||||
// 返回成功
|
||||
return super.preHandle(request, response, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
|
||||
// 清空 SecurityContext
|
||||
AdminSecurityContextHolder.clear();
|
||||
}
|
||||
|
||||
private AdminAuthorizationBO checkPermission(Object handler, OAuth2AuthenticationBO authentication) {
|
||||
// 获得 @RequiresPermissions 注解
|
||||
Assert.isTrue(handler instanceof HandlerMethod, "handler 必须是 HandlerMethod 类型");
|
||||
HandlerMethod handlerMethod = (HandlerMethod) handler;
|
||||
RequiresPermissions requiresPermissions = handlerMethod.getMethodAnnotation(RequiresPermissions.class);
|
||||
// 执行校验
|
||||
return adminService.checkPermissions(authentication.getUserId(),
|
||||
requiresPermissions != null ? Arrays.asList(requiresPermissions.value()) : null);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
/**
|
||||
* 提供 SDK 给其它服务,使用如下功能:
|
||||
*
|
||||
* 1. 通过 {@link cn.iocoder.mall.system.sdk.interceptor.AdminSecurityInterceptor} 拦截器,实现需要登陆 URL 的鉴权
|
||||
*/
|
||||
package cn.iocoder.mall.system.sdk;
|
|
@ -19,8 +19,6 @@ public interface RoleService {
|
|||
*/
|
||||
List<RoleBO> getRoleList();
|
||||
|
||||
List<RoleBO> getRoleList(Collection<Integer> ids);
|
||||
|
||||
RoleBO addRole(Integer adminId, RoleAddDTO roleAddDTO);
|
||||
|
||||
Boolean updateRole(Integer adminId, RoleUpdateDTO roleUpdateDTO);
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
package cn.iocoder.mall.admin.config;
|
||||
|
||||
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
|
||||
import com.baomidou.mybatisplus.core.injector.ISqlInjector;
|
||||
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
|
||||
@Configuration
|
||||
@MapperScan("cn.iocoder.mall.admin.dao") // 扫描对应的 Mapper 接口
|
||||
@EnableTransactionManagement(proxyTargetClass = true) // 启动事务管理。为什么使用 proxyTargetClass 参数,参见 https://blog.csdn.net/huang_550/article/details/76492600
|
||||
public class DatabaseConfiguration {
|
||||
|
||||
// 数据库连接池 Druid
|
||||
|
||||
@Bean
|
||||
public ISqlInjector sqlInjector() {
|
||||
return new DefaultSqlInjector(); // MyBatis Plus 逻辑删除
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PaginationInterceptor paginationInterceptor() {
|
||||
return new PaginationInterceptor(); // MyBatis Plus 分页插件
|
||||
}
|
||||
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
package cn.iocoder.mall.admin.config;
|
||||
|
||||
import cn.iocoder.common.framework.util.ServiceExceptionUtil;
|
||||
import cn.iocoder.mall.system.api.constant.AdminErrorCodeEnum;
|
||||
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.event.EventListener;
|
||||
|
||||
@Configuration
|
||||
public class ServiceExceptionConfiguration {
|
||||
|
||||
@EventListener(ApplicationReadyEvent.class) // 可参考 https://www.cnblogs.com/ssslinppp/p/7607509.html
|
||||
public void initMessages() {
|
||||
for (AdminErrorCodeEnum item : AdminErrorCodeEnum.values()) {
|
||||
ServiceExceptionUtil.put(item.getCode(), item.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
package cn.iocoder.mall.admin.dao;
|
||||
|
||||
import cn.iocoder.mall.admin.dataobject.AdminRoleDO;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public interface AdminRoleMapper extends BaseMapper<AdminRoleDO> {
|
||||
|
||||
default List<AdminRoleDO> selectByAdminId( Integer adminId) {
|
||||
return selectList(new QueryWrapper<AdminRoleDO>().eq("admin_id", adminId));
|
||||
}
|
||||
|
||||
default List<AdminRoleDO> selectListByAdminIds(Collection<Integer> adminIds) {
|
||||
return selectList(new QueryWrapper<AdminRoleDO>().in("admin_id", adminIds));
|
||||
}
|
||||
|
||||
default int deleteByAdminId(Integer adminId) {
|
||||
return delete(new QueryWrapper<AdminRoleDO>().eq("admin_id", adminId));
|
||||
}
|
||||
|
||||
default int deleteByRoleId(Integer roleId) {
|
||||
return delete(new QueryWrapper<AdminRoleDO>().eq("role_id", roleId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量插入。因为 MyBaits Plus 的批量插入是基于 Service 实现,所以只好写 XML
|
||||
*
|
||||
* @param adminRoleDOs 数组
|
||||
*/
|
||||
int insertList(@Param("adminRoleDOs") List<AdminRoleDO> adminRoleDOs);
|
||||
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
package cn.iocoder.mall.admin.dao;
|
||||
|
||||
import cn.iocoder.mall.admin.dataobject.ExceptionLogDO;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface ExceptionLogMapper extends BaseMapper<ExceptionLogDO> {
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
package cn.iocoder.mall.admin.dao;
|
||||
|
||||
import cn.iocoder.mall.admin.dataobject.OAuth2AccessTokenDO;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface OAuth2AccessTokenMapper extends BaseMapper<OAuth2AccessTokenDO> {
|
||||
|
||||
default int updateToInvalid(Integer userId, Integer userType) {
|
||||
QueryWrapper<OAuth2AccessTokenDO> query = new QueryWrapper<OAuth2AccessTokenDO>()
|
||||
.eq("user_id", userId).eq("user_type", userType)
|
||||
.eq("valid", true);
|
||||
return update(new OAuth2AccessTokenDO().setValid(false), query);
|
||||
}
|
||||
|
||||
default int updateToInvalidByRefreshToken(String refreshToken) {
|
||||
QueryWrapper<OAuth2AccessTokenDO> query = new QueryWrapper<OAuth2AccessTokenDO>()
|
||||
.eq("refresh_token", refreshToken).eq("valid", true);
|
||||
return update(new OAuth2AccessTokenDO().setValid(false), query);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
package cn.iocoder.mall.admin.dao;
|
||||
|
||||
import cn.iocoder.mall.admin.dataobject.OAuth2RefreshTokenDO;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface OAuth2RefreshTokenMapper extends BaseMapper<OAuth2RefreshTokenDO> {
|
||||
|
||||
default int updateToInvalid(Integer userId, Integer userType) {
|
||||
QueryWrapper<OAuth2RefreshTokenDO> query = new QueryWrapper<OAuth2RefreshTokenDO>()
|
||||
.eq("user_id", userId).eq("user_type", userType)
|
||||
.eq("valid", true);
|
||||
return update(new OAuth2RefreshTokenDO().setValid(false), query);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
package cn.iocoder.mall.admin.dataobject;
|
||||
|
||||
import cn.iocoder.common.framework.dataobject.DeletableDO;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* {@link AdminDO} 和 {@link RoleDO} 的关联表
|
||||
*/
|
||||
@TableName("admin_role")
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class AdminRoleDO extends DeletableDO {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
*/
|
||||
private Integer id;
|
||||
/**
|
||||
* 管理员编号(外键:{@link AdminDO}
|
||||
*/
|
||||
private Integer adminId;
|
||||
/**
|
||||
* 角色编号(外键:{@link RoleDO}
|
||||
*/
|
||||
private Integer roleId;
|
||||
|
||||
}
|
|
@ -1,118 +0,0 @@
|
|||
package cn.iocoder.mall.admin.dataobject;
|
||||
|
||||
import cn.iocoder.common.framework.dataobject.BaseDO;
|
||||
import cn.iocoder.mall.system.api.dto.systemlog.AccessLogAddDTO;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 异常日志 DO
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@TableName("exception_log")
|
||||
public class ExceptionLogDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
*/
|
||||
private Integer id;
|
||||
/**
|
||||
* 链路追踪编号
|
||||
*
|
||||
* 一般来说,通过链路追踪编号,可以将访问日志,错误日志,链路追踪日志,logger 打印日志等,结合在一起,从而进行排错。
|
||||
*/
|
||||
private String traceId;
|
||||
/**
|
||||
* 用户编号.
|
||||
*
|
||||
* 当管理员为空时,该值为 {@link AccessLogAddDTO#USER_ID_NULL}
|
||||
*/
|
||||
private Integer userId;
|
||||
/**
|
||||
* 用户类型
|
||||
*/
|
||||
private Integer userType;
|
||||
/**
|
||||
* 应用名
|
||||
*
|
||||
* 目前读取 spring.application.name
|
||||
*/
|
||||
private String applicationName;
|
||||
/**
|
||||
* 访问地址
|
||||
*/
|
||||
private String uri;
|
||||
/**
|
||||
* 参数
|
||||
*/
|
||||
private String queryString;
|
||||
/**
|
||||
* http 方法
|
||||
*/
|
||||
private String method;
|
||||
/**
|
||||
* userAgent
|
||||
*/
|
||||
private String userAgent;
|
||||
/**
|
||||
* ip
|
||||
*/
|
||||
private String ip;
|
||||
/**
|
||||
* 异常发生时间
|
||||
*/
|
||||
private Date exceptionTime;
|
||||
/**
|
||||
* 异常名
|
||||
*
|
||||
* {@link Throwable#getClass()} 的类全名
|
||||
*/
|
||||
private String exceptionName;
|
||||
/**
|
||||
* 异常导致的消息
|
||||
*
|
||||
* {@link cn.iocoder.common.framework.util.ExceptionUtil#getMessage(Throwable)}
|
||||
*/
|
||||
private String exceptionMessage;
|
||||
/**
|
||||
* 异常导致的根消息
|
||||
*
|
||||
* {@link cn.iocoder.common.framework.util.ExceptionUtil#getRootCauseMessage(Throwable)}
|
||||
*/
|
||||
private String exceptionRootCauseMessage;
|
||||
/**
|
||||
* 异常的栈轨迹
|
||||
*
|
||||
* {@link cn.iocoder.common.framework.util.ExceptionUtil#getServiceException(Exception)}
|
||||
*/
|
||||
private String exceptionStackTrace;
|
||||
/**
|
||||
* 异常发生的类全名
|
||||
*
|
||||
* {@link StackTraceElement#getClassName()}
|
||||
*/
|
||||
private String exceptionClassName;
|
||||
/**
|
||||
* 异常发生的类文件
|
||||
*
|
||||
* {@link StackTraceElement#getFileName()}
|
||||
*/
|
||||
private String exceptionFileName;
|
||||
/**
|
||||
* 异常发生的方法名
|
||||
*
|
||||
* {@link StackTraceElement#getMethodName()}
|
||||
*/
|
||||
private String exceptionMethodName;
|
||||
/**
|
||||
* 异常发生的方法所在行
|
||||
*
|
||||
* {@link StackTraceElement#getLineNumber()}
|
||||
*/
|
||||
private Integer exceptionLineNumber;
|
||||
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
package cn.iocoder.mall.admin.dataobject;
|
||||
|
||||
import cn.iocoder.common.framework.dataobject.BaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* OAUTH2 AccessToken
|
||||
*/
|
||||
@TableName("oauth2_access_token")
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class OAuth2AccessTokenDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 访问令牌
|
||||
*/
|
||||
@TableId(type = IdType.INPUT)
|
||||
private String id;
|
||||
/**
|
||||
* 刷新令牌
|
||||
*/
|
||||
private String refreshToken;
|
||||
/**
|
||||
* 用户编号
|
||||
*/
|
||||
private Integer userId;
|
||||
/**
|
||||
* 用户类型
|
||||
*/
|
||||
private Integer userType;
|
||||
/**
|
||||
* 过期时间
|
||||
*/
|
||||
private Date expiresTime;
|
||||
/**
|
||||
* 是否有效
|
||||
*/
|
||||
private Boolean valid;
|
||||
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
package cn.iocoder.mall.admin.dataobject;
|
||||
|
||||
import cn.iocoder.common.framework.dataobject.BaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 刷新令牌
|
||||
*
|
||||
* idx_uid
|
||||
*/
|
||||
@TableName("oauth2_refresh_token")
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class OAuth2RefreshTokenDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 刷新令牌
|
||||
*/
|
||||
@TableId(type = IdType.INPUT)
|
||||
private String id;
|
||||
/**
|
||||
* 用户编号
|
||||
*/
|
||||
private Integer userId;
|
||||
/**
|
||||
* 用户类型
|
||||
*/
|
||||
private Integer userType;
|
||||
/**
|
||||
* 是否有效
|
||||
*/
|
||||
private Boolean valid;
|
||||
/**
|
||||
* 过期时间
|
||||
*/
|
||||
private Date expiresTime;
|
||||
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
package cn.iocoder.mall.admin.dataobject;
|
||||
|
||||
import cn.iocoder.common.framework.dataobject.DeletableDO;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 资源实体
|
||||
*/
|
||||
@TableName(value = "resource")
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class ResourceDO extends DeletableDO {
|
||||
|
||||
/**
|
||||
* 资源编号
|
||||
*/
|
||||
private Integer id;
|
||||
/**
|
||||
* 资源类型
|
||||
*/
|
||||
private Integer type;
|
||||
/**
|
||||
* 排序
|
||||
*/
|
||||
private Integer sort;
|
||||
/**
|
||||
* 展示名
|
||||
*/
|
||||
private String displayName;
|
||||
/**
|
||||
* 父级资源编号(外键:{@link ResourceDO#id})
|
||||
*/
|
||||
private Integer pid;
|
||||
/**
|
||||
* 操作
|
||||
*
|
||||
* 目前当且仅当资源类型为【菜单】时,才会生效,即 handler 配置为界面 URL ,或者前端组件名,或者前端的路由。
|
||||
*/
|
||||
private String handler;
|
||||
/**
|
||||
* 图表
|
||||
*
|
||||
* 目前当且仅当资源类型为【菜单】时,才会生效
|
||||
*/
|
||||
private String icon;
|
||||
/**
|
||||
* 权限标识数组,使用逗号分隔。
|
||||
*
|
||||
* 例如:system.admin.add 。
|
||||
* 推荐格式为 ${系统}.${模块}.${操作} 。
|
||||
*/
|
||||
private String permissions;
|
||||
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
package cn.iocoder.mall.admin.dataobject;
|
||||
|
||||
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;
|
||||
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
package cn.iocoder.mall.admin.dataobject;
|
||||
|
||||
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;
|
||||
|
||||
}
|
|
@ -48,28 +48,6 @@ public class AdminServiceImpl implements AdminService {
|
|||
@Autowired
|
||||
private RoleServiceImpl roleService;
|
||||
|
||||
@Override
|
||||
public AdminAuthenticationBO authentication(AdminAuthenticationDTO adminAuthenticationDTO) {
|
||||
AdminDO admin = adminMapper.selectByUsername(adminAuthenticationDTO.getUsername());
|
||||
// 账号不存在
|
||||
if (admin == null) {
|
||||
throw ServiceExceptionUtil.exception(AdminErrorCodeEnum.ADMIN_USERNAME_NOT_REGISTERED.getCode());
|
||||
}
|
||||
// 密码不正确
|
||||
if (!encodePassword(adminAuthenticationDTO.getPassword()).equals(admin.getPassword())) {
|
||||
throw ServiceExceptionUtil.exception(AdminErrorCodeEnum.ADMIN_PASSWORD_ERROR.getCode());
|
||||
}
|
||||
// 账号被禁用
|
||||
if (CommonStatusEnum.DISABLE.getValue().equals(admin.getStatus())) {
|
||||
throw ServiceExceptionUtil.exception(AdminErrorCodeEnum.ADMIN_IS_DISABLE.getCode());
|
||||
}
|
||||
// 创建 accessToken
|
||||
OAuth2AccessTokenBO accessTokenBO = oauth2Service.createToken(new OAuth2CreateTokenDTO().setUserId(admin.getId())
|
||||
.setUserType(UserTypeEnum.ADMIN.getValue()));
|
||||
// 转换返回
|
||||
return AdminConvert.INSTANCE.convert2(admin).setToken(accessTokenBO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<AdminBO> getAdminPage(AdminPageDTO adminPageDTO) {
|
||||
IPage<AdminDO> page = adminMapper.selectPage(adminPageDTO);
|
||||
|
@ -227,29 +205,4 @@ public class AdminServiceImpl implements AdminService {
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminAuthorizationBO checkPermissions(Integer adminId, List<String> permissions) {
|
||||
// 查询管理员拥有的角色关联数据
|
||||
List<AdminRoleDO> adminRoleList = adminRoleMapper.selectByAdminId(adminId);
|
||||
Set<Integer> adminRoleIds = CollectionUtil.convertSet(adminRoleList, AdminRoleDO::getRoleId);
|
||||
// 授权校验
|
||||
if (!CollectionUtil.isEmpty(permissions)) {
|
||||
Map<String, List<Integer>> permissionRoleMap = roleService.getPermissionRoleMap(permissions);
|
||||
for (Map.Entry<String, List<Integer>> entry : permissionRoleMap.entrySet()) {
|
||||
if (!CollectionUtil.containsAny(entry.getValue(), adminRoleIds)) { // 所以有任一不满足,就验证失败,抛出异常
|
||||
throw ServiceExceptionUtil.exception(AdminErrorCodeEnum.ADMIN_INVALID_PERMISSION.getCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
// 获得用户
|
||||
AdminDO admin = adminMapper.selectById(adminId);
|
||||
// 返回成功
|
||||
return new AdminAuthorizationBO().setId(adminId).setUsername(admin.getUsername())
|
||||
.setRoleIds(adminRoleIds);
|
||||
}
|
||||
|
||||
private String encodePassword(String password) {
|
||||
return DigestUtils.md5DigestAsHex(password.getBytes());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
##################### 业务模块 #####################
|
||||
## OAuth2CodeService
|
||||
modules.oauth2-code-service.access-token-expire-time-millis = 2880000
|
||||
modules.oauth2-code-service.refresh-token-expire-time-millis = 43200000
|
|
@ -1,14 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="cn.iocoder.mall.admin.dao.AdminRoleMapper">
|
||||
|
||||
<insert id="insertList">
|
||||
INSERT INTO admin_role (
|
||||
admin_id, role_id, create_time, deleted
|
||||
) VALUES
|
||||
<foreach collection="adminRoleDOs" item="adminRole" separator=",">
|
||||
(#{adminRole.adminId}, #{adminRole.roleId}, #{adminRole.createTime}, #{adminRole.deleted})
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
</mapper>
|
|
@ -14,7 +14,6 @@
|
|||
<modules>
|
||||
<!-- <module>user-application</module>-->
|
||||
<!-- <module>user-service-api</module>-->
|
||||
<!-- <module>user-sdk</module>-->
|
||||
<!-- <module>user-service-impl</module>-->
|
||||
<module>user-application</module>
|
||||
<module>user-rest</module>
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
<?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>user</artifactId>
|
||||
<groupId>cn.iocoder.mall</groupId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>user-sdk</artifactId>
|
||||
<dependencies>
|
||||
<!-- Mall 相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.mall</groupId>
|
||||
<artifactId>common-framework</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.mall</groupId>
|
||||
<artifactId>user-service-api</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Web 相关 -->
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>servlet-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webmvc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- RPC 相关 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue