diff --git a/admin-web/src/models/login.js b/admin-web/src/models/login.js index 60b5ef1d3..24507fa9a 100644 --- a/admin-web/src/models/login.js +++ b/admin-web/src/models/login.js @@ -20,12 +20,14 @@ export default { type: 'changeLoginStatus', payload: response, }); + yield put(routerRedux.replace('/')); // Login successfully if (response.code === 0) { + // 保存 token 到 localStorage,发送请求的时候,会自动取 token 放到 header - setLoginToken(response.data.accessToken, response.data.refreshToken); + setLoginToken(response.data.token.accessToken, response.data.token.refreshToken); // 此处直接设置为 admin、和 user 角色,因为暂时不做服务控制前段 角色 setAuthority(['admin', 'user']); diff --git a/common/common-framework/src/main/java/cn/iocoder/common/framework/constant/CommonStatusEnum.java b/common/common-framework/src/main/java/cn/iocoder/common/framework/constant/CommonStatusEnum.java index 2dde2e185..633a25c98 100644 --- a/common/common-framework/src/main/java/cn/iocoder/common/framework/constant/CommonStatusEnum.java +++ b/common/common-framework/src/main/java/cn/iocoder/common/framework/constant/CommonStatusEnum.java @@ -46,6 +46,7 @@ public enum CommonStatusEnum implements IntArrayValuable { return this; } + @Deprecated public static boolean isValid(Integer status) { if (status == null) { return false; diff --git a/common/common-framework/src/main/java/cn/iocoder/common/framework/constant/MallConstants.java b/common/common-framework/src/main/java/cn/iocoder/common/framework/constant/MallConstants.java index 1ca49c508..9994bff06 100644 --- a/common/common-framework/src/main/java/cn/iocoder/common/framework/constant/MallConstants.java +++ b/common/common-framework/src/main/java/cn/iocoder/common/framework/constant/MallConstants.java @@ -19,10 +19,12 @@ public interface MallConstants { /** * 用户类型 - 用户 */ + @Deprecated Integer USER_TYPE_USER = 1; /** * 用户类型 - 管理员 */ + @Deprecated Integer USER_TYPE_ADMIN = 2; // HTTP Request Attr diff --git a/common/common-framework/src/main/java/cn/iocoder/common/framework/constant/UserTypeEnum.java b/common/common-framework/src/main/java/cn/iocoder/common/framework/constant/UserTypeEnum.java new file mode 100644 index 000000000..f763373bd --- /dev/null +++ b/common/common-framework/src/main/java/cn/iocoder/common/framework/constant/UserTypeEnum.java @@ -0,0 +1,54 @@ +package cn.iocoder.common.framework.constant; + +import cn.iocoder.common.framework.core.IntArrayValuable; + +import java.util.Arrays; + +/** + * 全局用户类型枚举 + */ +public enum UserTypeEnum implements IntArrayValuable { + + USER(1, "用户"), + ADMIN(2, "管理员"); + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(UserTypeEnum::getValue).toArray(); + + /** + * 类型 + */ + private Integer value; + /** + * 类型名 + */ + private String name; + + UserTypeEnum(Integer value, String name) { + this.value = value; + this.name = name; + } + + public Integer getValue() { + return value; + } + + public UserTypeEnum setValue(Integer value) { + this.value = value; + return this; + } + + public String getName() { + return name; + } + + public UserTypeEnum setName(String name) { + this.name = name; + return this; + } + + @Override + public int[] array() { + return ARRAYS; + } + +} diff --git a/common/common-framework/src/main/java/cn/iocoder/common/framework/mybatis/QueryWrapperX.java b/common/common-framework/src/main/java/cn/iocoder/common/framework/mybatis/QueryWrapperX.java index a1de9eeee..a82f29dd3 100644 --- a/common/common-framework/src/main/java/cn/iocoder/common/framework/mybatis/QueryWrapperX.java +++ b/common/common-framework/src/main/java/cn/iocoder/common/framework/mybatis/QueryWrapperX.java @@ -19,4 +19,11 @@ public class QueryWrapperX extends QueryWrapper { return this; } + public QueryWrapperX eqIfPresent(String column, Object val) { + if (val != null) { + return (QueryWrapperX) super.eq(column, val); + } + return this; + } + } diff --git a/common/common-framework/src/main/java/cn/iocoder/common/framework/util/CollectionUtil.java b/common/common-framework/src/main/java/cn/iocoder/common/framework/util/CollectionUtil.java index 81cdbcb3d..ee8251258 100644 --- a/common/common-framework/src/main/java/cn/iocoder/common/framework/util/CollectionUtil.java +++ b/common/common-framework/src/main/java/cn/iocoder/common/framework/util/CollectionUtil.java @@ -1,5 +1,7 @@ package cn.iocoder.common.framework.util; +import org.springframework.util.CollectionUtils; + import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; @@ -30,4 +32,8 @@ public class CollectionUtil { return from.stream().collect(Collectors.toMap(keyFunc, item -> item)); } + public static boolean containsAny(Collection source, Collection candidates) { + return CollectionUtils.containsAny(source, candidates); + } + } diff --git a/system/system-application/src/main/java/cn/iocoder/mall/admin/application/SystemApplication.java b/system/system-application/src/main/java/cn/iocoder/mall/admin/application/SystemApplication.java index d4b1b995f..9792b390d 100644 --- a/system/system-application/src/main/java/cn/iocoder/mall/admin/application/SystemApplication.java +++ b/system/system-application/src/main/java/cn/iocoder/mall/admin/application/SystemApplication.java @@ -2,7 +2,6 @@ package cn.iocoder.mall.admin.application; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.ConfigurableApplicationContext; import org.springframework.scheduling.annotation.EnableAsync; @SpringBootApplication(scanBasePackages = {"cn.iocoder.mall.admin"}) @@ -10,14 +9,7 @@ import org.springframework.scheduling.annotation.EnableAsync; public class SystemApplication { public static void main(String[] args) { - ConfigurableApplicationContext ctx = SpringApplication.run(SystemApplication.class, args); -// Object bean = ctx.getBean("test"); -// System.out.println(AopUtils.getTargetClass(bean)); - -// System.out.println(bean); - -// ConfigurableApplicationContext ctx = -// System.out.println(); // TODO 后面去掉,这里是临时的 + SpringApplication.run(SystemApplication.class, args); } } diff --git a/system/system-application/src/main/java/cn/iocoder/mall/admin/application/controller/admins/AdminController.java b/system/system-application/src/main/java/cn/iocoder/mall/admin/application/controller/admins/AdminController.java index ff043b462..8b874d5ac 100644 --- a/system/system-application/src/main/java/cn/iocoder/mall/admin/application/controller/admins/AdminController.java +++ b/system/system-application/src/main/java/cn/iocoder/mall/admin/application/controller/admins/AdminController.java @@ -17,6 +17,7 @@ import cn.iocoder.mall.admin.application.convert.ResourceConvert; import cn.iocoder.mall.admin.application.vo.admin.AdminMenuTreeNodeVO; import cn.iocoder.mall.admin.application.vo.admin.AdminRoleVO; import cn.iocoder.mall.admin.application.vo.admin.AdminVO; +import cn.iocoder.mall.admin.sdk.annotation.RequiresPermissions; import cn.iocoder.mall.admin.sdk.context.AdminSecurityContextHolder; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; @@ -86,6 +87,7 @@ public class AdminController { // =========== 管理员管理 API =========== @GetMapping("/page") + @RequiresPermissions("system.admin.page") @ApiOperation(value = "管理员分页") public CommonResult> page(AdminPageDTO adminPageDTO) { PageResult page = adminService.getAdminPage(adminPageDTO); @@ -128,9 +130,10 @@ public class AdminController { @ApiOperation(value = "指定管理员拥有的角色列表") @ApiImplicitParam(name = "id", value = "管理员编号", required = true, example = "1") public CommonResult> roleList(@RequestParam("id") Integer id) { - // 获得所有角色数组 - List allRoleList = adminService.getRoleList(id); - Set adminRoleIdSet = CollectionUtil.convertSet(allRoleList, RoleBO::getId); + // 获得所有角色列表 + List allRoleList = roleService.getRoleList(); + // 获得管理员的角色数组 + Set adminRoleIdSet = CollectionUtil.convertSet(adminService.getRoleList(id), RoleBO::getId); // 转换出返回结果 List result = AdminConvert.INSTANCE.convert(allRoleList); // 设置每个角色是否赋予给改管理员 diff --git a/system/system-application/src/main/java/cn/iocoder/mall/admin/application/controller/admins/DataDictController.java b/system/system-application/src/main/java/cn/iocoder/mall/admin/application/controller/admins/DataDictController.java index 95103301f..59b64fa44 100644 --- a/system/system-application/src/main/java/cn/iocoder/mall/admin/application/controller/admins/DataDictController.java +++ b/system/system-application/src/main/java/cn/iocoder/mall/admin/application/controller/admins/DataDictController.java @@ -7,13 +7,12 @@ import cn.iocoder.mall.admin.api.dto.datadict.DataDictAddDTO; import cn.iocoder.mall.admin.api.dto.datadict.DataDictUpdateDTO; import cn.iocoder.mall.admin.application.convert.DataDictConvert; import cn.iocoder.mall.admin.application.vo.datadict.DataDictEnumVO; -import cn.iocoder.mall.admin.application.vo.datadict.DataDictVO; +import cn.iocoder.mall.admin.sdk.annotation.RequiresPermissions; import cn.iocoder.mall.admin.sdk.context.AdminSecurityContextHolder; import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.Multimaps; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiOperation; import org.apache.dubbo.config.annotation.Reference; import org.springframework.web.bind.annotation.*; @@ -21,6 +20,8 @@ import org.springframework.web.bind.annotation.*; import java.util.ArrayList; import java.util.List; +import static cn.iocoder.common.framework.vo.CommonResult.success; + @RestController @RequestMapping("admins/data_dict") @Api("数据字典模块") @@ -31,21 +32,19 @@ public class DataDictController { @GetMapping("/list") @ApiOperation(value = "数据字典全列表") - public CommonResult> list() { - CommonResult> result = dataDictService.selectDataDictList(); - return DataDictConvert.INSTANCE.convert(result); + @RequiresPermissions("system.dataDict.list") + public CommonResult> list() { + return success( dataDictService.selectDataDictList()); } @GetMapping("/tree") + @RequiresPermissions({}) // 因为是通用的接口,所以无需权限标识 @ApiOperation(value = "数据字典树结构", notes = "该接口返回的信息更为精简。一般用于前端缓存数据字典到本地。") public CommonResult> tree() { // 查询数据字典全列表 - CommonResult> result = dataDictService.selectDataDictList(); - if (result.isError()) { - return CommonResult.error(result); - } + List dataDicts = dataDictService.selectDataDictList(); // 构建基于 enumValue 聚合的 Multimap - ImmutableListMultimap dataDictMap = Multimaps.index(result.getData(), DataDictBO::getEnumValue); // KEY 是 enumValue ,VALUE 是 DataDictBO 数组 + ImmutableListMultimap dataDictMap = Multimaps.index(dataDicts, DataDictBO::getEnumValue); // KEY 是 enumValue ,VALUE 是 DataDictBO 数组 // 构建返回结果 List dataDictEnumVOs = new ArrayList<>(dataDictMap.size()); dataDictMap.keys().forEach(enumValue -> { @@ -53,58 +52,29 @@ public class DataDictController { .setValues(DataDictConvert.INSTANCE.convert2(dataDictMap.get(enumValue))); dataDictEnumVOs.add(dataDictEnumVO); }); - return CommonResult.success(dataDictEnumVOs); + return success(dataDictEnumVOs); } @PostMapping("/add") + @RequiresPermissions("system.dataDict.add") @ApiOperation(value = "创建数据字典") - @ApiImplicitParams({ - @ApiImplicitParam(name = "enumValue", value = "大类枚举值", required = true, example = "gender"), - @ApiImplicitParam(name = "value", value = "小类数值", required = true, example = "1"), - @ApiImplicitParam(name = "displayName", value = "展示名", required = true, example = "男"), - @ApiImplicitParam(name = "sort", required = true, value = "排序值", defaultValue = "10"), - @ApiImplicitParam(name = "memo", value = "备注", example = "你猜我猜不猜"), - }) - public CommonResult add(@RequestParam("enumValue") String enumValue, - @RequestParam("value") String value, - @RequestParam("displayName") String displayName, - @RequestParam("sort") Integer sort, - @RequestParam(value = "memo", required = false) String memo) { - // 创建 DataDictAddDTO 对象 - DataDictAddDTO dataDictAddDTO = new DataDictAddDTO().setEnumValue(enumValue).setValue(value).setDisplayName(displayName) - .setSort(sort).setMemo(memo); - // 保存数据字典 - CommonResult result = dataDictService.addDataDict(AdminSecurityContextHolder.getContext().getAdminId(), dataDictAddDTO); - // 返回结果 - return DataDictConvert.INSTANCE.convert2(result); + public CommonResult add(DataDictAddDTO dataDictAddDTO) { + return success(dataDictService.addDataDict(AdminSecurityContextHolder.getContext().getAdminId(), dataDictAddDTO)); } @PostMapping("/update") + @RequiresPermissions("system.dataDict.update") @ApiOperation(value = "更新数据字典") - @ApiImplicitParams({ - @ApiImplicitParam(name = "id", value = "编号", required = true, example = "100"), - @ApiImplicitParam(name = "value", value = "小类数值", required = true, example = "1"), - @ApiImplicitParam(name = "displayName", value = "展示名", required = true, example = "男"), - @ApiImplicitParam(name = "sort", required = true, value = "排序值", defaultValue = "10"), - @ApiImplicitParam(name = "memo", value = "备注", example = "你猜我猜不猜"), - }) - public CommonResult update(@RequestParam("id") Integer id, - @RequestParam("value") String value, - @RequestParam("displayName") String displayName, - @RequestParam("sort") Integer sort, - @RequestParam(value = "memo", required = false) String memo) { - // 创建 DataDictAddDTO 对象 - DataDictUpdateDTO dataDictUpdateDTO = new DataDictUpdateDTO().setId(id).setValue(value).setDisplayName(displayName) - .setSort(sort).setMemo(memo); - // 更新数据字典 - return dataDictService.updateDataDict(AdminSecurityContextHolder.getContext().getAdminId(), dataDictUpdateDTO); + public CommonResult 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 delete(@RequestParam("id") Integer id) { - return dataDictService.deleteDataDict(AdminSecurityContextHolder.getContext().getAdminId(), id); + return success(dataDictService.deleteDataDict(AdminSecurityContextHolder.getContext().getAdminId(), id)); } } diff --git a/system/system-application/src/main/java/cn/iocoder/mall/admin/application/controller/admins/PassportController.java b/system/system-application/src/main/java/cn/iocoder/mall/admin/application/controller/admins/PassportController.java index ed3b3ce33..78a384d8b 100644 --- a/system/system-application/src/main/java/cn/iocoder/mall/admin/application/controller/admins/PassportController.java +++ b/system/system-application/src/main/java/cn/iocoder/mall/admin/application/controller/admins/PassportController.java @@ -1,20 +1,19 @@ package cn.iocoder.mall.admin.application.controller.admins; import cn.iocoder.common.framework.vo.CommonResult; +import cn.iocoder.mall.admin.api.AdminService; import cn.iocoder.mall.admin.api.OAuth2Service; -import cn.iocoder.mall.admin.api.bo.oauth2.OAuth2AccessTokenBO; -import cn.iocoder.mall.admin.application.convert.PassportConvert; -import cn.iocoder.mall.admin.application.vo.PassportLoginVO; +import cn.iocoder.mall.admin.api.bo.admin.AdminAuthenticationBO; +import cn.iocoder.mall.admin.api.dto.admin.AdminAuthenticationDTO; import io.swagger.annotations.Api; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiOperation; import org.apache.dubbo.config.annotation.Reference; 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; +import static cn.iocoder.common.framework.vo.CommonResult.success; + @RestController @RequestMapping("admins/passport") @Api("Admin Passport 模块") @@ -23,16 +22,13 @@ public class PassportController { @Reference(validation = "true", version = "${dubbo.provider.OAuth2Service.version}") private OAuth2Service oauth2Service; + @Reference(validation = "true", version = "${dubbo.provider.AdminService.version}") + private AdminService adminService; + @PostMapping("/login") @ApiOperation(value = "手机号 + 密码登陆") - @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); + public CommonResult login(AdminAuthenticationDTO adminAuthenticationDTO) { + return success(adminService.authentication(adminAuthenticationDTO)); } // TODO 功能 logout diff --git a/system/system-application/src/main/java/cn/iocoder/mall/admin/application/controller/admins/RoleController.java b/system/system-application/src/main/java/cn/iocoder/mall/admin/application/controller/admins/RoleController.java index 3846fe8d1..fa6460922 100644 --- a/system/system-application/src/main/java/cn/iocoder/mall/admin/application/controller/admins/RoleController.java +++ b/system/system-application/src/main/java/cn/iocoder/mall/admin/application/controller/admins/RoleController.java @@ -17,7 +17,6 @@ import cn.iocoder.mall.admin.application.vo.role.RoleResourceTreeNodeVO; import cn.iocoder.mall.admin.sdk.context.AdminSecurityContextHolder; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiOperation; import org.apache.dubbo.config.annotation.Reference; import org.springframework.web.bind.annotation.*; @@ -101,10 +100,6 @@ public class RoleController { @PostMapping("/assign_resource") @ApiOperation(value = "分配角色资源") - @ApiImplicitParams({ - @ApiImplicitParam(name = "id", value = "角色编号", required = true, example = "1"), - @ApiImplicitParam(name = "resourceIds", value = "资源数组", required = true, example = "1,2,3"), - }) public CommonResult assignResource(RoleAssignResourceDTO roleAssignResourceDTO) { return success(roleService.assignRoleResource(AdminSecurityContextHolder.getContext().getAdminId(), roleAssignResourceDTO)); } diff --git a/system/system-application/src/main/java/cn/iocoder/mall/admin/application/convert/DataDictConvert.java b/system/system-application/src/main/java/cn/iocoder/mall/admin/application/convert/DataDictConvert.java index 8d875c4d9..c9b8a867f 100644 --- a/system/system-application/src/main/java/cn/iocoder/mall/admin/application/convert/DataDictConvert.java +++ b/system/system-application/src/main/java/cn/iocoder/mall/admin/application/convert/DataDictConvert.java @@ -1,9 +1,7 @@ package cn.iocoder.mall.admin.application.convert; -import cn.iocoder.common.framework.vo.CommonResult; import cn.iocoder.mall.admin.api.bo.datadict.DataDictBO; -import cn.iocoder.mall.admin.application.vo.datadict.DataDictVO; -import cn.iocoder.mall.admin.application.vo.datadict.DataDictValueVO; +import cn.iocoder.mall.admin.application.vo.datadict.DataDictEnumVO; import org.mapstruct.Mapper; import org.mapstruct.Mappings; import org.mapstruct.factory.Mappers; @@ -16,18 +14,6 @@ public interface DataDictConvert { DataDictConvert INSTANCE = Mappers.getMapper(DataDictConvert.class); @Mappings({}) - DataDictVO convert(DataDictBO dataDictBO); - - @Mappings({}) - List convert(List dataDictBOs); - - @Mappings({}) - CommonResult> convert(CommonResult> result); - - @Mappings({}) - CommonResult convert2(CommonResult result); - - @Mappings({}) - List convert2(List dataDictBOs); + List convert2(List dataDictBOs); } diff --git a/system/system-application/src/main/java/cn/iocoder/mall/admin/application/vo/datadict/DataDictEnumVO.java b/system/system-application/src/main/java/cn/iocoder/mall/admin/application/vo/datadict/DataDictEnumVO.java index d9814747b..936b0496e 100644 --- a/system/system-application/src/main/java/cn/iocoder/mall/admin/application/vo/datadict/DataDictEnumVO.java +++ b/system/system-application/src/main/java/cn/iocoder/mall/admin/application/vo/datadict/DataDictEnumVO.java @@ -14,7 +14,21 @@ public class DataDictEnumVO { @ApiModelProperty(value = "大类枚举值", required = true, example = "gender") private String enumValue; + @ApiModelProperty(value = "小类数值数组", required = true) - private List values; + private List values; + + @ApiModel("数据字典枚举值 VO") + @Data + @Accessors(chain = true) + public static class Value { + + @ApiModelProperty(value = "小类数值", required = true, example = "1") + private String value; + + @ApiModelProperty(value = "展示名", required = true, example = "男") + private String displayName; + + } } diff --git a/system/system-application/src/main/java/cn/iocoder/mall/admin/application/vo/datadict/DataDictVO.java b/system/system-application/src/main/java/cn/iocoder/mall/admin/application/vo/datadict/DataDictVO.java deleted file mode 100644 index 1524ce4c6..000000000 --- a/system/system-application/src/main/java/cn/iocoder/mall/admin/application/vo/datadict/DataDictVO.java +++ /dev/null @@ -1,26 +0,0 @@ -package cn.iocoder.mall.admin.application.vo.datadict; - -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; -import lombok.Data; -import lombok.experimental.Accessors; - -@ApiModel("数据字典 VO") -@Data -@Accessors(chain = true) -public class DataDictVO { - - @ApiModelProperty(value = "编号", required = true, example = "1") - private Integer id; - @ApiModelProperty(value = "大类枚举值", required = true, example = "gender") - private String enumValue; - @ApiModelProperty(value = "小类数值", required = true, example = "1") - private String value; - @ApiModelProperty(value = "展示名", required = true, example = "男") - private String displayName; - @ApiModelProperty(value = "排序值", required = true, example = "10") - private Integer sort; - @ApiModelProperty(value = "备注", example = "你猜") - private String memo; - -} diff --git a/system/system-application/src/main/java/cn/iocoder/mall/admin/application/vo/datadict/DataDictValueVO.java b/system/system-application/src/main/java/cn/iocoder/mall/admin/application/vo/datadict/DataDictValueVO.java deleted file mode 100644 index 7ed6eeff4..000000000 --- a/system/system-application/src/main/java/cn/iocoder/mall/admin/application/vo/datadict/DataDictValueVO.java +++ /dev/null @@ -1,18 +0,0 @@ -package cn.iocoder.mall.admin.application.vo.datadict; - -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; -import lombok.Data; -import lombok.experimental.Accessors; - -@ApiModel("数据字典枚举值 VO") -@Data -@Accessors(chain = true) -public class DataDictValueVO { - - @ApiModelProperty(value = "小类数值", required = true, example = "1") - private String value; - @ApiModelProperty(value = "展示名", required = true, example = "男") - private String displayName; - -} diff --git a/system/system-application/src/main/java/cn/iocoder/mall/admin/application/vo/role/RoleResourceTreeNodeVO.java b/system/system-application/src/main/java/cn/iocoder/mall/admin/application/vo/role/RoleResourceTreeNodeVO.java index 1994c7826..386ae3b01 100644 --- a/system/system-application/src/main/java/cn/iocoder/mall/admin/application/vo/role/RoleResourceTreeNodeVO.java +++ b/system/system-application/src/main/java/cn/iocoder/mall/admin/application/vo/role/RoleResourceTreeNodeVO.java @@ -26,6 +26,7 @@ public class RoleResourceTreeNodeVO { private String displayName; @ApiModelProperty(value = "子节点数组") private List children; + @ApiModelProperty(value = "是否授权", required = true, example = "true") private Boolean assigned; diff --git a/system/system-sdk/src/main/java/cn/iocoder/mall/admin/sdk/annotation/RequiresPermissions.java b/system/system-sdk/src/main/java/cn/iocoder/mall/admin/sdk/annotation/RequiresPermissions.java new file mode 100644 index 000000000..4dee66263 --- /dev/null +++ b/system/system-sdk/src/main/java/cn/iocoder/mall/admin/sdk/annotation/RequiresPermissions.java @@ -0,0 +1,22 @@ +package cn.iocoder.mall.admin.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(); + +} diff --git a/system/system-sdk/src/main/java/cn/iocoder/mall/admin/sdk/constant/LogicalEnum.java b/system/system-sdk/src/main/java/cn/iocoder/mall/admin/sdk/constant/LogicalEnum.java new file mode 100644 index 000000000..b417c3d1d --- /dev/null +++ b/system/system-sdk/src/main/java/cn/iocoder/mall/admin/sdk/constant/LogicalEnum.java @@ -0,0 +1,17 @@ +package cn.iocoder.mall.admin.sdk.constant; + +/** + * 逻辑类型枚举 + */ +public enum LogicalEnum { + + /** + * 并且 + */ + AND, + /** + * 或者 + */ + OR, + +} diff --git a/system/system-sdk/src/main/java/cn/iocoder/mall/admin/sdk/context/AdminSecurityContext.java b/system/system-sdk/src/main/java/cn/iocoder/mall/admin/sdk/context/AdminSecurityContext.java index a720ad09c..ed7a9c57c 100644 --- a/system/system-sdk/src/main/java/cn/iocoder/mall/admin/sdk/context/AdminSecurityContext.java +++ b/system/system-sdk/src/main/java/cn/iocoder/mall/admin/sdk/context/AdminSecurityContext.java @@ -1,26 +1,18 @@ package cn.iocoder.mall.admin.sdk.context; +import lombok.Data; +import lombok.experimental.Accessors; + import java.util.Set; /** * Security 上下文 */ +@Data +@Accessors(chain = true) public class AdminSecurityContext { - private final Integer adminId; - private final Set roleIds; + private Integer adminId; + private 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/system/system-sdk/src/main/java/cn/iocoder/mall/admin/sdk/context/AdminSecurityContextHolder.java b/system/system-sdk/src/main/java/cn/iocoder/mall/admin/sdk/context/AdminSecurityContextHolder.java index 532d650e1..7e110ce8a 100644 --- a/system/system-sdk/src/main/java/cn/iocoder/mall/admin/sdk/context/AdminSecurityContextHolder.java +++ b/system/system-sdk/src/main/java/cn/iocoder/mall/admin/sdk/context/AdminSecurityContextHolder.java @@ -17,7 +17,7 @@ public class AdminSecurityContextHolder { AdminSecurityContext ctx = SECURITY_CONTEXT.get(); // 为空时,设置一个空的进去 if (ctx == null) { - ctx = new AdminSecurityContext(null, null); + ctx = new AdminSecurityContext(); SECURITY_CONTEXT.set(ctx); } return ctx; diff --git a/system/system-sdk/src/main/java/cn/iocoder/mall/admin/sdk/interceptor/AdminSecurityInterceptor.java b/system/system-sdk/src/main/java/cn/iocoder/mall/admin/sdk/interceptor/AdminSecurityInterceptor.java index d04cd7419..cbfce78f4 100644 --- a/system/system-sdk/src/main/java/cn/iocoder/mall/admin/sdk/interceptor/AdminSecurityInterceptor.java +++ b/system/system-sdk/src/main/java/cn/iocoder/mall/admin/sdk/interceptor/AdminSecurityInterceptor.java @@ -1,37 +1,48 @@ package cn.iocoder.mall.admin.sdk.interceptor; -import cn.iocoder.common.framework.constant.MallConstants; +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.vo.CommonResult; +import cn.iocoder.common.framework.util.StringUtil; +import cn.iocoder.mall.admin.api.AdminService; import cn.iocoder.mall.admin.api.OAuth2Service; +import cn.iocoder.mall.admin.api.bo.admin.AdminAuthorizationBO; import cn.iocoder.mall.admin.api.bo.oauth2.OAuth2AuthenticationBO; import cn.iocoder.mall.admin.api.constant.AdminErrorCodeEnum; +import cn.iocoder.mall.admin.api.dto.oauth2.OAuth2GetTokenDTO; +import cn.iocoder.mall.admin.sdk.annotation.RequiresPermissions; import cn.iocoder.mall.admin.sdk.context.AdminSecurityContext; import cn.iocoder.mall.admin.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_url:#{null}}") + @Value("${admins.security.ignore_urls:#{null}}") private Set ignoreUrls; public AdminSecurityInterceptor setIgnoreUrls(Set ignoreUrls) { @@ -42,39 +53,46 @@ public class AdminSecurityInterceptor extends HandlerInterceptorAdapter { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 设置当前访问的用户类型。注意,即使未登陆,我们也认为是管理员 - MallUtil.setUserType(request, MallConstants.USER_TYPE_ADMIN); - // 校验访问令牌是否正确。若正确,返回授权信息 + MallUtil.setUserType(request, UserTypeEnum.ADMIN.getValue()); + + // 根据 accessToken 获得认证信息,判断是谁 String accessToken = HttpUtil.obtainAuthorization(request); OAuth2AuthenticationBO authentication = null; - if (accessToken != null) { - CommonResult result = oauth2Service.checkToken(accessToken); - // TODO sin 先临时跳过 认证 -// CommonResult result = CommonResult.success(new OAuth2AuthenticationBO() -// .setAdminId(1) -// .setRoleIds(Sets.newHashSet(1, 2, 3, 4))); - if (result.isError()) { // TODO 芋艿,如果访问的地址无需登录,这里也不用抛异常 - throw new ServiceException(result.getCode(), result.getMessage()); - } - authentication = result.getData(); - // 添加到 AdminSecurityContext - AdminSecurityContext context = new AdminSecurityContext(authentication.getAdminId(), authentication.getRoleIds()); - AdminSecurityContextHolder.setContext(context); - // 同时也记录管理员编号到 AdminAccessLogInterceptor 中。因为: - // AdminAccessLogInterceptor 需要在 AdminSecurityInterceptor 之前执行,这样记录的访问日志才健全 - // AdminSecurityInterceptor 执行后,会移除 AdminSecurityContext 信息,这就导致 AdminAccessLogInterceptor 无法获得管理员编号 - // 因此,这里需要进行记录 - if (authentication.getAdminId() != null) { - MallUtil.setUserId(request, authentication.getAdminId()); - } - } else { - String url = request.getRequestURI(); - if (ignoreUrls != null && !ignoreUrls.contains(url)) { // TODO 临时写死。非登陆接口,必须已经认证身份,不允许匿名访问 - throw new ServiceException(AdminErrorCodeEnum.OAUTH_NOT_LOGIN.getCode(), AdminErrorCodeEnum.OAUTH_NOT_LOGIN.getMessage()); + ServiceException serviceException = null; + if (StringUtil.hasText(accessToken)) { + try { + authentication = oauth2Service.getAuthentication(new OAuth2GetTokenDTO().setAccessToken(accessToken) + .setUserType(UserTypeEnum.ADMIN.getValue())); + } catch (ServiceException e) { + serviceException = e; } } - // 校验是否需要已授权 - // TODO sin 暂时不校验 - // checkPermission(request, authentication); + + // 进行鉴权 + 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.setRoleIds(authorization.getRoleIds()); + } + } + // 返回成功 return super.preHandle(request, response, handler); } @@ -85,14 +103,18 @@ public class AdminSecurityInterceptor extends HandlerInterceptorAdapter { 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()); - } + 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); + } + + private void checkPermission() { + } } diff --git a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/AdminService.java b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/AdminService.java index 9337ecd11..da12818b9 100644 --- a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/AdminService.java +++ b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/AdminService.java @@ -1,8 +1,10 @@ package cn.iocoder.mall.admin.api; import cn.iocoder.common.framework.vo.PageResult; -import cn.iocoder.mall.admin.api.bo.role.RoleBO; +import cn.iocoder.mall.admin.api.bo.admin.AdminAuthenticationBO; +import cn.iocoder.mall.admin.api.bo.admin.AdminAuthorizationBO; import cn.iocoder.mall.admin.api.bo.admin.AdminBO; +import cn.iocoder.mall.admin.api.bo.role.RoleBO; import cn.iocoder.mall.admin.api.dto.admin.*; import java.util.Collection; @@ -14,6 +16,16 @@ import java.util.Map; */ public interface AdminService { + /** + * 用户认证。认证成功后,返回认证信息 + * + * 实际上,就是用户名 + 密码登陆 + * + * @param adminAuthenticationDTO 用户认证信息 + * @return 认证信息 + */ + AdminAuthenticationBO authentication(AdminAuthenticationDTO adminAuthenticationDTO); + PageResult getAdminPage(AdminPageDTO adminPageDTO); AdminBO addAdmin(Integer adminId, AdminAddDTO adminAddDTO); @@ -49,4 +61,13 @@ public interface AdminService { */ Boolean assignAdminRole(Integer adminId, AdminAssignRoleDTO adminAssignRoleDTO); + /** + * 判断管理员是否有指定权限 + * + * @param adminId 管理员 + * @param permissions 权限数组 + * @return 管理员授权信息 + */ + AdminAuthorizationBO checkPermissions(Integer adminId, List permissions); + } diff --git a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/DataDictService.java b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/DataDictService.java index 7d92b8bee..2d3da6bd3 100644 --- a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/DataDictService.java +++ b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/DataDictService.java @@ -10,13 +10,13 @@ import java.util.List; public interface DataDictService { - CommonResult> selectDataDictList(); + List selectDataDictList(); - CommonResult addDataDict(Integer adminId, DataDictAddDTO dataDictAddDTO); + DataDictBO addDataDict(Integer adminId, DataDictAddDTO dataDictAddDTO); - CommonResult updateDataDict(Integer adminId, DataDictUpdateDTO dataDictUpdateDTO); + Boolean updateDataDict(Integer adminId, DataDictUpdateDTO dataDictUpdateDTO); - CommonResult deleteDataDict(Integer adminId, Integer dataDictId); + Boolean deleteDataDict(Integer adminId, Integer dataDictId); /** * 获取字典值 - 单个 @@ -28,6 +28,7 @@ public interface DataDictService { * @return */ CommonResult getDataDict(String dictKey, Object dictValue); + CommonResult> getDataDict(String dictKey); /** diff --git a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/OAuth2Service.java b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/OAuth2Service.java index 1041a3ba9..ccc78681a 100644 --- a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/OAuth2Service.java +++ b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/OAuth2Service.java @@ -1,33 +1,31 @@ package cn.iocoder.mall.admin.api; -import cn.iocoder.common.framework.vo.CommonResult; import cn.iocoder.mall.admin.api.bo.oauth2.OAuth2AccessTokenBO; import cn.iocoder.mall.admin.api.bo.oauth2.OAuth2AuthenticationBO; +import cn.iocoder.mall.admin.api.dto.oauth2.OAuth2CreateTokenDTO; +import cn.iocoder.mall.admin.api.dto.oauth2.OAuth2GetTokenDTO; -import java.util.Set; - +/** + * Oauth2 服务接口 + */ public interface OAuth2Service { - CommonResult getAccessToken(String username, String password); - /** - * 校验访问令牌,获取身份信息( 不包括 accessToken 等等 ) + * 根据身份信息,创建 accessToken 信息 * - * @param accessToken 访问令牌 - * @return 授权信息 + * @param oauth2CreateTokenDTO 身份信息 DTO + * @return accessToken 信息 */ - CommonResult checkToken(String accessToken); - - /** - * 校验权限(鉴权) - * - * @param adminId 管理员编号 - * @param roleIds 管理员拥有的角色编号的集合 - * @param url 指定 URL - * @return 是否有权限 - */ - CommonResult checkPermission(Integer adminId, Set roleIds, String url); + OAuth2AccessTokenBO createToken(OAuth2CreateTokenDTO oauth2CreateTokenDTO); // TODO @see 刷新 token + /** + * 通过 accessToken 获得身份信息 + * + * @param oauth2GetTokenDTO accessToken 信息 + * @return 身份信息 + */ + OAuth2AuthenticationBO getAuthentication(OAuth2GetTokenDTO oauth2GetTokenDTO); + } diff --git a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/bo/admin/AdminAuthenticationBO.java b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/bo/admin/AdminAuthenticationBO.java new file mode 100644 index 000000000..905061d36 --- /dev/null +++ b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/bo/admin/AdminAuthenticationBO.java @@ -0,0 +1,22 @@ +package cn.iocoder.mall.admin.api.bo.admin; + +import cn.iocoder.mall.admin.api.bo.oauth2.OAuth2AccessTokenBO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.experimental.Accessors; + +@ApiModel("管理员认证 BO") +@Data +@Accessors(chain = true) +public class AdminAuthenticationBO { + + @ApiModelProperty(value = "管理员编号", required = true, example = "1") + private Integer id; + + @ApiModelProperty(value = "昵称", required = true, example = "小王") + private String nickname; + + private OAuth2AccessTokenBO token; + +} diff --git a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/bo/admin/AdminAuthorizationBO.java b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/bo/admin/AdminAuthorizationBO.java new file mode 100644 index 000000000..b16a4be02 --- /dev/null +++ b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/bo/admin/AdminAuthorizationBO.java @@ -0,0 +1,21 @@ +package cn.iocoder.mall.admin.api.bo.admin; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.Set; + +@ApiModel("管理员授权 BO") +@Data +@Accessors(chain = true) +public class AdminAuthorizationBO { + + @ApiModelProperty(value = "管理员编号", required = true, example = "1") + private Integer id; + + @ApiModelProperty(value = "角色编号数组", required = true, example = "1") + private Set roleIds; + +} diff --git a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/bo/oauth2/OAuth2AccessTokenBO.java b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/bo/oauth2/OAuth2AccessTokenBO.java index 25a5f466b..8c7126527 100644 --- a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/bo/oauth2/OAuth2AccessTokenBO.java +++ b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/bo/oauth2/OAuth2AccessTokenBO.java @@ -1,28 +1,24 @@ package cn.iocoder.mall.admin.api.bo.oauth2; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.experimental.Accessors; import java.io.Serializable; -/** - * OAUTH2 AccessToken BO - */ +@ApiModel("OAuth2 Token 信息 BO") @Data @Accessors(chain = true) public class OAuth2AccessTokenBO implements Serializable { - /** - * 访问令牌 - */ + @ApiModelProperty(value = "accessToken", required = true, example = "001e8f49b20e47f7b3a2de774497cd50") private String accessToken; - /** - * 刷新令牌 - */ + + @ApiModelProperty(value = "refreshToken", required = true, example = "001e8f49b20e47f7b3a2de774497cd50") private String refreshToken; - /** - * 过期时间,单位:秒。 - */ + + @ApiModelProperty(value = "过期时间,单位:秒", required = true, example = "1024") private Integer expiresIn; } diff --git a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/bo/oauth2/OAuth2AuthenticationBO.java b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/bo/oauth2/OAuth2AuthenticationBO.java index 68e5ff9ea..dfd6abb19 100644 --- a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/bo/oauth2/OAuth2AuthenticationBO.java +++ b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/bo/oauth2/OAuth2AuthenticationBO.java @@ -1,25 +1,19 @@ package cn.iocoder.mall.admin.api.bo.oauth2; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.experimental.Accessors; -import java.io.Serializable; -import java.util.Set; - -/** - * OAUTH2 认证 BO - */ +@ApiModel("OAUTH2 认证 BO") @Data @Accessors(chain = true) -public class OAuth2AuthenticationBO implements Serializable { +public class OAuth2AuthenticationBO { - /** - * 管理员编号 - */ - private Integer adminId; - /** - * 角色编号数组 - */ - private Set roleIds; + @ApiModelProperty(value = "用户编号", required = true, example = "1") + private Integer userId; + + @ApiModelProperty(value = "用户类型", required = true, example = "1", notes = "参考 UserTypeEnum 枚举") + private Integer userType; } diff --git a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/bo/oauth2/OAuth2AuthenticationOldBO.java b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/bo/oauth2/OAuth2AuthenticationOldBO.java new file mode 100644 index 000000000..027822397 --- /dev/null +++ b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/bo/oauth2/OAuth2AuthenticationOldBO.java @@ -0,0 +1,27 @@ +package cn.iocoder.mall.admin.api.bo.oauth2; + +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Set; + +/** + * OAUTH2 认证 BO + */ +@Data +@Accessors(chain = true) +public class OAuth2AuthenticationOldBO implements Serializable { + + /** + * 管理员编号 + */ + private Integer adminId; + /** + * 角色编号数组 + */ + private Set roleIds; + + + +} diff --git a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/constant/AdminErrorCodeEnum.java b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/constant/AdminErrorCodeEnum.java index 3764d63a7..1626a6668 100644 --- a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/constant/AdminErrorCodeEnum.java +++ b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/constant/AdminErrorCodeEnum.java @@ -12,11 +12,11 @@ public enum AdminErrorCodeEnum { // 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_NOT_LOGIN(1002001015, "账号未登陆"), + OAUTH2_INVALID_TOKEN_NOT_FOUND(1002001011, "访问令牌不存在"), + OAUTH2_INVALID_TOKEN_EXPIRED(1002001012, "访问令牌已过期"), + OAUTH2_INVALID_TOKEN_INVALID(1002001013, "访问令牌已失效"), + OAUTH2_NOT_LOGIN(1002001015, "账号未登陆"), + OAUTH2_INVALID_TOKEN_ERROR_USER_TYPE(1002001016, "访问令牌用户类型不正确"), OAUTH_INVALID_TOKEN(1002001020, ""), // 预留 @@ -29,6 +29,7 @@ public enum AdminErrorCodeEnum { ADMIN_DELETE_ONLY_DISABLE(1002002004, "只有关闭的账号才可以删除"), ADMIN_ADMIN_STATUS_CAN_NOT_UPDATE(1002002005, "管理员的账号状态不允许变更"), ADMIN_ASSIGN_ROLE_NOT_EXISTS(1002002006, "分配员工角色时,有角色不存在"), + ADMIN_INVALID_PERMISSION(1002002007, "没有该操作权限"), // ========== 资源模块 1002003000 ========== RESOURCE_NAME_DUPLICATE(1002003000, "已经存在该名字的资源"), diff --git a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/constant/ResourceTypeEnum.java b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/constant/ResourceTypeEnum.java new file mode 100644 index 000000000..47e0653bf --- /dev/null +++ b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/constant/ResourceTypeEnum.java @@ -0,0 +1,54 @@ +package cn.iocoder.mall.admin.api.constant; + +import cn.iocoder.common.framework.core.IntArrayValuable; + +import java.util.Arrays; + +/** + * 资源类型枚举 + */ +public enum ResourceTypeEnum implements IntArrayValuable { + + MENU(1, "菜单"), + BUTTON(2, "按钮"); + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ResourceTypeEnum::getValue).toArray(); + + /** + * 资源类型 + */ + private Integer value; + /** + * 资源类型名 + */ + private String name; + + ResourceTypeEnum(Integer value, String name) { + this.value = value; + this.name = name; + } + + public Integer getValue() { + return value; + } + + public ResourceTypeEnum setValue(Integer value) { + this.value = value; + return this; + } + + public String getName() { + return name; + } + + public ResourceTypeEnum setName(String name) { + this.name = name; + return this; + } + + @Override + public int[] array() { + return ARRAYS; + } + +} diff --git a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/admin/AdminAddDTO.java b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/admin/AdminAddDTO.java index ee9e73648..6d07ddb39 100644 --- a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/admin/AdminAddDTO.java +++ b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/admin/AdminAddDTO.java @@ -17,7 +17,7 @@ public class AdminAddDTO implements Serializable { @ApiModelProperty(value = "登陆账号", required = true, example = "15601691300") @NotEmpty(message = "登陆账号不能为空") - @Length(min = 6, max = 16, message = "账号长度为 6-16 位") + @Length(min = 5, max = 16, message = "账号长度为 5-16 位") @Pattern(regexp = "^[A-Za-z0-9]+$", message = "账号格式为数字以及字母") private String username; diff --git a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/admin/AdminAuthenticationDTO.java b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/admin/AdminAuthenticationDTO.java new file mode 100644 index 000000000..f20c9cb2e --- /dev/null +++ b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/admin/AdminAuthenticationDTO.java @@ -0,0 +1,28 @@ +package cn.iocoder.mall.admin.api.dto.admin; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.experimental.Accessors; +import org.hibernate.validator.constraints.Length; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.Pattern; + +@ApiModel("管理员认证 DTO") +@Data +@Accessors(chain = true) +public class AdminAuthenticationDTO { + + @ApiModelProperty(value = "登陆账号", required = true, example = "15601691300") + @NotEmpty(message = "登陆账号不能为空") + @Length(min = 5, max = 16, message = "账号长度为 5-16 位") + @Pattern(regexp = "^[A-Za-z0-9]+$", message = "账号格式为数字以及字母") + private String username; + + @ApiModelProperty(value = "密码", required = true, example = "buzhidao") + @NotEmpty(message = "密码不能为空") + @Length(min = 6, max = 16, message = "密码长度为 6-16 位") + private String password; + +} diff --git a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/admin/AdminUpdateDTO.java b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/admin/AdminUpdateDTO.java index fd8d1f25d..f6b189632 100644 --- a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/admin/AdminUpdateDTO.java +++ b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/admin/AdminUpdateDTO.java @@ -22,7 +22,7 @@ public class AdminUpdateDTO implements Serializable { @ApiModelProperty(value = "登陆账号", required = true, example = "15601691300") @NotEmpty(message = "登陆账号不能为空") - @Length(min = 6, max = 16, message = "账号长度为 6-16 位") + @Length(min = 5, max = 16, message = "账号长度为 5-16 位") @Pattern(regexp = "^[A-Za-z0-9]+$", message = "账号格式为数字以及字母") private String username; diff --git a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/datadict/DataDictAddDTO.java b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/datadict/DataDictAddDTO.java index 574a66ae5..378dc5bcb 100644 --- a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/datadict/DataDictAddDTO.java +++ b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/datadict/DataDictAddDTO.java @@ -1,5 +1,7 @@ package cn.iocoder.mall.admin.api.dto.datadict; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.experimental.Accessors; @@ -7,36 +9,28 @@ import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import java.io.Serializable; -/** - * 数据字典添加 DTO - */ +@ApiModel("数据字典添加 DTO") @Data @Accessors(chain = true) public class DataDictAddDTO implements Serializable { - /** - * 大类枚举值 - */ + @ApiModelProperty(value = "大类枚举值", required = true, example = "gender") @NotEmpty(message = "大类枚举值不能为空") private String enumValue; - /** - * 小类数值 - */ + + @ApiModelProperty(value = "小类数值", required = true, example = "1") @NotEmpty(message = "小类数值不能为空") private String value; - /** - * 展示名 - */ + + @ApiModelProperty(value = "展示名", required = true, example = "男") @NotEmpty(message = "展示名不能为空") private String displayName; - /** - * 排序值 - */ + + @ApiModelProperty(required = true, value = "排序值", example = "123") @NotNull(message = "排序值不能为空") private Integer sort; - /** - * 备注 - */ + + @ApiModelProperty(value = "备注", example = "你猜我猜不猜") private String memo; } diff --git a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/datadict/DataDictUpdateDTO.java b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/datadict/DataDictUpdateDTO.java index 9653beb2b..bfaa8b747 100644 --- a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/datadict/DataDictUpdateDTO.java +++ b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/datadict/DataDictUpdateDTO.java @@ -1,5 +1,6 @@ package cn.iocoder.mall.admin.api.dto.datadict; +import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.experimental.Accessors; @@ -14,29 +15,27 @@ import java.io.Serializable; @Accessors(chain = true) public class DataDictUpdateDTO implements Serializable { - /** - * 编号 - */ - @NotNull(message = "编号不能为空") + @ApiModelProperty(value = "数据字典编号", required = true, example = "1") + @NotNull(message = "数据字典编号不能为空") private Integer id; - /** - * 小类数值 - */ + + @ApiModelProperty(value = "大类枚举值", required = true, example = "gender") + @NotEmpty(message = "大类枚举值不能为空") + private String enumValue; + + @ApiModelProperty(value = "小类数值", required = true, example = "1") @NotEmpty(message = "小类数值不能为空") private String value; - /** - * 展示名 - */ + + @ApiModelProperty(value = "展示名", required = true, example = "男") @NotEmpty(message = "展示名不能为空") private String displayName; - /** - * 排序值 - */ + + @ApiModelProperty(required = true, value = "排序值", example = "123") @NotNull(message = "排序值不能为空") private Integer sort; - /** - * 备注 - */ + + @ApiModelProperty(value = "备注", example = "你猜我猜不猜") private String memo; } diff --git a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/oauth2/OAuth2CreateTokenDTO.java b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/oauth2/OAuth2CreateTokenDTO.java new file mode 100644 index 000000000..a70e2298f --- /dev/null +++ b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/oauth2/OAuth2CreateTokenDTO.java @@ -0,0 +1,26 @@ +package cn.iocoder.mall.admin.api.dto.oauth2; + +import cn.iocoder.common.framework.validator.InEnum; +import cn.iocoder.mall.admin.api.constant.ResourceTypeEnum; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.experimental.Accessors; + +import javax.validation.constraints.NotNull; + +@ApiModel("OAuth2 创建 Token DTO") +@Data +@Accessors(chain = true) +public class OAuth2CreateTokenDTO { + + @ApiModelProperty(value = "用户编号", required = true, example = "1") + @NotNull(message = "用户编号不能为空") + private Integer userId; + + @ApiModelProperty(value = "用户类型", required = true, example = "1", notes = "参见 ResourceTypeEnum 枚举") + @NotNull(message = "用户类型不能为空") + @InEnum(value = ResourceTypeEnum.class, message = "用户类型必须是 {value}") + private Integer userType; + +} diff --git a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/oauth2/OAuth2GetTokenDTO.java b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/oauth2/OAuth2GetTokenDTO.java new file mode 100644 index 000000000..0b0d4862c --- /dev/null +++ b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/oauth2/OAuth2GetTokenDTO.java @@ -0,0 +1,27 @@ +package cn.iocoder.mall.admin.api.dto.oauth2; + +import cn.iocoder.common.framework.validator.InEnum; +import cn.iocoder.mall.admin.api.constant.ResourceTypeEnum; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.experimental.Accessors; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +@ApiModel("OAuth2 身份验证 DTO") +@Data +@Accessors(chain = true) +public class OAuth2GetTokenDTO { + + @ApiModelProperty(value = "accessToken", required = true, example = "001e8f49b20e47f7b3a2de774497cd50") + @NotEmpty(message = "accessToken 不能为空") + private String accessToken; + + @ApiModelProperty(value = "用户类型", required = true, example = "1", notes = "参见 ResourceTypeEnum 枚举") + @NotNull(message = "用户类型不能为空") + @InEnum(value = ResourceTypeEnum.class, message = "用户类型必须是 {value}") + private Integer userType; + +} diff --git a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/resource/ResourceAddDTO.java b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/resource/ResourceAddDTO.java index f52b08514..a39ffae19 100644 --- a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/resource/ResourceAddDTO.java +++ b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/resource/ResourceAddDTO.java @@ -1,5 +1,7 @@ package cn.iocoder.mall.admin.api.dto.resource; +import cn.iocoder.common.framework.validator.InEnum; +import cn.iocoder.mall.admin.api.constant.ResourceTypeEnum; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @@ -17,6 +19,7 @@ public class ResourceAddDTO implements Serializable { @ApiModelProperty(value = "资源类型。1 代表【菜单】;2 代表【按钮】", required = true, example = "1") @NotNull(message = "类型不能为空") + @InEnum(value = ResourceTypeEnum.class, message = "资源类型必须是 {value}") private Integer type; @ApiModelProperty(value = "排序", required = true, example = "1") diff --git a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/resource/ResourceUpdateDTO.java b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/resource/ResourceUpdateDTO.java index 2b72f3020..9c14186e9 100644 --- a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/resource/ResourceUpdateDTO.java +++ b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/resource/ResourceUpdateDTO.java @@ -1,5 +1,7 @@ package cn.iocoder.mall.admin.api.dto.resource; +import cn.iocoder.common.framework.validator.InEnum; +import cn.iocoder.mall.admin.api.constant.ResourceTypeEnum; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @@ -21,6 +23,7 @@ public class ResourceUpdateDTO implements Serializable { @ApiModelProperty(value = "资源类型。1 代表【菜单】;2 代表【按钮】", required = true, example = "1") @NotNull(message = "类型不能为空") + @InEnum(value = ResourceTypeEnum.class, message = "资源类型必须是 {value}") private Integer type; @ApiModelProperty(value = "排序", required = true, example = "1") diff --git a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/convert/AdminConvert.java b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/convert/AdminConvert.java index 4a02b8caa..7909b2091 100644 --- a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/convert/AdminConvert.java +++ b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/convert/AdminConvert.java @@ -1,6 +1,7 @@ package cn.iocoder.mall.admin.convert; import cn.iocoder.common.framework.vo.PageResult; +import cn.iocoder.mall.admin.api.bo.admin.AdminAuthenticationBO; import cn.iocoder.mall.admin.api.bo.admin.AdminBO; import cn.iocoder.mall.admin.api.dto.admin.AdminAddDTO; import cn.iocoder.mall.admin.api.dto.admin.AdminUpdateDTO; @@ -21,6 +22,9 @@ public interface AdminConvert { @Mappings({}) AdminBO convert(AdminDO adminDO); + @Mappings({}) + AdminAuthenticationBO convert2(AdminDO admin); + @Mappings({}) AdminDO convert(AdminAddDTO adminAddDTO); diff --git a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/convert/OAuth2Convert.java b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/convert/OAuth2Convert.java index 4629e552f..44c10b5b4 100644 --- a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/convert/OAuth2Convert.java +++ b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/convert/OAuth2Convert.java @@ -2,6 +2,7 @@ package cn.iocoder.mall.admin.convert; import cn.iocoder.mall.admin.api.bo.oauth2.OAuth2AccessTokenBO; import cn.iocoder.mall.admin.api.bo.oauth2.OAuth2AuthenticationBO; +import cn.iocoder.mall.admin.api.bo.oauth2.OAuth2AuthenticationOldBO; import cn.iocoder.mall.admin.dataobject.AdminRoleDO; import cn.iocoder.mall.admin.dataobject.OAuth2AccessTokenDO; import org.mapstruct.Mapper; @@ -27,11 +28,14 @@ public interface OAuth2Convert { .setExpiresIn(Math.max((int) ((oauth2AccessTokenDO.getExpiresTime().getTime() - System.currentTimeMillis()) / 1000), 0)); } + @Mappings({}) + OAuth2AuthenticationOldBO convertToAuthenticationOld(OAuth2AccessTokenDO oauth2AccessTokenDO); + @Mappings({}) OAuth2AuthenticationBO convertToAuthentication(OAuth2AccessTokenDO oauth2AccessTokenDO); - default OAuth2AuthenticationBO convertToAuthentication(OAuth2AccessTokenDO oauth2AccessTokenDO, List adminRoleDOs) { - return convertToAuthentication(oauth2AccessTokenDO) + default OAuth2AuthenticationOldBO convertToAuthenticationOld(OAuth2AccessTokenDO oauth2AccessTokenDO, List adminRoleDOs) { + return convertToAuthenticationOld(oauth2AccessTokenDO) .setRoleIds(adminRoleDOs.stream().map(AdminRoleDO::getRoleId).collect(Collectors.toSet())); } diff --git a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dao/AdminRoleMapper.java b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dao/AdminRoleMapper.java index 9112a484e..a584990f3 100644 --- a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dao/AdminRoleMapper.java +++ b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dao/AdminRoleMapper.java @@ -12,16 +12,27 @@ import java.util.List; @Repository public interface AdminRoleMapper extends BaseMapper { - List selectByAdminId(@Param("adminId") Integer adminId); + default List selectByAdminId( Integer adminId) { + return selectList(new QueryWrapper().eq("admin_id", adminId)); + } default List selectListByAdminIds(Collection adminIds) { return selectList(new QueryWrapper().in("admin_id", adminIds)); } - int updateToDeletedByAdminId(@Param("adminId") Integer adminId); + default int deleteByAdminId(Integer adminId) { + return delete(new QueryWrapper().eq("admin_id", adminId)); + } - int updateToDeletedByRoleId(@Param("roleId") Integer roleId); + default int deleteByRoleId(Integer roleId) { + return delete(new QueryWrapper().eq("role_id", roleId)); + } - void insertList(@Param("adminRoleDOs") List adminRoleDOs); + /** + * 批量插入。因为 MyBaits Plus 的批量插入是基于 Service 实现,所以只好写 XML + * + * @param adminRoleDOs 数组 + */ + int insertList(@Param("adminRoleDOs") List adminRoleDOs); } diff --git a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dao/DataDictMapper.java b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dao/DataDictMapper.java index 5b6e5e5f6..90bb43f15 100644 --- a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dao/DataDictMapper.java +++ b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dao/DataDictMapper.java @@ -1,6 +1,8 @@ package cn.iocoder.mall.admin.dao; import cn.iocoder.mall.admin.dataobject.DataDictDO; +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; @@ -8,9 +10,7 @@ import java.util.Collection; import java.util.List; @Repository -public interface DataDictMapper { - - DataDictDO selectById(@Param("id") Integer id); +public interface DataDictMapper extends BaseMapper { DataDictDO selectByEnumValueAndValue( @Param("enumValue") String enumValue, @@ -26,10 +26,9 @@ public interface DataDictMapper { @Param("enumValue") String enumValue ); - List selectList(); + default List selectList() { + return selectList(new QueryWrapper<>()); + } - void insert(DataDictDO dataDict); - - int update(DataDictDO dataDict); } diff --git a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dao/OAuth2AccessTokenMapper.java b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dao/OAuth2AccessTokenMapper.java index 7931d0130..9f113ac36 100644 --- a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dao/OAuth2AccessTokenMapper.java +++ b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dao/OAuth2AccessTokenMapper.java @@ -1,16 +1,17 @@ package cn.iocoder.mall.admin.dao; import cn.iocoder.mall.admin.dataobject.OAuth2AccessTokenDO; -import org.apache.ibatis.annotations.Param; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.springframework.stereotype.Repository; @Repository -public interface OAuth2AccessTokenMapper { +public interface OAuth2AccessTokenMapper extends BaseMapper { - void insert(OAuth2AccessTokenDO entity); + default int updateToInvalidByAdminId(Integer adminId) { + QueryWrapper query = new QueryWrapper() + .eq("admin_id", adminId).eq("valid", true); + return update(new OAuth2AccessTokenDO().setValid(false), query); + } - OAuth2AccessTokenDO selectByTokenId(@Param("id") String id); - - int updateToInvalidByAdminId(@Param("adminId") Integer adminId); - -} \ No newline at end of file +} diff --git a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dao/OAuth2RefreshTokenMapper.java b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dao/OAuth2RefreshTokenMapper.java index 8cd6e6166..9d9caec75 100644 --- a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dao/OAuth2RefreshTokenMapper.java +++ b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dao/OAuth2RefreshTokenMapper.java @@ -1,14 +1,17 @@ package cn.iocoder.mall.admin.dao; import cn.iocoder.mall.admin.dataobject.OAuth2RefreshTokenDO; -import org.apache.ibatis.annotations.Param; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.springframework.stereotype.Repository; @Repository -public interface OAuth2RefreshTokenMapper { +public interface OAuth2RefreshTokenMapper extends BaseMapper { - void insert(OAuth2RefreshTokenDO entity); + default int updateToInvalidByAdminId(Integer adminId) { + QueryWrapper query = new QueryWrapper() + .eq("admin_id", adminId).eq("valid", true); + return update(new OAuth2RefreshTokenDO().setValid(false), query); + } - int updateToInvalidByAdminId(@Param("adminId") Integer adminId); - -} \ No newline at end of file +} diff --git a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dao/ResourceMapper.java b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dao/ResourceMapper.java index f2b0a9782..473065670 100644 --- a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dao/ResourceMapper.java +++ b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dao/ResourceMapper.java @@ -1,6 +1,8 @@ package cn.iocoder.mall.admin.dao; +import cn.iocoder.common.framework.mybatis.QueryWrapperX; import cn.iocoder.mall.admin.dataobject.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; @@ -11,16 +13,24 @@ import java.util.Set; @Repository public interface ResourceMapper extends BaseMapper { - ResourceDO selectByTypeAndHandler(@Param("type") Integer type, - @Param("handler") String handler); - + // TODO 芋艿,后续改造。 List selectListByTypeAndRoleIds(@Param("type") Integer type, @Param("roleIds") Set roleIds); - List selectListByType(@Param("type") Integer type); + default List selectListByPermission(String permission) { + return selectList(new QueryWrapperX().like("permissions", permission)); + } - List selectListByIds(@Param("ids") Set ids); + default List selectListByType(Integer type) { + return selectList(new QueryWrapperX().eqIfPresent("type", type)); + } - int selectCountByPid(@Param("pid") Integer pid); + default List selectListByIds(Set ids) { + return selectList(new QueryWrapper().in("id", ids)); + } + + default int selectCountByPid(Integer pid) { + return selectCount(new QueryWrapper().eq("pid", pid)); + } } diff --git a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dao/RoleMapper.java b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dao/RoleMapper.java index 3a4ed4a05..ae4d3aa09 100644 --- a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dao/RoleMapper.java +++ b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dao/RoleMapper.java @@ -9,16 +9,11 @@ import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import org.springframework.stereotype.Repository; -import java.util.Collection; import java.util.List; @Repository public interface RoleMapper extends BaseMapper { - default List selectListByIds(Collection ids) { - return selectList(new QueryWrapper().in("id", ids)); - } - default List selectList() { return selectList(new QueryWrapper<>()); } diff --git a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dao/RoleResourceMapper.java b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dao/RoleResourceMapper.java index 05023dbb5..6c31aed5c 100644 --- a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dao/RoleResourceMapper.java +++ b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dao/RoleResourceMapper.java @@ -1,22 +1,38 @@ package cn.iocoder.mall.admin.dao; import cn.iocoder.mall.admin.dataobject.RoleResourceDO; +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 RoleResourceMapper { +public interface RoleResourceMapper extends BaseMapper { - int insertList(@Param("roleResources") List resourceDOs); + /** + * 批量插入。因为 MyBaits Plus 的批量插入是基于 Service 实现,所以只好写 XML + * + * @param roleResources 数组 + */ + int insertList(@Param("roleResources") List roleResources); - List selectByResourceHandler(@Param("resourceHandler") String resourceHandler); + default List selectListByResourceId(Integer resourceId) { + return selectList(new QueryWrapper().eq("resource_id", resourceId)); + } - List selectByResourceId(@Param("resourceId") Integer resourceId); + default List selectListByResourceId(Collection resourceIds) { + return selectList(new QueryWrapper().in("resource_id", resourceIds)); + } - int updateToDeletedByResourceId(@Param("resourceId") Integer resourceId); + default int deleteByResourceId(Integer resourceId) { + return delete(new QueryWrapper().eq("resource_id", resourceId)); + } - int updateToDeletedByRoleId(@Param("roleId") Integer roleId); + default int deleteByRoleId(Integer roleId) { + return delete(new QueryWrapper().eq("role_id", roleId)); + } -} \ No newline at end of file +} diff --git a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/DataDictDO.java b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/DataDictDO.java index b65a853c0..091ed72a8 100644 --- a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/DataDictDO.java +++ b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/DataDictDO.java @@ -1,6 +1,7 @@ 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; @@ -13,6 +14,7 @@ import lombok.experimental.Accessors; * value:1 男 * value:2 女 */ +@TableName("data_dict") @Data @Accessors(chain = true) public class DataDictDO extends DeletableDO { diff --git a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/OAuth2AccessTokenDO.java b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/OAuth2AccessTokenDO.java index 5ef79d8ee..ec87c25c0 100644 --- a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/OAuth2AccessTokenDO.java +++ b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/OAuth2AccessTokenDO.java @@ -1,5 +1,9 @@ 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; @@ -8,22 +12,28 @@ import java.util.Date; /** * OAUTH2 AccessToken */ +@TableName("oauth2_access_token") @Data @Accessors(chain = true) -public class OAuth2AccessTokenDO { +public class OAuth2AccessTokenDO extends BaseDO { /** * 访问令牌 */ + @TableId(type = IdType.INPUT) private String id; /** * 刷新令牌 */ private String refreshToken; /** - * 管理员比那好 + * 用户编号 */ - private Integer adminId; + private Integer userId; + /** + * 用户类型 + */ + private Integer userType; /** * 过期时间 */ @@ -32,63 +42,5 @@ public class OAuth2AccessTokenDO { * 是否有效 */ 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; - } } diff --git a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/OAuth2RefreshTokenDO.java b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/OAuth2RefreshTokenDO.java index 8baddfab5..da1261915 100644 --- a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/OAuth2RefreshTokenDO.java +++ b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/OAuth2RefreshTokenDO.java @@ -1,5 +1,9 @@ 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; @@ -10,18 +14,24 @@ import java.util.Date; * * idx_uid */ +@TableName("oauth2_refresh_token") @Data @Accessors(chain = true) -public class OAuth2RefreshTokenDO { +public class OAuth2RefreshTokenDO extends BaseDO { /** * 刷新令牌 */ + @TableId(type = IdType.INPUT) private String id; /** * 用户编号 */ - private Integer adminId; + private Integer userId; + /** + * 用户类型 + */ + private Integer userType; /** * 是否有效 */ @@ -30,9 +40,5 @@ public class OAuth2RefreshTokenDO { * 过期时间 */ private Date expiresTime; - /** - * 创建时间 - */ - private Date createTime; } diff --git a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/RoleResourceDO.java b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/RoleResourceDO.java index 998c45b49..5a29bad35 100644 --- a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/RoleResourceDO.java +++ b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/dataobject/RoleResourceDO.java @@ -1,12 +1,14 @@ 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 { diff --git a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/AdminServiceImpl.java b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/AdminServiceImpl.java index 5b926d3ad..f14901159 100644 --- a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/AdminServiceImpl.java +++ b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/AdminServiceImpl.java @@ -2,16 +2,20 @@ package cn.iocoder.mall.admin.service; import cn.iocoder.common.framework.constant.CommonStatusEnum; import cn.iocoder.common.framework.constant.DeletedStatusEnum; +import cn.iocoder.common.framework.constant.UserTypeEnum; import cn.iocoder.common.framework.util.CollectionUtil; import cn.iocoder.common.framework.util.ServiceExceptionUtil; -import cn.iocoder.common.framework.vo.CommonResult; import cn.iocoder.common.framework.vo.PageResult; import cn.iocoder.mall.admin.api.AdminService; -import cn.iocoder.mall.admin.api.bo.role.RoleBO; +import cn.iocoder.mall.admin.api.bo.admin.AdminAuthenticationBO; +import cn.iocoder.mall.admin.api.bo.admin.AdminAuthorizationBO; import cn.iocoder.mall.admin.api.bo.admin.AdminBO; +import cn.iocoder.mall.admin.api.bo.oauth2.OAuth2AccessTokenBO; +import cn.iocoder.mall.admin.api.bo.role.RoleBO; import cn.iocoder.mall.admin.api.constant.AdminConstants; import cn.iocoder.mall.admin.api.constant.AdminErrorCodeEnum; import cn.iocoder.mall.admin.api.dto.admin.*; +import cn.iocoder.mall.admin.api.dto.oauth2.OAuth2CreateTokenDTO; import cn.iocoder.mall.admin.convert.AdminConvert; import cn.iocoder.mall.admin.dao.AdminMapper; import cn.iocoder.mall.admin.dao.AdminRoleMapper; @@ -39,32 +43,30 @@ public class AdminServiceImpl implements AdminService { private AdminRoleMapper adminRoleMapper; @Autowired - private OAuth2ServiceImpl oAuth2Service; + private OAuth2ServiceImpl oauth2Service; @Autowired private RoleServiceImpl roleService; - public CommonResult validAdmin(String username, String password) { - AdminDO admin = adminMapper.selectByUsername(username); + @Override + public AdminAuthenticationBO authentication(AdminAuthenticationDTO adminAuthenticationDTO) { + AdminDO admin = adminMapper.selectByUsername(adminAuthenticationDTO.getUsername()); // 账号不存在 if (admin == null) { - return ServiceExceptionUtil.error(AdminErrorCodeEnum.ADMIN_USERNAME_NOT_REGISTERED.getCode()); + throw ServiceExceptionUtil.exception(AdminErrorCodeEnum.ADMIN_USERNAME_NOT_REGISTERED.getCode()); } // 密码不正确 - if (encodePassword(password).equals(admin.getPassword())) { - return ServiceExceptionUtil.error(AdminErrorCodeEnum.ADMIN_PASSWORD_ERROR.getCode()); + if (encodePassword(adminAuthenticationDTO.getPassword()).equals(admin.getPassword())) { + throw ServiceExceptionUtil.exception(AdminErrorCodeEnum.ADMIN_PASSWORD_ERROR.getCode()); } // 账号被禁用 if (CommonStatusEnum.DISABLE.getValue().equals(admin.getStatus())) { - return ServiceExceptionUtil.error(AdminErrorCodeEnum.ADMIN_IS_DISABLE.getCode()); + throw ServiceExceptionUtil.exception(AdminErrorCodeEnum.ADMIN_IS_DISABLE.getCode()); } - // 校验成功,返回管理员。并且,去掉一些非关键字段,考虑安全性。 - admin.setPassword(null); - admin.setStatus(null); - return CommonResult.success(admin); - } - - public List getAdminRoles(Integer adminId) { - return adminRoleMapper.selectByAdminId(adminId); + // 创建 accessToken + OAuth2AccessTokenBO accessTokenBO = oauth2Service.createToken(new OAuth2CreateTokenDTO().setUserId(admin.getId()) + .setUserType(UserTypeEnum.ADMIN.getValue())); + // 转换返回 + return AdminConvert.INSTANCE.convert2(admin).setToken(accessTokenBO); } @Override @@ -130,7 +132,7 @@ public class AdminServiceImpl implements AdminService { adminMapper.updateById(updateAdmin); // 如果是关闭管理员,则标记 token 失效。否则,管理员还可以继续蹦跶 if (CommonStatusEnum.DISABLE.getValue().equals(adminUpdateStatusDTO.getStatus())) { - oAuth2Service.removeToken(adminUpdateStatusDTO.getId()); + oauth2Service.removeToken(adminUpdateStatusDTO.getId()); } // TODO 插入操作日志 // 返回成功 @@ -152,7 +154,7 @@ public class AdminServiceImpl implements AdminService { // 标记删除 AdminDO adminMapper.deleteById(updateAdminId); // 标记删除 // 标记删除 AdminRole - adminRoleMapper.updateToDeletedByAdminId(updateAdminId); + adminRoleMapper.deleteByAdminId(updateAdminId); // TODO 插入操作日志 // 返回成功 return true; @@ -202,7 +204,7 @@ public class AdminServiceImpl implements AdminService { } // TODO 芋艿,这里先简单实现。即方式是,删除老的分配的角色关系,然后添加新的分配的角色关系 // 标记管理员角色源关系都为删除 - adminRoleMapper.updateToDeletedByAdminId(adminAssignRoleDTO.getId()); + adminRoleMapper.deleteByAdminId(adminAssignRoleDTO.getId()); // 创建 RoleResourceDO 数组,并插入到数据库 if (!CollectionUtil.isEmpty(adminAssignRoleDTO.getRoleIds())) { List adminRoleDOs = adminAssignRoleDTO.getRoleIds().stream().map(roleId -> { @@ -218,6 +220,24 @@ public class AdminServiceImpl implements AdminService { return true; } + @Override + public AdminAuthorizationBO checkPermissions(Integer adminId, List permissions) { + // 查询管理员拥有的角色关联数据 + List adminRoleList = adminRoleMapper.selectByAdminId(adminId); + Set adminRoleIds = CollectionUtil.convertSet(adminRoleList, AdminRoleDO::getRoleId); + // 授权校验 + if (!CollectionUtil.isEmpty(permissions)) { + Map> permissionRoleMap = roleService.getPermissionRoleMap(permissions); + for (Map.Entry> entry : permissionRoleMap.entrySet()) { + if (!CollectionUtil.containsAny(entry.getValue(), adminRoleIds)) { // 所以有任一不满足,就验证失败,抛出异常 + throw ServiceExceptionUtil.exception(AdminErrorCodeEnum.ADMIN_INVALID_PERMISSION.getCode()); + } + } + } + // 返回成功 + return new AdminAuthorizationBO().setId(adminId).setRoleIds(adminRoleIds); + } + private String encodePassword(String password) { return DigestUtils.md5DigestAsHex(password.getBytes()); } diff --git a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/DataDictServiceImpl.java b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/DataDictServiceImpl.java index 348d7bbf7..f0f566d24 100644 --- a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/DataDictServiceImpl.java +++ b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/DataDictServiceImpl.java @@ -31,16 +31,16 @@ public class DataDictServiceImpl implements DataDictService { private DataDictMapper dataDictMapper; @Override - public CommonResult> selectDataDictList() { + public List selectDataDictList() { List dataDicts = dataDictMapper.selectList(); - return CommonResult.success(DataDictConvert.INSTANCE.convert(dataDicts)); + return DataDictConvert.INSTANCE.convert(dataDicts); } @Override - public CommonResult addDataDict(Integer adminId, DataDictAddDTO dataDictAddDTO) { + public DataDictBO addDataDict(Integer adminId, DataDictAddDTO dataDictAddDTO) { // 校验数据字典重复 if (dataDictMapper.selectByEnumValueAndValue(dataDictAddDTO.getEnumValue(), dataDictAddDTO.getValue()) != null) { - return ServiceExceptionUtil.error(AdminErrorCodeEnum.DATA_DICT_EXISTS.getCode()); + throw ServiceExceptionUtil.exception(AdminErrorCodeEnum.DATA_DICT_EXISTS.getCode()); } // 保存到数据库 DataDictDO dataDict = DataDictConvert.INSTANCE.convert(dataDictAddDTO); @@ -49,45 +49,43 @@ public class DataDictServiceImpl implements DataDictService { dataDictMapper.insert(dataDict); // TODO 插入操作日志 // 返回成功 - return CommonResult.success(DataDictConvert.INSTANCE.convert(dataDict)); + return DataDictConvert.INSTANCE.convert(dataDict); } @Override - public CommonResult updateDataDict(Integer adminId, DataDictUpdateDTO dataDictUpdateDTO) { + public Boolean updateDataDict(Integer adminId, DataDictUpdateDTO dataDictUpdateDTO) { // 校验数据字典不存在 DataDictDO existsDataDict = dataDictMapper.selectById(dataDictUpdateDTO.getId()); if (existsDataDict == null) { - return ServiceExceptionUtil.error(AdminErrorCodeEnum.DATA_DICT_NOT_EXISTS.getCode()); + throw ServiceExceptionUtil.exception(AdminErrorCodeEnum.DATA_DICT_NOT_EXISTS.getCode()); } // 校验数据字典重复 DataDictDO duplicateDataDict = dataDictMapper.selectByEnumValueAndValue(existsDataDict.getEnumValue(), dataDictUpdateDTO.getValue()); if (duplicateDataDict != null && !duplicateDataDict.getId().equals(dataDictUpdateDTO.getId())) { - return ServiceExceptionUtil.error(AdminErrorCodeEnum.DATA_DICT_EXISTS.getCode()); + throw ServiceExceptionUtil.exception(AdminErrorCodeEnum.DATA_DICT_EXISTS.getCode()); } // 更新到数据库 DataDictDO updateDataDict = DataDictConvert.INSTANCE.convert(dataDictUpdateDTO); - dataDictMapper.update(updateDataDict); + dataDictMapper.updateById(updateDataDict); // TODO 插入操作日志 // 返回成功 - return CommonResult.success(true); + return true; } // 一般情况下,不要删除数据字典。 // 因为,业务数据正在使用该数据字典,删除后,可能有不可预知的问题。 @Override - public CommonResult deleteDataDict(Integer adminId, Integer dataDictId) { + public Boolean deleteDataDict(Integer adminId, Integer dataDictId) { // 校验数据字典不存在 DataDictDO existsDataDict = dataDictMapper.selectById(dataDictId); if (existsDataDict == null) { - return ServiceExceptionUtil.error(AdminErrorCodeEnum.DATA_DICT_NOT_EXISTS.getCode()); + throw ServiceExceptionUtil.exception(AdminErrorCodeEnum.DATA_DICT_NOT_EXISTS.getCode()); } - // 更新到数据库 - DataDictDO updateDataDict = new DataDictDO().setId(dataDictId); - updateDataDict.setDeleted(DeletedStatusEnum.DELETED_YES.getValue()); - dataDictMapper.update(updateDataDict); + // 标记删除 + dataDictMapper.deleteById(dataDictId); // TODO 插入操作日志 // 返回成功 - return CommonResult.success(true); + return true; } @Override @@ -106,7 +104,7 @@ public class DataDictServiceImpl implements DataDictService { @Override public CommonResult> getDataDictList(String dictKey, Collection dictValueList) { - Set convertDictValueList = dictValueList.stream().map(o -> String.valueOf(o)).collect(Collectors.toSet()); + Set convertDictValueList = dictValueList.stream().map(String::valueOf).collect(Collectors.toSet()); List dataDictDOList = dataDictMapper.selectByEnumValueAndValues(dictKey, convertDictValueList); List dataDictBOList = DataDictConvert.INSTANCE.convert(dataDictDOList); return CommonResult.success(dataDictBOList); diff --git a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/OAuth2ServiceImpl.java b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/OAuth2ServiceImpl.java index 7c05febd0..5db47a9c1 100644 --- a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/OAuth2ServiceImpl.java +++ b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/OAuth2ServiceImpl.java @@ -1,24 +1,23 @@ 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.oauth2.OAuth2AccessTokenBO; import cn.iocoder.mall.admin.api.bo.oauth2.OAuth2AuthenticationBO; import cn.iocoder.mall.admin.api.constant.AdminErrorCodeEnum; -import cn.iocoder.mall.admin.api.constant.ResourceConstants; +import cn.iocoder.mall.admin.api.dto.oauth2.OAuth2CreateTokenDTO; +import cn.iocoder.mall.admin.api.dto.oauth2.OAuth2GetTokenDTO; 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 cn.iocoder.mall.admin.dataobject.OAuth2AccessTokenDO; +import cn.iocoder.mall.admin.dataobject.OAuth2RefreshTokenDO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.Date; -import java.util.List; -import java.util.Set; import java.util.UUID; @Service @@ -49,36 +48,15 @@ public class OAuth2ServiceImpl implements OAuth2Service { @Override @Transactional - public CommonResult getAccessToken(String username, String password) { - CommonResult adminResult = adminService.validAdmin(username, password); - // 校验失败,返回错误结果 - if (adminResult.isError()) { - return CommonResult.error(adminResult); - } - AdminDO admin = adminResult.getData(); + public OAuth2AccessTokenBO createToken(OAuth2CreateTokenDTO oauth2CreateTokenDTO) { + Integer userId = oauth2CreateTokenDTO.getUserId(); + Integer userType = oauth2CreateTokenDTO.getUserType(); // 创建刷新令牌 - OAuth2RefreshTokenDO oauth2RefreshTokenDO = createOAuth2RefreshToken(admin.getId()); + OAuth2RefreshTokenDO oauth2RefreshTokenDO = createOAuth2RefreshToken(userId, userType); // 创建访问令牌 - OAuth2AccessTokenDO oauth2AccessTokenDO = createOAuth2AccessToken(admin.getId(), oauth2RefreshTokenDO.getId()); + OAuth2AccessTokenDO oauth2AccessTokenDO = createOAuth2AccessToken(userId, userType, 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)); + return OAuth2Convert.INSTANCE.convertToAccessTokenWithExpiresIn(oauth2AccessTokenDO); } /** @@ -95,40 +73,37 @@ public class OAuth2ServiceImpl implements OAuth2Service { } @Override - public CommonResult checkPermission(Integer adminId, Set roleIds, String url) { - // 如果未配置该资源,说明无需权限控制。 - ResourceDO resource = resourceService.getResourceByTypeAndHandler(ResourceConstants.TYPE_BUTTON, url); - if (resource == null) { - return CommonResult.success(true); + public OAuth2AuthenticationBO getAuthentication(OAuth2GetTokenDTO oauth2GetTokenDTO) { + OAuth2AccessTokenDO accessTokenDO = oauth2AccessTokenMapper.selectById(oauth2GetTokenDTO.getAccessToken()); + if (accessTokenDO == null) { // 不存在 + throw ServiceExceptionUtil.exception(AdminErrorCodeEnum.OAUTH2_INVALID_TOKEN_NOT_FOUND.getCode()); } - // 资源存在,结果无角色,说明没有权限。 - if (roleIds == null || roleIds.isEmpty()) { - return ServiceExceptionUtil.error(AdminErrorCodeEnum.OAUTH_INVALID_PERMISSION.getCode()); + if (accessTokenDO.getExpiresTime().getTime() < System.currentTimeMillis()) { // 已过期 + throw ServiceExceptionUtil.exception(AdminErrorCodeEnum.OAUTH2_INVALID_TOKEN_EXPIRED.getCode()); } - // 校验是否有资源对应的角色,即 RBAC 。 - List roleResourceDOs = roleService.getRoleByResourceId(resource.getId()); - for (RoleResourceDO roleResourceDO : roleResourceDOs) { - if (roleIds.contains(roleResourceDO.getRoleId())) { - return CommonResult.success(true); - } + if (!accessTokenDO.getValid()) { // 无效 + throw ServiceExceptionUtil.exception(AdminErrorCodeEnum.OAUTH2_INVALID_TOKEN_INVALID.getCode()); } - // 没有权限,返回错误 - return ServiceExceptionUtil.error(AdminErrorCodeEnum.OAUTH_INVALID_PERMISSION.getCode()); + if (!oauth2GetTokenDTO.getUserType().equals(accessTokenDO.getUserType())) { + throw ServiceExceptionUtil.exception(AdminErrorCodeEnum.OAUTH2_INVALID_TOKEN_INVALID.getCode()); + } + // 转换返回 + return OAuth2Convert.INSTANCE.convertToAuthentication(accessTokenDO); } - private OAuth2AccessTokenDO createOAuth2AccessToken(Integer adminId, String refreshToken) { + private OAuth2AccessTokenDO createOAuth2AccessToken(Integer userId, Integer userType, String refreshToken) { OAuth2AccessTokenDO accessToken = new OAuth2AccessTokenDO().setId(generateAccessToken()) .setRefreshToken(refreshToken) - .setAdminId(adminId) + .setUserId(userId).setUserType(userType) .setExpiresTime(new Date(System.currentTimeMillis() + accessTokenExpireTimeMillis)) .setValid(true); oauth2AccessTokenMapper.insert(accessToken); return accessToken; } - private OAuth2RefreshTokenDO createOAuth2RefreshToken(Integer adminId) { + private OAuth2RefreshTokenDO createOAuth2RefreshToken(Integer userId, Integer userType) { OAuth2RefreshTokenDO refreshToken = new OAuth2RefreshTokenDO().setId(generateRefreshToken()) - .setAdminId(adminId) + .setUserId(userId).setUserType(userType) .setExpiresTime(new Date(System.currentTimeMillis() + refreshTokenExpireTimeMillis)) .setValid(true); oauth2RefreshTokenMapper.insert(refreshToken); diff --git a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/ResourceServiceImpl.java b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/ResourceServiceImpl.java index 9caad0fa8..659b5bce1 100644 --- a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/ResourceServiceImpl.java +++ b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/ResourceServiceImpl.java @@ -1,8 +1,8 @@ package cn.iocoder.mall.admin.service; import cn.iocoder.common.framework.constant.DeletedStatusEnum; -import cn.iocoder.common.framework.constant.SysErrorCodeEnum; import cn.iocoder.common.framework.util.ServiceExceptionUtil; +import cn.iocoder.common.framework.util.StringUtil; import cn.iocoder.mall.admin.api.ResourceService; import cn.iocoder.mall.admin.api.bo.resource.ResourceBO; import cn.iocoder.mall.admin.api.constant.AdminErrorCodeEnum; @@ -31,8 +31,14 @@ public class ResourceServiceImpl implements ResourceService { @Autowired private RoleResourceMapper roleResourceMapper; - public ResourceDO getResourceByTypeAndHandler(Integer type, String handler) { - return resourceMapper.selectByTypeAndHandler(type, handler); + public List getResourceListByPermission(String permission) { + List resources = resourceMapper.selectListByPermission(permission); + if (resources.isEmpty()) { + return Collections.emptyList(); + } + // 因为 ResourceDO 存储的 permissions 是字符串,使用逗号分隔,需要进一步判断 + resources.removeIf(resourceDO -> !StringUtil.split(resourceDO.getPermissions(), ",").contains(permission)); + return resources; } @Override @@ -49,12 +55,7 @@ public class ResourceServiceImpl implements ResourceService { } @Override - @SuppressWarnings("Duplicates") public ResourceBO addResource(Integer adminId, ResourceAddDTO resourceAddDTO) { - // 补充未在 Validation 中校验的参数校验 - if (!isValidResourceType(resourceAddDTO.getType())) { - throw ServiceExceptionUtil.exception(SysErrorCodeEnum.VALIDATION_REQUEST_PARAM_ERROR.getCode(), "资源类型必须是菜单或 Url"); // TODO 有点搓 - } // 校验父资源存在 checkParentResource(resourceAddDTO.getPid(), null); // 存储到数据库 @@ -69,7 +70,6 @@ public class ResourceServiceImpl implements ResourceService { } @Override - @SuppressWarnings("Duplicates") public Boolean updateResource(Integer adminId, ResourceUpdateDTO resourceUpdateDTO) { // 校验更新的资源是否存在 if (resourceMapper.selectById(resourceUpdateDTO.getId()) == null) { @@ -100,7 +100,7 @@ public class ResourceServiceImpl implements ResourceService { // 更新到数据库 resourceMapper.deleteById(resourceId); // 删除资源关联表 - roleResourceMapper.updateToDeletedByResourceId(resourceId); + roleResourceMapper.deleteByResourceId(resourceId); // 返回成功 return true; } @@ -112,18 +112,6 @@ public class ResourceServiceImpl implements ResourceService { return resourceMapper.selectListByIds(resourceIds); } - private boolean isValidResourceType(Integer type) { - return ResourceConstants.TYPE_MENU.equals(type) - || ResourceConstants.TYPE_BUTTON.equals(type); - } - - private boolean checkParentExists(Integer pid) { - if (!ResourceConstants.PID_ROOT.equals(pid)) { - return resourceMapper.selectById(pid) == null; - } - return false; - } - private void checkParentResource(Integer pid, Integer childId) { if (pid == null || ResourceConstants.PID_ROOT.equals(pid)) { return; diff --git a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/RoleServiceImpl.java b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/RoleServiceImpl.java index bb8048cfa..f43ba6a8c 100644 --- a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/RoleServiceImpl.java +++ b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/RoleServiceImpl.java @@ -19,6 +19,7 @@ import cn.iocoder.mall.admin.dataobject.ResourceDO; import cn.iocoder.mall.admin.dataobject.RoleDO; import cn.iocoder.mall.admin.dataobject.RoleResourceDO; import com.baomidou.mybatisplus.core.metadata.IPage; +import com.google.common.collect.Maps; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -40,12 +41,8 @@ public class RoleServiceImpl implements RoleService { @Autowired private ResourceServiceImpl resourceService; - public List getRoleByResourceHandler(String resourceHandler) { - return roleResourceMapper.selectByResourceHandler(resourceHandler); - } - public List getRoleByResourceId(Integer resourceId) { - return roleResourceMapper.selectByResourceId(resourceId); + return roleResourceMapper.selectListByResourceId(resourceId); } @Override @@ -62,7 +59,7 @@ public class RoleServiceImpl implements RoleService { @Override public List getRoleList(Collection ids) { - List roles = roleMapper.selectListByIds(ids); + List roles = roleMapper.selectBatchIds(ids); return RoleConvert.INSTANCE.convert(roles); } @@ -104,9 +101,9 @@ public class RoleServiceImpl implements RoleService { // 更新到数据库,标记删除 roleMapper.deleteById(roleId); // 标记删除 RoleResource - roleResourceMapper.updateToDeletedByRoleId(roleId); + roleResourceMapper.deleteByRoleId(roleId); // 标记删除 AdminRole - adminRoleMapper.updateToDeletedByRoleId(roleId); + adminRoleMapper.deleteByRoleId(roleId); // TODO 插入操作日志 // 返回成功 return true; @@ -130,7 +127,7 @@ public class RoleServiceImpl implements RoleService { } // TODO 芋艿,这里先简单实现。即方式是,删除老的分配的资源关系,然后添加新的分配的资源关系 // 标记角色原资源关系都为删除 - roleResourceMapper.updateToDeletedByRoleId(roleId); + roleResourceMapper.deleteByRoleId(roleId); // 创建 RoleResourceDO 数组,并插入到数据库 if (!CollectionUtil.isEmpty(resourceIds)) { List roleResources = resourceIds.stream().map(resourceId -> { @@ -150,7 +147,37 @@ public class RoleServiceImpl implements RoleService { if (CollectionUtil.isEmpty(roleIds)) { return Collections.emptyList(); } - return roleMapper.selectListByIds(roleIds); + return roleMapper.selectBatchIds(roleIds); + } + + /** + * 获得权限与角色的映射关系。 + * + * TODO 芋艿,等以后有 redis ,优化成从缓存读取。每个 permission ,哪些角色可以访问 + * + * @param permissions 权限标识数组 + * @return 映射关系。KEY:权限标识;VALUE:角色编号数组 + */ + public Map> getPermissionRoleMap(List permissions) { + if (CollectionUtil.isEmpty(permissions)) { + return Collections.emptyMap(); + } + Map> result = Maps.newHashMapWithExpectedSize(permissions.size()); + for (String permission : permissions) { + List resources = resourceService.getResourceListByPermission(permission); + if (resources.isEmpty()) { // 无需授权 + result.put(permission, Collections.emptyList()); + } else { + List roleResources = roleResourceMapper.selectListByResourceId( + CollectionUtil.convertSet(resources, ResourceDO::getId)); + if (roleResources.isEmpty()) { + result.put(permission, Collections.emptyList()); + } else { + result.put(permission, CollectionUtil.convertList(roleResources, RoleResourceDO::getRoleId)); + } + } + } + return result; } } diff --git a/system/system-service-impl/src/main/resources/mapper/AdminRoleMapper.xml b/system/system-service-impl/src/main/resources/mapper/AdminRoleMapper.xml index 8faa8f3af..c734f858b 100644 --- a/system/system-service-impl/src/main/resources/mapper/AdminRoleMapper.xml +++ b/system/system-service-impl/src/main/resources/mapper/AdminRoleMapper.xml @@ -2,37 +2,6 @@ - - - - - - - - - - - - UPDATE admin_role - SET deleted = 1 - WHERE admin_id = #{adminId} - AND deleted = 0 - - - - UPDATE admin_role - SET deleted = 1 - WHERE role_id = #{roleId} - AND deleted = 0 - - INSERT INTO admin_role ( admin_id, role_id, create_time, deleted @@ -42,4 +11,4 @@ - \ No newline at end of file + diff --git a/system/system-service-impl/src/main/resources/mapper/DataDictMapper.xml b/system/system-service-impl/src/main/resources/mapper/DataDictMapper.xml index e5b588a49..546d268c2 100644 --- a/system/system-service-impl/src/main/resources/mapper/DataDictMapper.xml +++ b/system/system-service-impl/src/main/resources/mapper/DataDictMapper.xml @@ -29,21 +29,6 @@ - - - - - - INSERT INTO data_dict ( - id, enum_value, value, display_name, sort, - memo, create_time, deleted - ) VALUES ( - #{id}, #{enumValue}, #{value}, #{displayName}, #{sort}, - #{memo}, #{createTime}, #{deleted} - ) - - - - UPDATE data_dict - - - enum_value = #{enumValue}, - - - value = #{value}, - - - display_name = #{displayName}, - - - sort = #{sort}, - - - memo = #{memo}, - - - deleted = #{deleted} - - - WHERE id = #{id} - - - \ No newline at end of file + diff --git a/system/system-service-impl/src/main/resources/mapper/OAuth2AccessTokenMapper.xml b/system/system-service-impl/src/main/resources/mapper/OAuth2AccessTokenMapper.xml deleted file mode 100644 index e9b8ce6eb..000000000 --- a/system/system-service-impl/src/main/resources/mapper/OAuth2AccessTokenMapper.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - INSERT INTO oauth2_access_token ( - id, refresh_token, admin_id, valid, expires_time, - create_time - ) VALUES ( - #{id}, #{refreshToken}, #{adminId}, #{valid}, #{expiresTime}, - #{createTime} - ) - - - - - - UPDATE oauth2_access_token - SET valid = 0 - WHERE admin_id = #{adminId} - AND valid = 1 - - - \ No newline at end of file diff --git a/system/system-service-impl/src/main/resources/mapper/OAuth2RefreshTokenMapper.xml b/system/system-service-impl/src/main/resources/mapper/OAuth2RefreshTokenMapper.xml deleted file mode 100644 index 34472899c..000000000 --- a/system/system-service-impl/src/main/resources/mapper/OAuth2RefreshTokenMapper.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - INSERT INTO oauth2_refresh_token ( - id, admin_id, valid, expires_time, create_time - ) VALUES ( - #{id}, #{adminId}, #{valid}, #{expiresTime}, #{createTime} - ) - - - - UPDATE oauth2_refresh_token - SET valid = 0 - WHERE admin_id = #{adminId} - AND valid = 1 - - - \ No newline at end of file diff --git a/system/system-service-impl/src/main/resources/mapper/ResourceMapper.xml b/system/system-service-impl/src/main/resources/mapper/ResourceMapper.xml index 2edd8cf35..11770d79e 100644 --- a/system/system-service-impl/src/main/resources/mapper/ResourceMapper.xml +++ b/system/system-service-impl/src/main/resources/mapper/ResourceMapper.xml @@ -7,31 +7,9 @@ create_time, pid, handler - - - - - - - - diff --git a/system/system-service-impl/src/main/resources/mapper/RoleResourceMapper.xml b/system/system-service-impl/src/main/resources/mapper/RoleResourceMapper.xml index 3f480aab6..88edd5739 100644 --- a/system/system-service-impl/src/main/resources/mapper/RoleResourceMapper.xml +++ b/system/system-service-impl/src/main/resources/mapper/RoleResourceMapper.xml @@ -2,45 +2,6 @@ - - - - - - - - - - - - - - UPDATE role_resource - SET deleted = 1 - WHERE resource_id = #{resourceId} - AND deleted = 0 - - - - UPDATE role_resource - SET deleted = 1 - WHERE role_id = #{roleId} - AND deleted = 0 - - INSERT INTO role_resource ( resource_id, role_id, create_time, deleted @@ -50,4 +11,4 @@ - \ No newline at end of file +