From 09004dc0003197368c50239e8f7b7c7b45c87677 Mon Sep 17 00:00:00 2001 From: YunaiV <> Date: Wed, 27 Feb 2019 00:00:37 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=AE=A1=E7=90=86=E5=91=98?= =?UTF-8?q?=E6=A8=A1=E5=9D=97~?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- admin/admin-application/pom.xml | 114 ++++++++++++++++ .../iocoder/mall/admin/AdminApplication.java | 13 ++ .../mall/admin/config/MVCConfiguration.java | 29 ++++ .../admin/config/SwaggerConfiguration.java | 36 +++++ .../admin/controller/AdminController.java | 14 ++ .../admin/controller/PassportController.java | 38 ++++++ .../mall/admin/convert/PassportConvert.java | 21 +++ .../mall/admin/vo/PassportLoginVO.java | 43 ++++++ .../src/main/resources/application.yaml | 7 + admin/admin-sdk/pom.xml | 53 +++++++ .../sdk/context/AdminSecurityContext.java | 26 ++++ .../context/AdminSecurityContextHolder.java | 30 ++++ .../interceptor/AdminSecurityInterceptor.java | 64 +++++++++ .../iocoder/mall/admin/sdk/package-info.java | 6 + admin/admin-service-api/pom.xml | 27 ++++ .../iocoder/mall/admin/api/AdminService.java | 16 +++ .../iocoder/mall/admin/api/OAuth2Service.java | 35 +++++ .../iocoder/mall/admin/api/RoleService.java | 4 + .../admin/api/bo/OAuth2AccessTokenBO.java | 47 +++++++ .../admin/api/bo/OAuth2AuthenticationBO.java | 35 +++++ .../api/constant/AdminErrorCodeEnum.java | 43 ++++++ admin/admin-service-impl/pom.xml | 77 +++++++++++ .../admin/config/DatabaseConfiguration.java | 14 ++ .../config/ServiceExceptionConfiguration.java | 26 ++++ .../mall/admin/convert/OAuth2Convert.java | 35 +++++ .../iocoder/mall/admin/dao/AdminMapper.java | 12 ++ .../mall/admin/dao/AdminRoleMapper.java | 14 ++ .../admin/dao/OAuth2AccessTokenMapper.java | 13 ++ .../admin/dao/OAuth2RefreshTokenMapper.java | 11 ++ .../mall/admin/dao/RoleResourceMapper.java | 14 ++ .../mall/admin/dataobject/AdminDO.java | 100 ++++++++++++++ .../mall/admin/dataobject/AdminRoleDO.java | 65 +++++++++ .../admin/dataobject/OAuth2AccessTokenDO.java | 86 ++++++++++++ .../dataobject/OAuth2RefreshTokenDO.java | 78 +++++++++++ .../mall/admin/dataobject/ResourceDO.java | 129 ++++++++++++++++++ .../iocoder/mall/admin/dataobject/RoleDO.java | 63 +++++++++ .../mall/admin/dataobject/RoleResourceDO.java | 65 +++++++++ .../cn/iocoder/mall/admin/package-info.java | 1 + .../mall/admin/service/AdminServiceImpl.java | 50 +++++++ .../mall/admin/service/OAuth2ServiceImpl.java | 123 +++++++++++++++++ .../mall/admin/service/RoleServiceImpl.java | 22 +++ .../resources/config/application.properties | 4 + .../main/resources/config/application.yaml | 32 +++++ .../src/main/resources/mapper/AdminMapper.xml | 20 +++ .../main/resources/mapper/AdminRoleMapper.xml | 21 +++ .../mapper/OAuth2AccessTokenMapper.xml | 22 +++ .../mapper/OAuth2RefreshTokenMapper.xml | 13 ++ .../resources/mapper/RoleResourceMapper.xml | 21 +++ .../src/main/resources/mybatis-config.xml | 19 +++ admin/pom.xml | 22 +++ .../common/framework/util/HttpUtil.java | 21 +++ pom.xml | 1 + .../mall/user/config/MVCConfiguration.java | 6 +- .../user/controller/PassportController.java | 2 +- .../mall/user/controller/UserController.java | 4 +- .../sdk/context/SecurityContextHolder.java | 30 ---- ...yContext.java => UserSecurityContext.java} | 6 +- .../context/UserSecurityContextHolder.java | 30 ++++ ...ptor.java => UserSecurityInterceptor.java} | 28 ++-- .../iocoder/mall/user/sdk/package-info.java | 2 +- .../mall/user/service/api/OAuth2Service.java | 16 +-- .../config/ServiceExceptionConfiguration.java | 0 .../user/service/MobileCodeServiceImpl.java | 26 +--- .../mall/user/service/OAuth2ServiceImpl.java | 24 +--- .../mapper/OAuth2AccessTokenMapper.xml | 6 +- .../mapper/OAuth2RefreshTokenMapper.xml | 4 +- 66 files changed, 1952 insertions(+), 127 deletions(-) create mode 100644 admin/admin-application/pom.xml create mode 100644 admin/admin-application/src/main/java/cn/iocoder/mall/admin/AdminApplication.java create mode 100644 admin/admin-application/src/main/java/cn/iocoder/mall/admin/config/MVCConfiguration.java create mode 100644 admin/admin-application/src/main/java/cn/iocoder/mall/admin/config/SwaggerConfiguration.java create mode 100644 admin/admin-application/src/main/java/cn/iocoder/mall/admin/controller/AdminController.java create mode 100644 admin/admin-application/src/main/java/cn/iocoder/mall/admin/controller/PassportController.java create mode 100644 admin/admin-application/src/main/java/cn/iocoder/mall/admin/convert/PassportConvert.java create mode 100644 admin/admin-application/src/main/java/cn/iocoder/mall/admin/vo/PassportLoginVO.java create mode 100644 admin/admin-application/src/main/resources/application.yaml create mode 100644 admin/admin-sdk/pom.xml create mode 100644 admin/admin-sdk/src/main/java/cn/iocoder/mall/admin/sdk/context/AdminSecurityContext.java create mode 100644 admin/admin-sdk/src/main/java/cn/iocoder/mall/admin/sdk/context/AdminSecurityContextHolder.java create mode 100644 admin/admin-sdk/src/main/java/cn/iocoder/mall/admin/sdk/interceptor/AdminSecurityInterceptor.java create mode 100644 admin/admin-sdk/src/main/java/cn/iocoder/mall/admin/sdk/package-info.java create mode 100644 admin/admin-service-api/pom.xml create mode 100644 admin/admin-service-api/src/main/java/cn/iocoder/mall/admin/api/AdminService.java create mode 100644 admin/admin-service-api/src/main/java/cn/iocoder/mall/admin/api/OAuth2Service.java create mode 100644 admin/admin-service-api/src/main/java/cn/iocoder/mall/admin/api/RoleService.java create mode 100644 admin/admin-service-api/src/main/java/cn/iocoder/mall/admin/api/bo/OAuth2AccessTokenBO.java create mode 100644 admin/admin-service-api/src/main/java/cn/iocoder/mall/admin/api/bo/OAuth2AuthenticationBO.java create mode 100644 admin/admin-service-api/src/main/java/cn/iocoder/mall/admin/api/constant/AdminErrorCodeEnum.java create mode 100644 admin/admin-service-impl/pom.xml create mode 100644 admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/config/DatabaseConfiguration.java create mode 100644 admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/config/ServiceExceptionConfiguration.java create mode 100644 admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/convert/OAuth2Convert.java create mode 100644 admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dao/AdminMapper.java create mode 100644 admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dao/AdminRoleMapper.java create mode 100644 admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dao/OAuth2AccessTokenMapper.java create mode 100644 admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dao/OAuth2RefreshTokenMapper.java create mode 100644 admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dao/RoleResourceMapper.java create mode 100644 admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/AdminDO.java create mode 100644 admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/AdminRoleDO.java create mode 100644 admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/OAuth2AccessTokenDO.java create mode 100644 admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/OAuth2RefreshTokenDO.java create mode 100644 admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/ResourceDO.java create mode 100644 admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/RoleDO.java create mode 100644 admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/RoleResourceDO.java create mode 100644 admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/package-info.java create mode 100644 admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/service/AdminServiceImpl.java create mode 100644 admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/service/OAuth2ServiceImpl.java create mode 100644 admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/service/RoleServiceImpl.java create mode 100644 admin/admin-service-impl/src/main/resources/config/application.properties create mode 100644 admin/admin-service-impl/src/main/resources/config/application.yaml create mode 100644 admin/admin-service-impl/src/main/resources/mapper/AdminMapper.xml create mode 100644 admin/admin-service-impl/src/main/resources/mapper/AdminRoleMapper.xml create mode 100644 admin/admin-service-impl/src/main/resources/mapper/OAuth2AccessTokenMapper.xml create mode 100644 admin/admin-service-impl/src/main/resources/mapper/OAuth2RefreshTokenMapper.xml create mode 100644 admin/admin-service-impl/src/main/resources/mapper/RoleResourceMapper.xml create mode 100644 admin/admin-service-impl/src/main/resources/mybatis-config.xml create mode 100644 admin/pom.xml create mode 100644 common/common-framework/src/main/java/cn/iocoder/common/framework/util/HttpUtil.java delete mode 100644 user/user-sdk/src/main/java/cn/iocoder/mall/user/sdk/context/SecurityContextHolder.java rename user/user-sdk/src/main/java/cn/iocoder/mall/user/sdk/context/{SecurityContext.java => UserSecurityContext.java} (61%) create mode 100644 user/user-sdk/src/main/java/cn/iocoder/mall/user/sdk/context/UserSecurityContextHolder.java rename user/user-sdk/src/main/java/cn/iocoder/mall/user/sdk/interceptor/{SecurityInterceptor.java => UserSecurityInterceptor.java} (69%) rename user/{user-application => user-service-impl}/src/main/java/cn/iocoder/mall/user/config/ServiceExceptionConfiguration.java (100%) diff --git a/admin/admin-application/pom.xml b/admin/admin-application/pom.xml new file mode 100644 index 000000000..c316c806d --- /dev/null +++ b/admin/admin-application/pom.xml @@ -0,0 +1,114 @@ + + + + admin + cn.iocoder.mall + 1.0-SNAPSHOT + + 4.0.0 + + admin-application + + + 1.3.0.Final + + + + + cn.iocoder.mall + admin-service-impl + 1.0-SNAPSHOT + + + + cn.iocoder.mall + common-framework + 1.0-SNAPSHOT + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + test + + + + com.alibaba + dubbo + 2.6.5 + + + + com.alibaba.boot + dubbo-spring-boot-starter + 0.2.1.RELEASE + + + + org.apache.curator + curator-framework + 2.12.0 + + + + org.mapstruct + mapstruct + ${org.mapstruct.version} + + + + io.springfox + springfox-swagger2 + 2.9.2 + + + io.springfox + springfox-swagger-ui + 2.9.2 + + + + org.mapstruct + mapstruct + ${org.mapstruct.version} + + + + org.springframework.boot + spring-boot-devtools + true + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.5.1 + + 1.8 + 1.8 + + + org.mapstruct + mapstruct-processor + ${org.mapstruct.version} + + + + + + + + + \ No newline at end of file diff --git a/admin/admin-application/src/main/java/cn/iocoder/mall/admin/AdminApplication.java b/admin/admin-application/src/main/java/cn/iocoder/mall/admin/AdminApplication.java new file mode 100644 index 000000000..46385f023 --- /dev/null +++ b/admin/admin-application/src/main/java/cn/iocoder/mall/admin/AdminApplication.java @@ -0,0 +1,13 @@ +package cn.iocoder.mall.admin; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication(scanBasePackages = {"cn.iocoder.mall.admin"}) +public class AdminApplication { + + public static void main(String[] args) { + SpringApplication.run(AdminApplication.class, args); + } + +} \ No newline at end of file diff --git a/admin/admin-application/src/main/java/cn/iocoder/mall/admin/config/MVCConfiguration.java b/admin/admin-application/src/main/java/cn/iocoder/mall/admin/config/MVCConfiguration.java new file mode 100644 index 000000000..70d181d5d --- /dev/null +++ b/admin/admin-application/src/main/java/cn/iocoder/mall/admin/config/MVCConfiguration.java @@ -0,0 +1,29 @@ +package cn.iocoder.mall.admin.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@EnableWebMvc +@Configuration +//@Import(value = {GlobalExceptionHandler.class, // 统一全局返回 +// ) // TODO 安全拦截器,实现认证和授权功能。 +public class MVCConfiguration implements WebMvcConfigurer { + +// @Autowired +// private UserSecurityInterceptor securityInterceptor; +// +// @Override +// public void addInterceptors(InterceptorRegistry registry) { +// registry.addInterceptor(securityInterceptor).addPathPatterns("/user/**", "/admin/**"); // 只拦截我们定义的接口 +// } + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + // 解决 swagger-ui.html 的访问,参考自 https://stackoverflow.com/questions/43545540/swagger-ui-no-mapping-found-for-http-request 解决 + registry.addResourceHandler("swagger-ui.html**").addResourceLocations("classpath:/META-INF/resources/swagger-ui.html"); + registry.addResourceHandler("webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/"); + } + +} \ No newline at end of file diff --git a/admin/admin-application/src/main/java/cn/iocoder/mall/admin/config/SwaggerConfiguration.java b/admin/admin-application/src/main/java/cn/iocoder/mall/admin/config/SwaggerConfiguration.java new file mode 100644 index 000000000..a6b26cd2b --- /dev/null +++ b/admin/admin-application/src/main/java/cn/iocoder/mall/admin/config/SwaggerConfiguration.java @@ -0,0 +1,36 @@ +package cn.iocoder.mall.admin.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +@Configuration +@EnableSwagger2 +public class SwaggerConfiguration { + + @Bean + public Docket createRestApi() { + return new Docket(DocumentationType.SWAGGER_2) + .apiInfo(apiInfo()) + .select() + .apis(RequestHandlerSelectors.basePackage("cn.iocoder.mall.admin.controller")) + .paths(PathSelectors.any()) + .build(); + } + + private ApiInfo apiInfo() { + return new ApiInfoBuilder() + .title("管理员子系统") + .description("管理员子系统") + .termsOfServiceUrl("http://www.iocoder.cn") + .version("1.0.0") + .build(); + } + +} \ No newline at end of file diff --git a/admin/admin-application/src/main/java/cn/iocoder/mall/admin/controller/AdminController.java b/admin/admin-application/src/main/java/cn/iocoder/mall/admin/controller/AdminController.java new file mode 100644 index 000000000..33f556690 --- /dev/null +++ b/admin/admin-application/src/main/java/cn/iocoder/mall/admin/controller/AdminController.java @@ -0,0 +1,14 @@ +package cn.iocoder.mall.admin.controller; + +import io.swagger.annotations.Api; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("admin/admin") +@Api("管理员模块") +public class AdminController { + + + +} \ No newline at end of file diff --git a/admin/admin-application/src/main/java/cn/iocoder/mall/admin/controller/PassportController.java b/admin/admin-application/src/main/java/cn/iocoder/mall/admin/controller/PassportController.java new file mode 100644 index 000000000..4e5c5ef7f --- /dev/null +++ b/admin/admin-application/src/main/java/cn/iocoder/mall/admin/controller/PassportController.java @@ -0,0 +1,38 @@ +package cn.iocoder.mall.admin.controller; + +import cn.iocoder.common.framework.vo.CommonResult; +import cn.iocoder.mall.admin.api.OAuth2Service; +import cn.iocoder.mall.admin.api.bo.OAuth2AccessTokenBO; +import cn.iocoder.mall.admin.convert.PassportConvert; +import cn.iocoder.mall.admin.vo.PassportLoginVO; +import com.alibaba.dubbo.config.annotation.Reference; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("admin/passport") +@Api("Admin Passport 模块") +public class PassportController { + + @Reference + private OAuth2Service oauth2Service; + + @PostMapping("/login") + @ApiOperation(value = "手机号 + 验证码登陆(注册)", notes = "如果手机对应的账号不存在,则会自动创建") + @ApiImplicitParams({ + @ApiImplicitParam(name = "username", value = "账号", required = true, example = "15601691300"), + @ApiImplicitParam(name = "password", value = "密码", required = true, example = "future") + }) + public CommonResult login(@RequestParam("username") String username, + @RequestParam("password") String password) { + CommonResult result = oauth2Service.getAccessToken(username, password); + return PassportConvert.INSTANCE.convert(result); + } + +} \ No newline at end of file diff --git a/admin/admin-application/src/main/java/cn/iocoder/mall/admin/convert/PassportConvert.java b/admin/admin-application/src/main/java/cn/iocoder/mall/admin/convert/PassportConvert.java new file mode 100644 index 000000000..1572dfbfd --- /dev/null +++ b/admin/admin-application/src/main/java/cn/iocoder/mall/admin/convert/PassportConvert.java @@ -0,0 +1,21 @@ +package cn.iocoder.mall.admin.convert; + +import cn.iocoder.common.framework.vo.CommonResult; +import cn.iocoder.mall.admin.api.bo.OAuth2AccessTokenBO; +import cn.iocoder.mall.admin.vo.PassportLoginVO; +import org.mapstruct.Mapper; +import org.mapstruct.Mappings; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface PassportConvert { + + PassportConvert INSTANCE = Mappers.getMapper(PassportConvert.class); + + @Mappings({}) + PassportLoginVO convert(OAuth2AccessTokenBO oauth2AccessTokenBO); + + @Mappings({}) + CommonResult convert(CommonResult oauth2AccessTokenBO); + +} \ No newline at end of file diff --git a/admin/admin-application/src/main/java/cn/iocoder/mall/admin/vo/PassportLoginVO.java b/admin/admin-application/src/main/java/cn/iocoder/mall/admin/vo/PassportLoginVO.java new file mode 100644 index 000000000..b1376bcde --- /dev/null +++ b/admin/admin-application/src/main/java/cn/iocoder/mall/admin/vo/PassportLoginVO.java @@ -0,0 +1,43 @@ +package cn.iocoder.mall.admin.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +@ApiModel("登陆结果 VO") +public class PassportLoginVO { + + @ApiModelProperty(value = "访问令牌", required = true, example = "2e3d7635c15e47e997611707a237859f") + private String accessToken; + @ApiModelProperty(value = "刷新令牌", required = true, example = "d091e7c35bbb4313b0f557a6ef23d033") + private String refreshToken; + @ApiModelProperty(value = "过期时间,单位:秒", required = true, example = "2879") + private Integer expiresIn; + + public String getAccessToken() { + return accessToken; + } + + public PassportLoginVO setAccessToken(String accessToken) { + this.accessToken = accessToken; + return this; + } + + public String getRefreshToken() { + return refreshToken; + } + + public PassportLoginVO setRefreshToken(String refreshToken) { + this.refreshToken = refreshToken; + return this; + } + + public Integer getExpiresIn() { + return expiresIn; + } + + public PassportLoginVO setExpiresIn(Integer expiresIn) { + this.expiresIn = expiresIn; + return this; + } + +} \ No newline at end of file diff --git a/admin/admin-application/src/main/resources/application.yaml b/admin/admin-application/src/main/resources/application.yaml new file mode 100644 index 000000000..e6d37fdf2 --- /dev/null +++ b/admin/admin-application/src/main/resources/application.yaml @@ -0,0 +1,7 @@ +spring: + application: + name: admin-application + +# server +server: + port: 8083 \ No newline at end of file diff --git a/admin/admin-sdk/pom.xml b/admin/admin-sdk/pom.xml new file mode 100644 index 000000000..b83e37b75 --- /dev/null +++ b/admin/admin-sdk/pom.xml @@ -0,0 +1,53 @@ + + + + admin + cn.iocoder.mall + 1.0-SNAPSHOT + + 4.0.0 + + application-sdk + + + org.springframework + spring-context + 5.1.5.RELEASE + compile + + + org.springframework + spring-webmvc + 5.1.5.RELEASE + compile + + + com.alibaba + dubbo + 2.6.5 + compile + + + javax.servlet + servlet-api + 2.5 + compile + + + cn.iocoder.mall + common-framework + 1.0-SNAPSHOT + compile + + + cn.iocoder.mall + admin-service-api + 1.0-SNAPSHOT + compile + + + + + \ No newline at end of file diff --git a/admin/admin-sdk/src/main/java/cn/iocoder/mall/admin/sdk/context/AdminSecurityContext.java b/admin/admin-sdk/src/main/java/cn/iocoder/mall/admin/sdk/context/AdminSecurityContext.java new file mode 100644 index 000000000..a720ad09c --- /dev/null +++ b/admin/admin-sdk/src/main/java/cn/iocoder/mall/admin/sdk/context/AdminSecurityContext.java @@ -0,0 +1,26 @@ +package cn.iocoder.mall.admin.sdk.context; + +import java.util.Set; + +/** + * Security 上下文 + */ +public class AdminSecurityContext { + + private final Integer adminId; + private final Set roleIds; + + public AdminSecurityContext(Integer adminId, Set roleIds) { + this.adminId = adminId; + this.roleIds = roleIds; + } + + public Integer getAdminId() { + return adminId; + } + + public Set getRoleIds() { + return roleIds; + } + +} \ No newline at end of file diff --git a/admin/admin-sdk/src/main/java/cn/iocoder/mall/admin/sdk/context/AdminSecurityContextHolder.java b/admin/admin-sdk/src/main/java/cn/iocoder/mall/admin/sdk/context/AdminSecurityContextHolder.java new file mode 100644 index 000000000..9eca2c5bf --- /dev/null +++ b/admin/admin-sdk/src/main/java/cn/iocoder/mall/admin/sdk/context/AdminSecurityContextHolder.java @@ -0,0 +1,30 @@ +package cn.iocoder.mall.admin.sdk.context; + +/** + * {@link AdminSecurityContext} Holder + * + * 参考 spring security 的 ThreadLocalSecurityContextHolderStrategy 类,简单实现。 + */ +public class AdminSecurityContextHolder { + + private static final ThreadLocal securityContext = new ThreadLocal(); + + public static void setContext(AdminSecurityContext context) { + securityContext.set(context); + } + + public static AdminSecurityContext getContext() { + AdminSecurityContext ctx = securityContext.get(); + // 为空时,设置一个空的进去 + if (ctx == null) { + ctx = new AdminSecurityContext(null, roleIds); + securityContext.set(ctx); + } + return ctx; + } + + public static void clear() { + securityContext.remove(); + } + +} \ No newline at end of file diff --git a/admin/admin-sdk/src/main/java/cn/iocoder/mall/admin/sdk/interceptor/AdminSecurityInterceptor.java b/admin/admin-sdk/src/main/java/cn/iocoder/mall/admin/sdk/interceptor/AdminSecurityInterceptor.java new file mode 100644 index 000000000..6ba914581 --- /dev/null +++ b/admin/admin-sdk/src/main/java/cn/iocoder/mall/admin/sdk/interceptor/AdminSecurityInterceptor.java @@ -0,0 +1,64 @@ +package cn.iocoder.mall.admin.sdk.interceptor; + +import cn.iocoder.common.framework.exception.ServiceException; +import cn.iocoder.common.framework.util.HttpUtil; +import cn.iocoder.common.framework.vo.CommonResult; +import cn.iocoder.mall.admin.api.OAuth2Service; +import cn.iocoder.mall.admin.api.bo.OAuth2AuthenticationBO; +import cn.iocoder.mall.admin.sdk.context.AdminSecurityContext; +import cn.iocoder.mall.admin.sdk.context.AdminSecurityContextHolder; +import com.alibaba.dubbo.config.annotation.Reference; +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.Set; + +/** + * 安全拦截器 + */ +@Component +public class AdminSecurityInterceptor extends HandlerInterceptorAdapter { + + @Reference + private OAuth2Service oauth2Service; + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + // 校验访问令牌是否正确。若正确,返回授权信息 + String accessToken = HttpUtil.obtainAccess(request); + OAuth2AuthenticationBO authentication = null; + if (accessToken != null) { + CommonResult result = oauth2Service.checkToken(accessToken); + if (result.isError()) { // TODO 芋艿,如果访问的地址无需登录,这里也不用抛异常 + throw new ServiceException(result.getCode(), result.getMessage()); + } + authentication = result.getData(); + // 添加到 SecurityContext + AdminSecurityContext context = new AdminSecurityContext(authentication.getAdminId(), authentication.getRoleIds()); + AdminSecurityContextHolder.setContext(context); + } + // 校验是否需要已授权 + checkPermission(request, authentication); + // 返回成功 + return super.preHandle(request, response, handler); + } + + @Override + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { + // 清空 SecurityContext + AdminSecurityContextHolder.clear(); + } + + private void checkPermission(HttpServletRequest request, OAuth2AuthenticationBO authentication) { + Integer adminId = authentication != null ? authentication.getAdminId() : null; + Set roleIds = authentication != null ? authentication.getRoleIds() : null; + String url = request.getRequestURI(); + CommonResult result = oauth2Service.checkPermission(adminId, roleIds, url); + if (result.isError()) { + throw new ServiceException(result.getCode(), result.getMessage()); + } + } + +} \ No newline at end of file diff --git a/admin/admin-sdk/src/main/java/cn/iocoder/mall/admin/sdk/package-info.java b/admin/admin-sdk/src/main/java/cn/iocoder/mall/admin/sdk/package-info.java new file mode 100644 index 000000000..57a5a7f74 --- /dev/null +++ b/admin/admin-sdk/src/main/java/cn/iocoder/mall/admin/sdk/package-info.java @@ -0,0 +1,6 @@ +/** + * 提供 SDK 给其它服务,使用如下功能: + * + * 1. 通过 {@link cn.iocoder.mall.admin.sdk.interceptor.UserSecurityInterceptor} 拦截器,实现需要登陆 URL 的鉴权 + */ +package cn.iocoder.mall.admin.sdk; \ No newline at end of file diff --git a/admin/admin-service-api/pom.xml b/admin/admin-service-api/pom.xml new file mode 100644 index 000000000..bb3089468 --- /dev/null +++ b/admin/admin-service-api/pom.xml @@ -0,0 +1,27 @@ + + + + admin + cn.iocoder.mall + 1.0-SNAPSHOT + + 4.0.0 + + admin-service-api + + + cn.iocoder.mall + common-framework + 1.0-SNAPSHOT + + + cn.iocoder.mall + admin-service-api + 1.0-SNAPSHOT + + + + + \ No newline at end of file diff --git a/admin/admin-service-api/src/main/java/cn/iocoder/mall/admin/api/AdminService.java b/admin/admin-service-api/src/main/java/cn/iocoder/mall/admin/api/AdminService.java new file mode 100644 index 000000000..36079c724 --- /dev/null +++ b/admin/admin-service-api/src/main/java/cn/iocoder/mall/admin/api/AdminService.java @@ -0,0 +1,16 @@ +package cn.iocoder.mall.admin.api; + +public interface AdminService { + +// /** +// * 创建用户。一般在用户注册时,调用该方法 +// * +// * TODO 芋艿,此处要传递一些用户注册时的相关信息,例如说 ip、ua、客户端来源等等。用于数据分析、风控等等。 +// * +// * @param mobile 手机号 +// * @param code 手机验证码 +// * @return 用户 +// */ +// UserBO createUser(String mobile, String code) throws ServiceException; + +} \ No newline at end of file diff --git a/admin/admin-service-api/src/main/java/cn/iocoder/mall/admin/api/OAuth2Service.java b/admin/admin-service-api/src/main/java/cn/iocoder/mall/admin/api/OAuth2Service.java new file mode 100644 index 000000000..cb62661dc --- /dev/null +++ b/admin/admin-service-api/src/main/java/cn/iocoder/mall/admin/api/OAuth2Service.java @@ -0,0 +1,35 @@ +package cn.iocoder.mall.admin.api; + +import cn.iocoder.common.framework.vo.CommonResult; +import cn.iocoder.mall.admin.api.bo.OAuth2AccessTokenBO; +import cn.iocoder.mall.admin.api.bo.OAuth2AuthenticationBO; + +import java.util.Set; + +public interface OAuth2Service { + + CommonResult getAccessToken(String username, String password); + + /** + * 校验访问令牌,获取身份信息( 不包括 accessToken 等等 ) + * + * @param accessToken 访问令牌 + * @return 授权信息 + */ + CommonResult checkToken(String accessToken); + + /** + * TODO 校验权限 + * + * @param adminId 管理员编号 + * @param roleIds 管理员拥有的角色编号的集合 + * @param url 指定 URL + * @return 是否有权限 + */ + CommonResult checkPermission(Integer adminId, Set roleIds, String url); + + // TODO @see 刷新 token + + // TODO @see 移除 token + +} \ No newline at end of file diff --git a/admin/admin-service-api/src/main/java/cn/iocoder/mall/admin/api/RoleService.java b/admin/admin-service-api/src/main/java/cn/iocoder/mall/admin/api/RoleService.java new file mode 100644 index 000000000..b19deee5d --- /dev/null +++ b/admin/admin-service-api/src/main/java/cn/iocoder/mall/admin/api/RoleService.java @@ -0,0 +1,4 @@ +package cn.iocoder.mall.admin.api; + +public interface RoleService { +} diff --git a/admin/admin-service-api/src/main/java/cn/iocoder/mall/admin/api/bo/OAuth2AccessTokenBO.java b/admin/admin-service-api/src/main/java/cn/iocoder/mall/admin/api/bo/OAuth2AccessTokenBO.java new file mode 100644 index 000000000..3d66558d2 --- /dev/null +++ b/admin/admin-service-api/src/main/java/cn/iocoder/mall/admin/api/bo/OAuth2AccessTokenBO.java @@ -0,0 +1,47 @@ +package cn.iocoder.mall.admin.api.bo; + +import java.io.Serializable; + +public class OAuth2AccessTokenBO implements Serializable { + + /** + * 访问令牌 + */ + private String accessToken; + /** + * 刷新令牌 + */ + private String refreshToken; + /** + * 过期时间,单位:秒。 + */ + private Integer expiresIn; + + public String getAccessToken() { + return accessToken; + } + + public OAuth2AccessTokenBO setAccessToken(String accessToken) { + this.accessToken = accessToken; + return this; + } + + public String getRefreshToken() { + return refreshToken; + } + + public OAuth2AccessTokenBO setRefreshToken(String refreshToken) { + this.refreshToken = refreshToken; + return this; + } + + public Integer getExpiresIn() { + return expiresIn; + } + + public OAuth2AccessTokenBO setExpiresIn(Integer expiresIn) { + this.expiresIn = expiresIn; + return this; + } + +} \ No newline at end of file diff --git a/admin/admin-service-api/src/main/java/cn/iocoder/mall/admin/api/bo/OAuth2AuthenticationBO.java b/admin/admin-service-api/src/main/java/cn/iocoder/mall/admin/api/bo/OAuth2AuthenticationBO.java new file mode 100644 index 000000000..f499e25ca --- /dev/null +++ b/admin/admin-service-api/src/main/java/cn/iocoder/mall/admin/api/bo/OAuth2AuthenticationBO.java @@ -0,0 +1,35 @@ +package cn.iocoder.mall.admin.api.bo; + +import java.io.Serializable; +import java.util.Set; + +public class OAuth2AuthenticationBO implements Serializable { + + /** + * 管理员编号 + */ + private Integer adminId; + /** + * 角色编号数组 + */ + private Set roleIds; + + public Integer getAdminId() { + return adminId; + } + + public OAuth2AuthenticationBO setAdminId(Integer adminId) { + this.adminId = adminId; + return this; + } + + public Set getRoleIds() { + return roleIds; + } + + public OAuth2AuthenticationBO setRoleIds(Set roleIds) { + this.roleIds = roleIds; + return this; + } + +} \ No newline at end of file diff --git a/admin/admin-service-api/src/main/java/cn/iocoder/mall/admin/api/constant/AdminErrorCodeEnum.java b/admin/admin-service-api/src/main/java/cn/iocoder/mall/admin/api/constant/AdminErrorCodeEnum.java new file mode 100644 index 000000000..85d445641 --- /dev/null +++ b/admin/admin-service-api/src/main/java/cn/iocoder/mall/admin/api/constant/AdminErrorCodeEnum.java @@ -0,0 +1,43 @@ +package cn.iocoder.mall.admin.api.constant; + +/** + * 错误码枚举类 + * + * 用户中心,使用 1-002-000-000 段 + */ +public enum AdminErrorCodeEnum { + + // ========== OAUTH2 模块 ========== + OAUTH2_UNKNOWN(1002001000, "未知错误"), // 预留 +// OAUTH2_INVALID_GRANT_BAD_CREDENTIALS(1001001001, "密码不正确"), // 暂时没用到 +// OAUTH2_INVALID_GRANT_USERNAME_NOT_FOUND(1001001002, "账号不存在"), // 暂时没用到 +// OAUTH2_INVALID_GRANT(1001001010, ""), // 预留 + OAUTH_INVALID_TOKEN_NOT_FOUND(1002001011, "访问令牌不存在"), + OAUTH_INVALID_TOKEN_EXPIRED(1002001012, "访问令牌已过期"), + OAUTH_INVALID_TOKEN_INVALID(1002001013, "访问令牌已失效"), + OAUTH_INVALID_PERMISSION(1002001014, "没有该操作权限"), // TODO 芋艿,临时放在 OAUTH2 模块,理论来说,OAUTH2 只做认证,不做鉴权。 + + OAUTH_INVALID_TOKEN(1002001020, ""), // 预留 + + // ========== 管理员模块 ========== + ADMIN_USERNAME_NOT_REGISTERED(1002002000, "账号不存在"), + ADMIN_PASSWORD_ERROR(1002002001, "密码不正确"), + ADMIN_IS_DISABLE(1002002002, "账号被禁用"); + + private final int code; + private final String message; + + AdminErrorCodeEnum(int code, String message) { + this.code = code; + this.message = message; + } + + public int getCode() { + return code; + } + + public String getMessage() { + return message; + } + +} \ No newline at end of file diff --git a/admin/admin-service-impl/pom.xml b/admin/admin-service-impl/pom.xml new file mode 100644 index 000000000..578ae0764 --- /dev/null +++ b/admin/admin-service-impl/pom.xml @@ -0,0 +1,77 @@ + + + + admin + cn.iocoder.mall + 1.0-SNAPSHOT + + 4.0.0 + + admin-service-impl + + + 1.3.0.Final + + + + + com.alibaba + dubbo + 2.6.5 + compile + + + cn.iocoder.mall + admin-service-api + 1.0-SNAPSHOT + compile + + + + mysql + mysql-connector-java + + + org.springframework.boot + spring-boot-starter-jdbc + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 2.0.0 + + + org.mapstruct + mapstruct + ${org.mapstruct.version} + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.5.1 + + 1.8 + 1.8 + + + org.mapstruct + mapstruct-processor + ${org.mapstruct.version} + + + + + + + + + \ No newline at end of file diff --git a/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/config/DatabaseConfiguration.java b/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/config/DatabaseConfiguration.java new file mode 100644 index 000000000..efa7bf84a --- /dev/null +++ b/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/config/DatabaseConfiguration.java @@ -0,0 +1,14 @@ +package cn.iocoder.mall.admin.config; + +import org.mybatis.spring.annotation.MapperScan; +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 { + + // 数据源,使用 HikariCP + +} \ No newline at end of file diff --git a/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/config/ServiceExceptionConfiguration.java b/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/config/ServiceExceptionConfiguration.java new file mode 100644 index 000000000..de21043ac --- /dev/null +++ b/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/config/ServiceExceptionConfiguration.java @@ -0,0 +1,26 @@ +package cn.iocoder.mall.admin.config; + +import cn.iocoder.common.framework.util.ServiceExceptionUtil; +import cn.iocoder.mall.admin.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() { +// 从 service_exception_message.properties 加载错误码的方案 +// Properties properties; +// try { +// properties = PropertiesLoaderUtils.loadAllProperties("classpath:service_exception_message.properties"); +// } catch (IOException e) { +// throw new RuntimeException(e); +// } + for (AdminErrorCodeEnum item : AdminErrorCodeEnum.values()) { + ServiceExceptionUtil.put(item.getCode(), item.getMessage()); + } + } + +} \ No newline at end of file diff --git a/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/convert/OAuth2Convert.java b/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/convert/OAuth2Convert.java new file mode 100644 index 000000000..57ccd8f8f --- /dev/null +++ b/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/convert/OAuth2Convert.java @@ -0,0 +1,35 @@ +package cn.iocoder.mall.admin.convert; + +import cn.iocoder.mall.admin.api.bo.OAuth2AccessTokenBO; +import cn.iocoder.mall.admin.api.bo.OAuth2AuthenticationBO; +import cn.iocoder.mall.admin.dataobject.AdminRoleDO; +import cn.iocoder.mall.admin.dataobject.OAuth2AccessTokenDO; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +@Mapper +public interface OAuth2Convert { + + OAuth2Convert INSTANCE = Mappers.getMapper(OAuth2Convert.class); + + @Mappings({ + @Mapping(source = "id", target = "accessToken") + }) + OAuth2AccessTokenBO convertToAccessToken(OAuth2AccessTokenDO oauth2AccessTokenDO); + + default OAuth2AccessTokenBO convertToAccessTokenWithExpiresIn(OAuth2AccessTokenDO oauth2AccessTokenDO) { + return this.convertToAccessToken(oauth2AccessTokenDO) + .setExpiresIn(Math.max((int) ((oauth2AccessTokenDO.getExpiresTime().getTime() - System.currentTimeMillis()) / 1000), 0)); + } + + @Mappings({ + @Mapping(source = "oauth2AccessTokenDO.id", target = "accessToken"), + @Mapping(source = "adminRoleDOs.roleId", target = "roleIds") + }) + OAuth2AuthenticationBO convertToAuthentication(OAuth2AccessTokenDO oauth2AccessTokenDO, List adminRoleDOs); + +} \ No newline at end of file diff --git a/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dao/AdminMapper.java b/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dao/AdminMapper.java new file mode 100644 index 000000000..d27e96b02 --- /dev/null +++ b/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dao/AdminMapper.java @@ -0,0 +1,12 @@ +package cn.iocoder.mall.admin.dao; + +import cn.iocoder.mall.admin.dataobject.AdminDO; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; + +@Repository +public interface AdminMapper { + + AdminDO selectByUsername(@Param("username") String username); + +} \ No newline at end of file diff --git a/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dao/AdminRoleMapper.java b/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dao/AdminRoleMapper.java new file mode 100644 index 000000000..3a0399e97 --- /dev/null +++ b/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dao/AdminRoleMapper.java @@ -0,0 +1,14 @@ +package cn.iocoder.mall.admin.dao; + +import cn.iocoder.mall.admin.dataobject.AdminRoleDO; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface AdminRoleMapper { + + List selectByAdminId(@Param("adminId") Integer adminId); + +} \ No newline at end of file diff --git a/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dao/OAuth2AccessTokenMapper.java b/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dao/OAuth2AccessTokenMapper.java new file mode 100644 index 000000000..4e1a1399a --- /dev/null +++ b/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dao/OAuth2AccessTokenMapper.java @@ -0,0 +1,13 @@ +package cn.iocoder.mall.admin.dao; + +import cn.iocoder.mall.admin.dataobject.OAuth2AccessTokenDO; +import org.springframework.stereotype.Repository; + +@Repository +public interface OAuth2AccessTokenMapper { + + void insert(OAuth2AccessTokenDO entity); + + OAuth2AccessTokenDO selectByTokenId(String tokenId); + +} \ No newline at end of file diff --git a/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dao/OAuth2RefreshTokenMapper.java b/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dao/OAuth2RefreshTokenMapper.java new file mode 100644 index 000000000..73659b94b --- /dev/null +++ b/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dao/OAuth2RefreshTokenMapper.java @@ -0,0 +1,11 @@ +package cn.iocoder.mall.admin.dao; + +import cn.iocoder.mall.admin.dataobject.OAuth2RefreshTokenDO; +import org.springframework.stereotype.Repository; + +@Repository +public interface OAuth2RefreshTokenMapper { + + void insert(OAuth2RefreshTokenDO entity); + +} \ No newline at end of file diff --git a/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dao/RoleResourceMapper.java b/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dao/RoleResourceMapper.java new file mode 100644 index 000000000..92f882c06 --- /dev/null +++ b/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dao/RoleResourceMapper.java @@ -0,0 +1,14 @@ +package cn.iocoder.mall.admin.dao; + +import cn.iocoder.mall.admin.dataobject.RoleResourceDO; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface RoleResourceMapper { + + List selectByResourceHandler(@Param("resourceHandler") String resourceHandler); + +} \ No newline at end of file diff --git a/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/AdminDO.java b/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/AdminDO.java new file mode 100644 index 000000000..1874a0ea6 --- /dev/null +++ b/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/AdminDO.java @@ -0,0 +1,100 @@ +package cn.iocoder.mall.admin.dataobject; + +import java.util.Date; + +/** + * 管理员实体 + */ +public class AdminDO { + + /** + * 账号状态 - 开启 + */ + public static final Integer STATUS_ENABLE = 1; + /** + * 账号状态 - 禁用 + */ + public static final Integer STATUS_DISABLE = 2; + + /** + * 管理员编号 + */ + private Integer id; + /** + * 登陆账号 + */ + private String username; + /** + * 昵称 + */ + private String nickname; + /** + * 密码 + * + * TODO 芋艿 暂时最简单的 MD5 + */ + private String password; + /** + * 创建时间 + */ + private Date createTime; + /** + * 账号状态 + */ + private Integer status; + + public Integer getId() { + return id; + } + + public AdminDO setId(Integer id) { + this.id = id; + return this; + } + + public String getUsername() { + return username; + } + + public AdminDO setUsername(String username) { + this.username = username; + return this; + } + + public String getNickname() { + return nickname; + } + + public AdminDO setNickname(String nickname) { + this.nickname = nickname; + return this; + } + + public String getPassword() { + return password; + } + + public AdminDO setPassword(String password) { + this.password = password; + return this; + } + + public Date getCreateTime() { + return createTime; + } + + public AdminDO setCreateTime(Date createTime) { + this.createTime = createTime; + return this; + } + + public Integer getStatus() { + return status; + } + + public AdminDO setStatus(Integer status) { + this.status = status; + return this; + } + +} diff --git a/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/AdminRoleDO.java b/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/AdminRoleDO.java new file mode 100644 index 000000000..048ac41e6 --- /dev/null +++ b/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/AdminRoleDO.java @@ -0,0 +1,65 @@ +package cn.iocoder.mall.admin.dataobject; + +import java.util.Date; + +/** + * {@link AdminDO} 和 {@link RoleDO} 的关联表 + */ +public class AdminRoleDO { + + /** + * 编号 + */ + private Integer id; + /** + * 管理员编号(外键:{@link AdminDO} + */ + private Integer adminId; + /** + * 角色编号(外键:{@link RoleDO} + */ + private Integer roleId; + /** + * 创建时间 + */ + private Date createTime; + + // TODO 芋艿 删除状态 + + public Integer getId() { + return id; + } + + public AdminRoleDO setId(Integer id) { + this.id = id; + return this; + } + + public Integer getAdminId() { + return adminId; + } + + public AdminRoleDO setAdminId(Integer adminId) { + this.adminId = adminId; + return this; + } + + public Integer getRoleId() { + return roleId; + } + + public AdminRoleDO setRoleId(Integer roleId) { + this.roleId = roleId; + return this; + } + + public Date getCreateTime() { + return createTime; + } + + public AdminRoleDO setCreateTime(Date createTime) { + this.createTime = createTime; + return this; + } + +} \ No newline at end of file diff --git a/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/OAuth2AccessTokenDO.java b/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/OAuth2AccessTokenDO.java new file mode 100644 index 000000000..373fad188 --- /dev/null +++ b/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/OAuth2AccessTokenDO.java @@ -0,0 +1,86 @@ +package cn.iocoder.mall.admin.dataobject; + +import java.util.Date; + +public class OAuth2AccessTokenDO { + + /** + * 访问令牌 + */ + private String id; + /** + * 刷新令牌 + */ + private String refreshToken; + /** + * 管理员比那好 + */ + private Integer adminId; + /** + * 过期时间 + */ + private Date expiresTime; + /** + * 是否有效 + */ + private Boolean valid; + /** + * 创建时间 + */ + private Date createTime; + + public String getId() { + return id; + } + + public OAuth2AccessTokenDO setId(String id) { + this.id = id; + return this; + } + + public String getRefreshToken() { + return refreshToken; + } + + public OAuth2AccessTokenDO setRefreshToken(String refreshToken) { + this.refreshToken = refreshToken; + return this; + } + + public Integer getAdminId() { + return adminId; + } + + public OAuth2AccessTokenDO setAdminId(Integer adminId) { + this.adminId = adminId; + return this; + } + + public Date getExpiresTime() { + return expiresTime; + } + + public OAuth2AccessTokenDO setExpiresTime(Date expiresTime) { + this.expiresTime = expiresTime; + return this; + } + + public Boolean getValid() { + return valid; + } + + public OAuth2AccessTokenDO setValid(Boolean valid) { + this.valid = valid; + return this; + } + + public Date getCreateTime() { + return createTime; + } + + public OAuth2AccessTokenDO setCreateTime(Date createTime) { + this.createTime = createTime; + return this; + } + +} \ No newline at end of file diff --git a/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/OAuth2RefreshTokenDO.java b/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/OAuth2RefreshTokenDO.java new file mode 100644 index 000000000..271d60cb5 --- /dev/null +++ b/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/OAuth2RefreshTokenDO.java @@ -0,0 +1,78 @@ +package cn.iocoder.mall.admin.dataobject; + +import java.util.Date; + +/** + * 刷新令牌 + * + * idx_uid + */ +public class OAuth2RefreshTokenDO { + + /** + * 刷新令牌 + */ + private String id; + /** + * 用户编号 + */ + private Integer adminId; + /** + * 是否有效 + */ + private Boolean valid; + /** + * 过期时间 + */ + private Date expiresTime; + /** + * 创建时间 + */ + private Date createTime; + + public String getId() { + return id; + } + + public OAuth2RefreshTokenDO setId(String id) { + this.id = id; + return this; + } + + public Integer getAdminId() { + return adminId; + } + + public OAuth2RefreshTokenDO setAdminId(Integer adminId) { + this.adminId = adminId; + return this; + } + + public Boolean getValid() { + return valid; + } + + public OAuth2RefreshTokenDO setValid(Boolean valid) { + this.valid = valid; + return this; + } + + public Date getExpiresTime() { + return expiresTime; + } + + public OAuth2RefreshTokenDO setExpiresTime(Date expiresTime) { + this.expiresTime = expiresTime; + return this; + } + + public Date getCreateTime() { + return createTime; + } + + public OAuth2RefreshTokenDO setCreateTime(Date createTime) { + this.createTime = createTime; + return this; + } + +} diff --git a/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/ResourceDO.java b/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/ResourceDO.java new file mode 100644 index 000000000..b8b278263 --- /dev/null +++ b/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/ResourceDO.java @@ -0,0 +1,129 @@ +package cn.iocoder.mall.admin.dataobject; + +import java.util.Date; + +/** + * 资源实体 + */ +public class ResourceDO { + + /** + * 资源类型 - 菜单 + */ + public static final Integer TYPE_MENU = 1; + /** + * 资源类型 - 操作 + * + * 例如,按钮。 + */ + public static final Integer TYPE_OPERATION = 2; + + /** + * 资源编号 + */ + private Integer id; + /** + * 资源名字 + */ + private String name; + /** + * 资源类型 + */ + private Integer type; + /** + * 排序 + */ + private Integer sort; + /** + * 展示名 + */ + private String displayName; + /** + * 添加时间 + */ + private Date createTime; + /** + * 父级资源编号(外键:{@link ResourceDO#id}) + */ + private Integer pid; + /** + * 操作 + * + * 当资源类型为【菜单】时,handler 配置为界面 URL ,或者前端组件名 + * 当资源类型为【操作】时,handler 配置为后端 URL 。举个例子,如果有一个「创建管理员」的表单,那么前端界面上的按钮可以根据这个 url 判断是否展示,后端接收到该 url 的请求时会判断是否有权限。 + */ + private String handler; + + public Integer getId() { + return id; + } + + public ResourceDO setId(Integer id) { + this.id = id; + return this; + } + + public String getName() { + return name; + } + + public ResourceDO setName(String name) { + this.name = name; + return this; + } + + public Integer getType() { + return type; + } + + public ResourceDO setType(Integer type) { + this.type = type; + return this; + } + + public Integer getSort() { + return sort; + } + + public ResourceDO setSort(Integer sort) { + this.sort = sort; + return this; + } + + public String getDisplayName() { + return displayName; + } + + public ResourceDO setDisplayName(String displayName) { + this.displayName = displayName; + return this; + } + + public Date getCreateTime() { + return createTime; + } + + public ResourceDO setCreateTime(Date createTime) { + this.createTime = createTime; + return this; + } + + public Integer getPid() { + return pid; + } + + public ResourceDO setPid(Integer pid) { + this.pid = pid; + return this; + } + + public String getHandler() { + return handler; + } + + public ResourceDO setHandler(String handler) { + this.handler = handler; + return this; + } + +} diff --git a/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/RoleDO.java b/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/RoleDO.java new file mode 100644 index 000000000..e5e34b4c7 --- /dev/null +++ b/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/RoleDO.java @@ -0,0 +1,63 @@ +package cn.iocoder.mall.admin.dataobject; + +import java.util.Date; + +/** + * 角色实体 + */ +public class RoleDO { + + /** + * 账号状态 - 开启 + */ + public static final Integer STATUS_ENABLE = 1; + /** + * 账号状态 - 禁用 + */ + public static final Integer STATUS_DISABLE = 2; + + /** + * 角色编号 + */ + private Integer id; + /** + * 角色名 + */ + private String name; + /** + * 创建时间 + */ + private Date createTime; + /** + * 状态 + */ + private Integer status; + + public String getName() { + return name; + } + + public RoleDO setName(String name) { + this.name = name; + return this; + } + + public Date getCreateTime() { + return createTime; + } + + public RoleDO setCreateTime(Date createTime) { + this.createTime = createTime; + return this; + } + + public Integer getStatus() { + return status; + } + + public RoleDO setStatus(Integer status) { + this.status = status; + return this; + } + +} diff --git a/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/RoleResourceDO.java b/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/RoleResourceDO.java new file mode 100644 index 000000000..79ed702da --- /dev/null +++ b/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/RoleResourceDO.java @@ -0,0 +1,65 @@ +package cn.iocoder.mall.admin.dataobject; + +import java.util.Date; + +/** + * {@link RoleDO} 和 {@link ResourceDO} 的关联表 + */ +public class RoleResourceDO { + + /** + * 编号 + */ + private Integer id; + /** + * 角色编号(外键:{@link RoleDO} + */ + private Integer roleId; + /** + * 资源比那好(外键:{@link ResourceDO} + */ + private Integer resourceId; + /** + * 创建时间 + */ + private Date createTime; + + // TODO 芋艿 删除状态 + + public Integer getId() { + return id; + } + + public RoleResourceDO setId(Integer id) { + this.id = id; + return this; + } + + public Integer getRoleId() { + return roleId; + } + + public RoleResourceDO setRoleId(Integer roleId) { + this.roleId = roleId; + return this; + } + + public Date getCreateTime() { + return createTime; + } + + public RoleResourceDO setCreateTime(Date createTime) { + this.createTime = createTime; + return this; + } + + public Integer getResourceId() { + return resourceId; + } + + public RoleResourceDO setResourceId(Integer resourceId) { + this.resourceId = resourceId; + return this; + } + +} \ No newline at end of file diff --git a/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/package-info.java b/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/package-info.java new file mode 100644 index 000000000..35990e238 --- /dev/null +++ b/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/package-info.java @@ -0,0 +1 @@ +package cn.iocoder.mall.admin; \ No newline at end of file diff --git a/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/service/AdminServiceImpl.java b/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/service/AdminServiceImpl.java new file mode 100644 index 000000000..64033c463 --- /dev/null +++ b/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/service/AdminServiceImpl.java @@ -0,0 +1,50 @@ +package cn.iocoder.mall.admin.service; + +import cn.iocoder.common.framework.util.ServiceExceptionUtil; +import cn.iocoder.common.framework.vo.CommonResult; +import cn.iocoder.mall.admin.api.AdminService; +import cn.iocoder.mall.admin.api.constant.AdminErrorCodeEnum; +import cn.iocoder.mall.admin.dao.AdminMapper; +import cn.iocoder.mall.admin.dao.AdminRoleMapper; +import cn.iocoder.mall.admin.dataobject.AdminDO; +import cn.iocoder.mall.admin.dataobject.AdminRoleDO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.DigestUtils; + +import java.util.List; + +@Service +@com.alibaba.dubbo.config.annotation.Service +public class AdminServiceImpl implements AdminService { + + @Autowired + private AdminMapper adminMapper; + @Autowired + private AdminRoleMapper adminRoleMapper; + + public CommonResult validAdmin(String username, String password) { + AdminDO admin = adminMapper.selectByUsername(username); + // 账号不存在 + if (admin == null) { + return ServiceExceptionUtil.error(AdminErrorCodeEnum.ADMIN_USERNAME_NOT_REGISTERED.getCode()); + } + // 密码不正确 + if (DigestUtils.md5DigestAsHex(password.getBytes()).equals(admin.getPassword())) { + return ServiceExceptionUtil.error(AdminErrorCodeEnum.ADMIN_PASSWORD_ERROR.getCode()); + } + // 账号被禁用 + if (AdminDO.STATUS_DISABLE.equals(admin.getStatus())) { + return ServiceExceptionUtil.error(AdminErrorCodeEnum.ADMIN_IS_DISABLE.getCode()); + } + // 校验成功,返回管理员。并且,去掉一些非关键字段,考虑安全性。 + admin.setPassword(null); + admin.setStatus(null); + return CommonResult.success(admin); + } + + public List getAdminRoles(Integer adminId) { + return adminRoleMapper.selectByAdminId(adminId); + } + +} diff --git a/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/service/OAuth2ServiceImpl.java b/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/service/OAuth2ServiceImpl.java new file mode 100644 index 000000000..22e173568 --- /dev/null +++ b/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/service/OAuth2ServiceImpl.java @@ -0,0 +1,123 @@ +package cn.iocoder.mall.admin.service; + +import cn.iocoder.common.framework.util.ServiceExceptionUtil; +import cn.iocoder.common.framework.vo.CommonResult; +import cn.iocoder.mall.admin.api.OAuth2Service; +import cn.iocoder.mall.admin.api.bo.OAuth2AccessTokenBO; +import cn.iocoder.mall.admin.api.bo.OAuth2AuthenticationBO; +import cn.iocoder.mall.admin.api.constant.AdminErrorCodeEnum; +import cn.iocoder.mall.admin.convert.OAuth2Convert; +import cn.iocoder.mall.admin.dao.OAuth2AccessTokenMapper; +import cn.iocoder.mall.admin.dao.OAuth2RefreshTokenMapper; +import cn.iocoder.mall.admin.dataobject.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.util.*; + +@Service +@com.alibaba.dubbo.config.annotation.Service +public class OAuth2ServiceImpl implements OAuth2Service { + + /** + * 访问令牌过期时间,单位:毫秒 + */ + @Value("${modules.oauth2-code-service.access-token-expire-time-millis}") + private int accessTokenExpireTimeMillis; + /** + * 刷新令牌过期时间,单位:毫秒 + */ + @Value("${modules.oauth2-code-service.refresh-token-expire-time-millis}") + private int refreshTokenExpireTimeMillis; + + @Autowired + private AdminServiceImpl adminService; + @Autowired + private OAuth2AccessTokenMapper oauth2AccessTokenMapper; + @Autowired + private OAuth2RefreshTokenMapper oauth2RefreshTokenMapper; + @Autowired + private RoleServiceImpl roleService; + + @Override + public CommonResult getAccessToken(String username, String password) { + CommonResult adminResult = adminService.validAdmin(username, password); + // 校验失败,返回错误结果 + if (adminResult.isError()) { + return CommonResult.error(adminResult); + } + AdminDO admin = adminResult.getData(); + // 创建刷新令牌 + OAuth2RefreshTokenDO oauth2RefreshTokenDO = createOAuth2RefreshToken(admin.getId()); + // 创建访问令牌 + OAuth2AccessTokenDO oauth2AccessTokenDO = createOAuth2AccessToken(admin.getId(), oauth2RefreshTokenDO.getId()); + // 转换返回 + return CommonResult.success(OAuth2Convert.INSTANCE.convertToAccessTokenWithExpiresIn(oauth2AccessTokenDO)); + } + + @Override + public CommonResult checkToken(String accessToken) { + OAuth2AccessTokenDO accessTokenDO = oauth2AccessTokenMapper.selectByTokenId(accessToken); + if (accessTokenDO == null) { // 不存在 + return ServiceExceptionUtil.error(AdminErrorCodeEnum.OAUTH_INVALID_TOKEN_NOT_FOUND.getCode()); + } + if (accessTokenDO.getExpiresTime().getTime() < System.currentTimeMillis()) { // 已过期 + return ServiceExceptionUtil.error(AdminErrorCodeEnum.OAUTH_INVALID_TOKEN_EXPIRED.getCode()); + } + if (!accessTokenDO.getValid()) { // 无效 + return ServiceExceptionUtil.error(AdminErrorCodeEnum.OAUTH_INVALID_TOKEN_INVALID.getCode()); + } + // 获得管理员拥有的角色 + List adminRoleDOs = adminService.getAdminRoles(accessTokenDO.getAdminId()); + return CommonResult.success(OAuth2Convert.INSTANCE.convertToAuthentication(accessTokenDO, adminRoleDOs)); + } + + @Override + public CommonResult checkPermission(Integer adminId, Set roleIds, String url) { + // 避免传入的是空集合 + if (roleIds == null) { + roleIds = Collections.emptySet(); + } + // 校验权限 + List roleResourceDOs = roleService.getRoleByResourceHandler(url); + if (roleResourceDOs.isEmpty()) { // 任何角色,都可以访问 + return CommonResult.success(true); + } + for (RoleResourceDO roleResourceDO : roleResourceDOs) { + if (roleIds.contains(roleResourceDO.getId())) { + return CommonResult.success(true); + } + } + // 没有权限,返回错误 + return ServiceExceptionUtil.error(AdminErrorCodeEnum.OAUTH_INVALID_PERMISSION.getCode()); + } + + private OAuth2AccessTokenDO createOAuth2AccessToken(Integer adminId, String refreshToken) { + OAuth2AccessTokenDO accessToken = new OAuth2AccessTokenDO().setId(generateAccessToken()) + .setRefreshToken(refreshToken) + .setAdminId(adminId) + .setExpiresTime(new Date(System.currentTimeMillis() + accessTokenExpireTimeMillis)) + .setValid(true); + oauth2AccessTokenMapper.insert(accessToken); + return accessToken; + } + + private OAuth2RefreshTokenDO createOAuth2RefreshToken(Integer adminId) { + OAuth2RefreshTokenDO refreshToken = new OAuth2RefreshTokenDO().setId(generateRefreshToken()) + .setAdminId(adminId) + .setExpiresTime(new Date(System.currentTimeMillis() + refreshTokenExpireTimeMillis)) + .setValid(true); + oauth2RefreshTokenMapper.insert(refreshToken); + return refreshToken; + } + + private String generateAccessToken() { + return UUID.randomUUID().toString().replaceAll("-", ""); + } + + private String generateRefreshToken() { + return UUID.randomUUID().toString().replaceAll("-", ""); + } + +} \ No newline at end of file diff --git a/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/service/RoleServiceImpl.java b/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/service/RoleServiceImpl.java new file mode 100644 index 000000000..22aace4d9 --- /dev/null +++ b/admin/admin-service-impl/src/main/java/cn/iocoder/mall/admin/service/RoleServiceImpl.java @@ -0,0 +1,22 @@ +package cn.iocoder.mall.admin.service; + +import cn.iocoder.mall.admin.api.RoleService; +import cn.iocoder.mall.admin.dao.RoleResourceMapper; +import cn.iocoder.mall.admin.dataobject.RoleResourceDO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@com.alibaba.dubbo.config.annotation.Service +public class RoleServiceImpl implements RoleService { + + @Autowired + private RoleResourceMapper roleResourceMapper; + + public List getRoleByResourceHandler(String resourceHandler) { + return roleResourceMapper.selectByResourceHandler(resourceHandler); + } + +} \ No newline at end of file diff --git a/admin/admin-service-impl/src/main/resources/config/application.properties b/admin/admin-service-impl/src/main/resources/config/application.properties new file mode 100644 index 000000000..2196c16be --- /dev/null +++ b/admin/admin-service-impl/src/main/resources/config/application.properties @@ -0,0 +1,4 @@ +##################### 业务模块 ##################### +## OAuth2CodeService +modules.oauth2-code-service.access-token-expire-time-millis = 2880000 +modules.oauth2-code-service.refresh-token-expire-time-millis = 43200000 \ No newline at end of file diff --git a/admin/admin-service-impl/src/main/resources/config/application.yaml b/admin/admin-service-impl/src/main/resources/config/application.yaml new file mode 100644 index 000000000..d5fa428f0 --- /dev/null +++ b/admin/admin-service-impl/src/main/resources/config/application.yaml @@ -0,0 +1,32 @@ +spring: + # datasource + datasource: + url: jdbc:mysql://127.0.0.1:33061/mall_admin?useSSL=false + driver-class-name: com.mysql.jdbc.Driver + username: root + password: 123456 + +# server +server: + port: 8083 + +# mybatis +mybatis: + config-location: classpath:mybatis-config.xml + mapper-locations: classpath:mapper/*.xml + type-aliases-package: cn.iocoder.mall.admin.dataobject + +# dubbo +dubbo: + application: + name: admin-service + registry: + address: zookeeper://127.0.0.1:2181 + protocol: + port: -1 + name: dubbo + scan: + base-packages: cn.iocoder.mall.admin.service +demo: + service: + version: 1.0.0 \ No newline at end of file diff --git a/admin/admin-service-impl/src/main/resources/mapper/AdminMapper.xml b/admin/admin-service-impl/src/main/resources/mapper/AdminMapper.xml new file mode 100644 index 000000000..1855f9514 --- /dev/null +++ b/admin/admin-service-impl/src/main/resources/mapper/AdminMapper.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/admin/admin-service-impl/src/main/resources/mapper/AdminRoleMapper.xml b/admin/admin-service-impl/src/main/resources/mapper/AdminRoleMapper.xml new file mode 100644 index 000000000..63a517de0 --- /dev/null +++ b/admin/admin-service-impl/src/main/resources/mapper/AdminRoleMapper.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/admin/admin-service-impl/src/main/resources/mapper/OAuth2AccessTokenMapper.xml b/admin/admin-service-impl/src/main/resources/mapper/OAuth2AccessTokenMapper.xml new file mode 100644 index 000000000..47c1cf10c --- /dev/null +++ b/admin/admin-service-impl/src/main/resources/mapper/OAuth2AccessTokenMapper.xml @@ -0,0 +1,22 @@ + + + + + + INSERT INTO oauth2_access_token ( + id, refresh_token, admin_id, valid, expires_time, + create_time + ) VALUES ( + #{id}, #{refreshToken}, #{adminId}, #{valid}, #{expiresTime}, + #{createTime} + ) + + + + + \ No newline at end of file diff --git a/admin/admin-service-impl/src/main/resources/mapper/OAuth2RefreshTokenMapper.xml b/admin/admin-service-impl/src/main/resources/mapper/OAuth2RefreshTokenMapper.xml new file mode 100644 index 000000000..04cefd1b7 --- /dev/null +++ b/admin/admin-service-impl/src/main/resources/mapper/OAuth2RefreshTokenMapper.xml @@ -0,0 +1,13 @@ + + + + + + INSERT INTO oauth2_refresh_token ( + id, admin_id, valid, expires_time, create_time + ) VALUES ( + #{id}, #{adminId}, #{valid}, #{expiresTime}, #{createTime} + ) + + + \ No newline at end of file diff --git a/admin/admin-service-impl/src/main/resources/mapper/RoleResourceMapper.xml b/admin/admin-service-impl/src/main/resources/mapper/RoleResourceMapper.xml new file mode 100644 index 000000000..d3d57df15 --- /dev/null +++ b/admin/admin-service-impl/src/main/resources/mapper/RoleResourceMapper.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/admin/admin-service-impl/src/main/resources/mybatis-config.xml b/admin/admin-service-impl/src/main/resources/mybatis-config.xml new file mode 100644 index 000000000..7f604cc7e --- /dev/null +++ b/admin/admin-service-impl/src/main/resources/mybatis-config.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/admin/pom.xml b/admin/pom.xml new file mode 100644 index 000000000..ea03dd1cf --- /dev/null +++ b/admin/pom.xml @@ -0,0 +1,22 @@ + + + + mall-parent + cn.iocoder.mall + 1.0-SNAPSHOT + + 4.0.0 + + admin + pom + + admin-application + admin-sdk + admin-service-api + admin-service-impl + + + + \ No newline at end of file diff --git a/common/common-framework/src/main/java/cn/iocoder/common/framework/util/HttpUtil.java b/common/common-framework/src/main/java/cn/iocoder/common/framework/util/HttpUtil.java new file mode 100644 index 000000000..4cd649b6e --- /dev/null +++ b/common/common-framework/src/main/java/cn/iocoder/common/framework/util/HttpUtil.java @@ -0,0 +1,21 @@ +package cn.iocoder.common.framework.util; + +import org.springframework.util.StringUtils; + +import javax.servlet.http.HttpServletRequest; + +public class HttpUtil { + + public static String obtainAccess(HttpServletRequest request) { + String authorization = request.getHeader("Authorization"); + if (!StringUtils.hasText(authorization)) { + return null; + } + int index = authorization.indexOf("Bearer "); + if (index == -1) { // 未找到 + return null; + } + return authorization.substring(index + 7).trim(); + } + +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index a76b6db58..2b71f2742 100644 --- a/pom.xml +++ b/pom.xml @@ -18,6 +18,7 @@ order user common + admin pom diff --git a/user/user-application/src/main/java/cn/iocoder/mall/user/config/MVCConfiguration.java b/user/user-application/src/main/java/cn/iocoder/mall/user/config/MVCConfiguration.java index 2994a78e0..d3e420ffe 100644 --- a/user/user-application/src/main/java/cn/iocoder/mall/user/config/MVCConfiguration.java +++ b/user/user-application/src/main/java/cn/iocoder/mall/user/config/MVCConfiguration.java @@ -1,7 +1,7 @@ package cn.iocoder.mall.user.config; import cn.iocoder.common.framework.config.GlobalExceptionHandler; -import cn.iocoder.mall.user.sdk.interceptor.SecurityInterceptor; +import cn.iocoder.mall.user.sdk.interceptor.UserSecurityInterceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -13,11 +13,11 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @EnableWebMvc @Configuration @Import(value = {GlobalExceptionHandler.class, // 统一全局返回 - SecurityInterceptor.class}) // 安全拦截器,实现认证和授权功能。 + UserSecurityInterceptor.class}) // 安全拦截器,实现认证和授权功能。 public class MVCConfiguration implements WebMvcConfigurer { @Autowired - private SecurityInterceptor securityInterceptor; + private UserSecurityInterceptor securityInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { diff --git a/user/user-application/src/main/java/cn/iocoder/mall/user/controller/PassportController.java b/user/user-application/src/main/java/cn/iocoder/mall/user/controller/PassportController.java index d68898914..71c9112b4 100644 --- a/user/user-application/src/main/java/cn/iocoder/mall/user/controller/PassportController.java +++ b/user/user-application/src/main/java/cn/iocoder/mall/user/controller/PassportController.java @@ -46,7 +46,7 @@ public class PassportController { }) public CommonResult mobileRegister(@RequestParam("mobile") String mobile, @RequestParam("code") String code) { - CommonResult result = oauth2Service.getAccessToken2(mobile, code); + CommonResult result = oauth2Service.getAccessToken(mobile, code); return PassportConvert.INSTANCE.convert(result); } diff --git a/user/user-application/src/main/java/cn/iocoder/mall/user/controller/UserController.java b/user/user-application/src/main/java/cn/iocoder/mall/user/controller/UserController.java index d84c797a6..e12d96bbd 100644 --- a/user/user-application/src/main/java/cn/iocoder/mall/user/controller/UserController.java +++ b/user/user-application/src/main/java/cn/iocoder/mall/user/controller/UserController.java @@ -1,7 +1,7 @@ package cn.iocoder.mall.user.controller; import cn.iocoder.common.framework.vo.CommonResult; -import cn.iocoder.mall.user.sdk.context.SecurityContextHolder; +import cn.iocoder.mall.user.sdk.context.UserSecurityContextHolder; import cn.iocoder.mall.user.vo.UserInfoVO; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @@ -18,7 +18,7 @@ public class UserController { @ApiOperation(value = "用户信息") public CommonResult info() { // TODO 芋艿,正在实现中 - UserInfoVO user = new UserInfoVO().setId(SecurityContextHolder.getContext().getUid()); + UserInfoVO user = new UserInfoVO().setId(UserSecurityContextHolder.getContext().getUid()); return CommonResult.success(user); } diff --git a/user/user-sdk/src/main/java/cn/iocoder/mall/user/sdk/context/SecurityContextHolder.java b/user/user-sdk/src/main/java/cn/iocoder/mall/user/sdk/context/SecurityContextHolder.java deleted file mode 100644 index a618c3591..000000000 --- a/user/user-sdk/src/main/java/cn/iocoder/mall/user/sdk/context/SecurityContextHolder.java +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.mall.user.sdk.context; - -/** - * {@link SecurityContext} Holder - * - * 参考 spring security 的 ThreadLocalSecurityContextHolderStrategy 类,简单实现。 - */ -public class SecurityContextHolder { - - private static final ThreadLocal securityContext = new ThreadLocal(); - - public static void setContext(SecurityContext context) { - securityContext.set(context); - } - - public static SecurityContext getContext() { - SecurityContext ctx = securityContext.get(); - // 为空时,设置一个空的进去 - if (ctx == null) { - ctx = new SecurityContext(null); - securityContext.set(ctx); - } - return ctx; - } - - public static void clear() { - securityContext.remove(); - } - -} \ No newline at end of file diff --git a/user/user-sdk/src/main/java/cn/iocoder/mall/user/sdk/context/SecurityContext.java b/user/user-sdk/src/main/java/cn/iocoder/mall/user/sdk/context/UserSecurityContext.java similarity index 61% rename from user/user-sdk/src/main/java/cn/iocoder/mall/user/sdk/context/SecurityContext.java rename to user/user-sdk/src/main/java/cn/iocoder/mall/user/sdk/context/UserSecurityContext.java index 12f93bc69..cfadba524 100644 --- a/user/user-sdk/src/main/java/cn/iocoder/mall/user/sdk/context/SecurityContext.java +++ b/user/user-sdk/src/main/java/cn/iocoder/mall/user/sdk/context/UserSecurityContext.java @@ -1,13 +1,13 @@ package cn.iocoder.mall.user.sdk.context; /** - * Security 上下文 + * User Security 上下文 */ -public class SecurityContext { +public class UserSecurityContext { private final Long uid; - public SecurityContext(Long uid) { + public UserSecurityContext(Long uid) { this.uid = uid; } diff --git a/user/user-sdk/src/main/java/cn/iocoder/mall/user/sdk/context/UserSecurityContextHolder.java b/user/user-sdk/src/main/java/cn/iocoder/mall/user/sdk/context/UserSecurityContextHolder.java new file mode 100644 index 000000000..cb51cabeb --- /dev/null +++ b/user/user-sdk/src/main/java/cn/iocoder/mall/user/sdk/context/UserSecurityContextHolder.java @@ -0,0 +1,30 @@ +package cn.iocoder.mall.user.sdk.context; + +/** + * {@link UserSecurityContext} Holder + * + * 参考 spring security 的 ThreadLocalSecurityContextHolderStrategy 类,简单实现。 + */ +public class UserSecurityContextHolder { + + private static final ThreadLocal securityContext = new ThreadLocal(); + + public static void setContext(UserSecurityContext context) { + securityContext.set(context); + } + + public static UserSecurityContext getContext() { + UserSecurityContext ctx = securityContext.get(); + // 为空时,设置一个空的进去 + if (ctx == null) { + ctx = new UserSecurityContext(null); + securityContext.set(ctx); + } + return ctx; + } + + public static void clear() { + securityContext.remove(); + } + +} \ No newline at end of file diff --git a/user/user-sdk/src/main/java/cn/iocoder/mall/user/sdk/interceptor/SecurityInterceptor.java b/user/user-sdk/src/main/java/cn/iocoder/mall/user/sdk/interceptor/UserSecurityInterceptor.java similarity index 69% rename from user/user-sdk/src/main/java/cn/iocoder/mall/user/sdk/interceptor/SecurityInterceptor.java rename to user/user-sdk/src/main/java/cn/iocoder/mall/user/sdk/interceptor/UserSecurityInterceptor.java index 511f18c5f..cb7a8d165 100644 --- a/user/user-sdk/src/main/java/cn/iocoder/mall/user/sdk/interceptor/SecurityInterceptor.java +++ b/user/user-sdk/src/main/java/cn/iocoder/mall/user/sdk/interceptor/UserSecurityInterceptor.java @@ -1,15 +1,15 @@ package cn.iocoder.mall.user.sdk.interceptor; import cn.iocoder.common.framework.exception.ServiceException; +import cn.iocoder.common.framework.util.HttpUtil; import cn.iocoder.common.framework.vo.CommonResult; import cn.iocoder.mall.user.sdk.annotation.PermitAll; -import cn.iocoder.mall.user.sdk.context.SecurityContext; -import cn.iocoder.mall.user.sdk.context.SecurityContextHolder; +import cn.iocoder.mall.user.sdk.context.UserSecurityContext; +import cn.iocoder.mall.user.sdk.context.UserSecurityContextHolder; import cn.iocoder.mall.user.service.api.OAuth2Service; import cn.iocoder.mall.user.service.api.bo.OAuth2AuthenticationBO; import com.alibaba.dubbo.config.annotation.Reference; import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; @@ -21,7 +21,7 @@ import javax.servlet.http.HttpServletResponse; * 安全拦截器 */ @Component -public class SecurityInterceptor extends HandlerInterceptorAdapter { +public class UserSecurityInterceptor extends HandlerInterceptorAdapter { @Reference private OAuth2Service oauth2Service; @@ -29,7 +29,7 @@ public class SecurityInterceptor extends HandlerInterceptorAdapter { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 校验访问令牌是否正确。若正确,返回授权信息 - String accessToken = obtainAccess(request); + String accessToken = HttpUtil.obtainAccess(request); OAuth2AuthenticationBO authentication = null; if (accessToken != null) { CommonResult result = oauth2Service.checkToken(accessToken); @@ -38,8 +38,8 @@ public class SecurityInterceptor extends HandlerInterceptorAdapter { } authentication = result.getData(); // 添加到 SecurityContext - SecurityContext context = new SecurityContext(authentication.getUid()); - SecurityContextHolder.setContext(context); + UserSecurityContext context = new UserSecurityContext(authentication.getUid()); + UserSecurityContextHolder.setContext(context); } // 校验是否需要已授权 HandlerMethod method = (HandlerMethod) handler; @@ -53,19 +53,7 @@ public class SecurityInterceptor extends HandlerInterceptorAdapter { @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { // 清空 SecurityContext - SecurityContextHolder.clear(); - } - - private String obtainAccess(HttpServletRequest request) { - String authorization = request.getHeader("Authorization"); - if (!StringUtils.hasText(authorization)) { - return null; - } - int index = authorization.indexOf("Bearer "); - if (index == -1) { // 未找到 - return null; - } - return authorization.substring(index + 7).trim(); + UserSecurityContextHolder.clear(); } } \ No newline at end of file diff --git a/user/user-sdk/src/main/java/cn/iocoder/mall/user/sdk/package-info.java b/user/user-sdk/src/main/java/cn/iocoder/mall/user/sdk/package-info.java index 656d91a8c..36a1a7694 100644 --- a/user/user-sdk/src/main/java/cn/iocoder/mall/user/sdk/package-info.java +++ b/user/user-sdk/src/main/java/cn/iocoder/mall/user/sdk/package-info.java @@ -1,6 +1,6 @@ /** * 提供 SDK 给其它服务,使用如下功能: * - * 1. 通过 {@link } 拦截器, + * 1. 通过 {@link cn.iocoder.mall.user.sdk.interceptor.UserSecurityInterceptor} 拦截器,实现需要登陆 URL 的鉴权 */ package cn.iocoder.mall.user.sdk; \ No newline at end of file diff --git a/user/user-service-api/src/main/java/cn/iocoder/mall/user/service/api/OAuth2Service.java b/user/user-service-api/src/main/java/cn/iocoder/mall/user/service/api/OAuth2Service.java index 1ac861335..5bc3224c7 100644 --- a/user/user-service-api/src/main/java/cn/iocoder/mall/user/service/api/OAuth2Service.java +++ b/user/user-service-api/src/main/java/cn/iocoder/mall/user/service/api/OAuth2Service.java @@ -1,27 +1,13 @@ package cn.iocoder.mall.user.service.api; -import cn.iocoder.common.framework.exception.ServiceException; import cn.iocoder.common.framework.vo.CommonResult; import cn.iocoder.mall.user.service.api.bo.OAuth2AccessTokenBO; import cn.iocoder.mall.user.service.api.bo.OAuth2AuthenticationBO; public interface OAuth2Service { - /** - * 使用手机号 + 验证码,获取访问令牌等信息 - * - * 如果手机未注册,并且验证码正确,进行自动注册。 - * - * @param mobile 手机号 - * @param code 验证码 - * @return 授权信息 - */ - @Deprecated - OAuth2AccessTokenBO getAccessToken(String mobile, String code) - throws ServiceException; - - CommonResult getAccessToken2(String mobile, String code); + CommonResult getAccessToken(String mobile, String code); /** * 校验访问令牌,获取身份信息( 不包括 accessToken 等等 ) diff --git a/user/user-application/src/main/java/cn/iocoder/mall/user/config/ServiceExceptionConfiguration.java b/user/user-service-impl/src/main/java/cn/iocoder/mall/user/config/ServiceExceptionConfiguration.java similarity index 100% rename from user/user-application/src/main/java/cn/iocoder/mall/user/config/ServiceExceptionConfiguration.java rename to user/user-service-impl/src/main/java/cn/iocoder/mall/user/config/ServiceExceptionConfiguration.java diff --git a/user/user-service-impl/src/main/java/cn/iocoder/mall/user/service/MobileCodeServiceImpl.java b/user/user-service-impl/src/main/java/cn/iocoder/mall/user/service/MobileCodeServiceImpl.java index 0615f4093..5df55576a 100644 --- a/user/user-service-impl/src/main/java/cn/iocoder/mall/user/service/MobileCodeServiceImpl.java +++ b/user/user-service-impl/src/main/java/cn/iocoder/mall/user/service/MobileCodeServiceImpl.java @@ -47,31 +47,7 @@ public class MobileCodeServiceImpl implements MobileCodeService { * @param code 验证码 * @return 手机验证码信息 */ - public MobileCodeDO validLastMobileCode(String mobile, String code) { - MobileCodeDO mobileCodePO = mobileCodeMapper.selectLast1ByMobile(mobile); - if (mobileCodePO == null) { // 若验证码不存在,抛出异常 - throw ServiceExceptionUtil.exception(UserErrorCodeEnum.MOBILE_CODE_NOT_FOUND.getCode()); - } - if (System.currentTimeMillis() - mobileCodePO.getCreateTime().getTime() >= codeExpireTimes) { // 验证码已过期 - throw ServiceExceptionUtil.exception(UserErrorCodeEnum.MOBILE_CODE_EXPIRED.getCode()); - } - if (mobileCodePO.getUsed()) { // 验证码已使用 - throw ServiceExceptionUtil.exception(UserErrorCodeEnum.MOBILE_CODE_USED.getCode()); - } - if (!mobileCodePO.getCode().equals(code)) { - throw ServiceExceptionUtil.exception(UserErrorCodeEnum.MOBILE_CODE_NOT_CORRECT.getCode()); - } - return mobileCodePO; - } - - /** - * 校验手机号的最后一个手机验证码是否有效 - * - * @param mobile 手机号 - * @param code 验证码 - * @return 手机验证码信息 - */ - public CommonResult validLastMobileCode2(String mobile, String code) { + public CommonResult validLastMobileCode(String mobile, String code) { MobileCodeDO mobileCodePO = mobileCodeMapper.selectLast1ByMobile(mobile); if (mobileCodePO == null) { // 若验证码不存在,抛出异常 return ServiceExceptionUtil.error(UserErrorCodeEnum.MOBILE_CODE_NOT_FOUND.getCode()); diff --git a/user/user-service-impl/src/main/java/cn/iocoder/mall/user/service/OAuth2ServiceImpl.java b/user/user-service-impl/src/main/java/cn/iocoder/mall/user/service/OAuth2ServiceImpl.java index c06960b2b..f17b54866 100644 --- a/user/user-service-impl/src/main/java/cn/iocoder/mall/user/service/OAuth2ServiceImpl.java +++ b/user/user-service-impl/src/main/java/cn/iocoder/mall/user/service/OAuth2ServiceImpl.java @@ -52,29 +52,9 @@ public class OAuth2ServiceImpl implements OAuth2Service { @Override @Transactional - public OAuth2AccessTokenBO getAccessToken(String mobile, String code) { - // 校验手机号的最后一个手机验证码是否有效 - MobileCodeDO mobileCodeDO = mobileCodeService.validLastMobileCode(mobile, code); - // 获取用户 - UserDO userDO = userService.getUser(mobile); - if (userDO == null) { // 用户不存在 - throw ServiceExceptionUtil.exception(UserErrorCodeEnum.USER_MOBILE_NOT_REGISTERED.getCode()); - } - // 创建刷新令牌 - OAuth2RefreshTokenDO oauth2RefreshTokenDO = createOAuth2RefreshToken(userDO.getId()); - // 创建访问令牌 - OAuth2AccessTokenDO oauth2AccessTokenDO = createOAuth2AccessToken(userDO.getId(), oauth2RefreshTokenDO.getId()); - // 标记已使用 - mobileCodeService.useMobileCode(mobileCodeDO.getId(), userDO.getId()); - // 转换返回 - return OAuth2Convert.INSTANCE.convertToAccessTokenWithExpiresIn(oauth2AccessTokenDO); - } - - @Override - @Transactional - public CommonResult getAccessToken2(String mobile, String code) { + public CommonResult getAccessToken(String mobile, String code) { // 校验传入的 mobile 和 code 是否合法 - CommonResult result = mobileCodeService.validLastMobileCode2(mobile, code); + CommonResult result = mobileCodeService.validLastMobileCode(mobile, code); if (result.isError()) { return CommonResult.error(result); } diff --git a/user/user-service-impl/src/main/resources/mapper/OAuth2AccessTokenMapper.xml b/user/user-service-impl/src/main/resources/mapper/OAuth2AccessTokenMapper.xml index 763b77790..ee3b4dcaa 100644 --- a/user/user-service-impl/src/main/resources/mapper/OAuth2AccessTokenMapper.xml +++ b/user/user-service-impl/src/main/resources/mapper/OAuth2AccessTokenMapper.xml @@ -4,17 +4,17 @@ INSERT INTO oauth2_access_token ( - id, refresh_token, uid, valid, expires_time, + id, refresh_token, adminId, valid, expires_time, create_time ) VALUES ( - #{id}, #{refreshToken}, #{uid}, #{valid}, #{expiresTime}, + #{id}, #{refreshToken}, #{adminId}, #{valid}, #{expiresTime}, #{createTime} ) diff --git a/user/user-service-impl/src/main/resources/mapper/OAuth2RefreshTokenMapper.xml b/user/user-service-impl/src/main/resources/mapper/OAuth2RefreshTokenMapper.xml index 3d9277070..b4646aee4 100644 --- a/user/user-service-impl/src/main/resources/mapper/OAuth2RefreshTokenMapper.xml +++ b/user/user-service-impl/src/main/resources/mapper/OAuth2RefreshTokenMapper.xml @@ -4,9 +4,9 @@ INSERT INTO oauth2_refresh_token ( - id, uid, valid, expires_time, create_time + id, adminId, valid, expires_time, create_time ) VALUES ( - #{id}, #{uid}, #{valid}, #{expiresTime}, #{createTime} + #{id}, #{adminId}, #{valid}, #{expiresTime}, #{createTime} )