package org.linlinjava.litemall.admin.util; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.linlinjava.litemall.admin.annotation.RequiresPermissionsDesc; public class Permission { private RequiresPermissions requiresPermissions; private RequiresPermissionsDesc requiresPermissionsDesc; private String api; public RequiresPermissions getRequiresPermissions() { return requiresPermissions; } public RequiresPermissionsDesc getRequiresPermissionsDesc() { return requiresPermissionsDesc; } public void setRequiresPermissions(RequiresPermissions requiresPermissions) { this.requiresPermissions = requiresPermissions; } public void setRequiresPermissionsDesc(RequiresPermissionsDesc requiresPermissionsDesc) { this.requiresPermissionsDesc = requiresPermissionsDesc; } public String getApi() { return api; } public void setApi(String api) { this.api = api; } }
package org.linlinjava.litemall.admin.util; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.reflect.MethodUtils; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.linlinjava.litemall.admin.annotation.RequiresPermissionsDesc; import org.linlinjava.litemall.admin.vo.PermVo; import org.springframework.context.ApplicationContext; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.stereotype.Controller; import org.springframework.util.ClassUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import java.lang.reflect.Method; import java.util.*; public class PermissionUtil { public static List<PermVo> listPermVo(List<Permission> permissions) { List<PermVo> root = new ArrayList<>(); for (Permission permission : permissions) { RequiresPermissions requiresPermissions = permission.getRequiresPermissions(); RequiresPermissionsDesc requiresPermissionsDesc = permission.getRequiresPermissionsDesc(); String api = permission.getApi(); String[] menus = requiresPermissionsDesc.menu(); if (menus.length != 2) { throw new RuntimeException("目前只支持两级菜单"); } String menu1 = menus[0]; PermVo perm1 = null; for (PermVo permVo : root) { if (permVo.getLabel().equals(menu1)) { perm1 = permVo; break; } } if (perm1 == null) { perm1 = new PermVo(); perm1.setId(menu1); perm1.setLabel(menu1); perm1.setChildren(new ArrayList<>()); root.add(perm1); } String menu2 = menus[1]; PermVo perm2 = null; for (PermVo permVo : perm1.getChildren()) { if (permVo.getLabel().equals(menu2)) { perm2 = permVo; break; } } if (perm2 == null) { perm2 = new PermVo(); perm2.setId(menu2); perm2.setLabel(menu2); perm2.setChildren(new ArrayList<>()); perm1.getChildren().add(perm2); } String button = requiresPermissionsDesc.button(); PermVo leftPerm = null; for (PermVo permVo : perm2.getChildren()) { if (permVo.getLabel().equals(button)) { leftPerm = permVo; break; } } if (leftPerm == null) { leftPerm = new PermVo(); leftPerm.setId(requiresPermissions.value()[0]); leftPerm.setLabel(requiresPermissionsDesc.button()); leftPerm.setApi(api); perm2.getChildren().add(leftPerm); } else { // TODO // 目前限制Controller里面每个方法的RequiresPermissionsDesc注解是唯一的 // 如果允许相同,可能会造成内部权限不一致。 throw new RuntimeException("权限已经存在,不能添加新权限"); } } return root; } public static List<Permission> listPermission(ApplicationContext context, String basicPackage) { Map<String, Object> map = context.getBeansWithAnnotation(Controller.class); List<Permission> permissions = new ArrayList<>(); for (Map.Entry<String, Object> entry : map.entrySet()) { Object bean = entry.getValue(); if (!StringUtils.contains(ClassUtils.getPackageName(bean.getClass()), basicPackage)) { continue; } Class<?> clz = bean.getClass(); Class controllerClz = clz.getSuperclass(); RequestMapping clazzRequestMapping = AnnotationUtils.findAnnotation(controllerClz, RequestMapping.class); List<Method> methods = MethodUtils.getMethodsListWithAnnotation(controllerClz, RequiresPermissions.class); for (Method method : methods) { RequiresPermissions requiresPermissions = AnnotationUtils.getAnnotation(method, RequiresPermissions.class); RequiresPermissionsDesc requiresPermissionsDesc = AnnotationUtils.getAnnotation(method, RequiresPermissionsDesc.class); if (requiresPermissions == null || requiresPermissionsDesc == null) { continue; } String api = ""; if (clazzRequestMapping != null) { api = clazzRequestMapping.value()[0]; } PostMapping postMapping = AnnotationUtils.getAnnotation(method, PostMapping.class); if (postMapping != null) { api = "POST " + api + postMapping.value()[0]; Permission permission = new Permission(); permission.setRequiresPermissions(requiresPermissions); permission.setRequiresPermissionsDesc(requiresPermissionsDesc); permission.setApi(api); permissions.add(permission); continue; } GetMapping getMapping = AnnotationUtils.getAnnotation(method, GetMapping.class); if (getMapping != null) { api = "GET " + api + getMapping.value()[0]; Permission permission = new Permission(); permission.setRequiresPermissions(requiresPermissions); permission.setRequiresPermissionsDesc(requiresPermissionsDesc); permission.setApi(api); permissions.add(permission); continue; } // TODO // 这里只支持GetMapping注解或者PostMapping注解,应该进一步提供灵活性 throw new RuntimeException("目前权限管理应该在method的前面使用GetMapping注解或者PostMapping注解"); } } return permissions; } public static Set<String> listPermissionString(List<Permission> permissions) { Set<String> permissionsString = new HashSet<>(); for (Permission permission : permissions) { permissionsString.add(permission.getRequiresPermissions().value()[0]); } return permissionsString; } }
package org.linlinjava.litemall.admin.vo; import java.util.List; public class PermVo { private String id; private String label; private String api; private List<PermVo> children; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getLabel() { return label; } public void setLabel(String label) { this.label = label; } public void setApi(String api) { this.api = api; } public String getApi() { return api; } public List<PermVo> getChildren() { return children; } public void setChildren(List<PermVo> children) { this.children = children; } }
package org.linlinjava.litemall.admin.web; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.apache.shiro.subject.Subject; import org.linlinjava.litemall.admin.annotation.RequiresPermissionsDesc; import org.linlinjava.litemall.admin.service.LogHelper; import org.linlinjava.litemall.core.util.RegexUtil; import org.linlinjava.litemall.core.util.ResponseUtil; import org.linlinjava.litemall.core.util.bcrypt.BCryptPasswordEncoder; import org.linlinjava.litemall.core.validator.Order; import org.linlinjava.litemall.core.validator.Sort; import org.linlinjava.litemall.db.domain.LitemallAdmin; import org.linlinjava.litemall.db.service.LitemallAdminService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.StringUtils; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.validation.constraints.NotNull; import java.util.List; import static org.linlinjava.litemall.admin.util.AdminResponseCode.*; @RestController @RequestMapping("/admin/admin") @Validated public class AdminAdminController { private final Log logger = LogFactory.getLog(AdminAdminController.class); @Autowired private LitemallAdminService adminService; @Autowired private LogHelper logHelper; @RequiresPermissions("admin:admin:list") @RequiresPermissionsDesc(menu = {"系统管理", "管理员管理"}, button = "查询") @GetMapping("/list") public Object list(String username, @RequestParam(defaultValue = "1") Integer page, @RequestParam(defaultValue = "10") Integer limit, @Sort @RequestParam(defaultValue = "add_time") String sort, @Order @RequestParam(defaultValue = "desc") String order) { List<LitemallAdmin> adminList = adminService.querySelective(username, page, limit, sort, order); return ResponseUtil.okList(adminList); } private Object validate(LitemallAdmin admin) { String name = admin.getUsername(); if (StringUtils.isEmpty(name)) { return ResponseUtil.badArgument(); } if (!RegexUtil.isUsername(name)) { return ResponseUtil.fail(ADMIN_INVALID_NAME, "管理员名称不符合规定"); } String password = admin.getPassword(); if (StringUtils.isEmpty(password) || password.length() < 6) { return ResponseUtil.fail(ADMIN_INVALID_PASSWORD, "管理员密码长度不能小于6"); } return null; } @RequiresPermissions("admin:admin:create") @RequiresPermissionsDesc(menu = {"系统管理", "管理员管理"}, button = "添加") @PostMapping("/create") public Object create(@RequestBody LitemallAdmin admin) { Object error = validate(admin); if (error != null) { return error; } String username = admin.getUsername(); List<LitemallAdmin> adminList = adminService.findAdmin(username); if (adminList.size() > 0) { return ResponseUtil.fail(ADMIN_NAME_EXIST, "管理员已经存在"); } String rawPassword = admin.getPassword(); BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); String encodedPassword = encoder.encode(rawPassword); admin.setPassword(encodedPassword); adminService.add(admin); logHelper.logAuthSucceed("添加管理员", username); return ResponseUtil.ok(admin); } @RequiresPermissions("admin:admin:read") @RequiresPermissionsDesc(menu = {"系统管理", "管理员管理"}, button = "详情") @GetMapping("/read") public Object read(@NotNull Integer id) { LitemallAdmin admin = adminService.findById(id); return ResponseUtil.ok(admin); } @RequiresPermissions("admin:admin:update") @RequiresPermissionsDesc(menu = {"系统管理", "管理员管理"}, button = "编辑") @PostMapping("/update") public Object update(@RequestBody LitemallAdmin admin) { Object error = validate(admin); if (error != null) { return error; } Integer anotherAdminId = admin.getId(); if (anotherAdminId == null) { return ResponseUtil.badArgument(); } // 不允许管理员通过编辑接口修改密码 admin.setPassword(null); if (adminService.updateById(admin) == 0) { return ResponseUtil.updatedDataFailed(); } logHelper.logAuthSucceed("编辑管理员", admin.getUsername()); return ResponseUtil.ok(admin); } @RequiresPermissions("admin:admin:delete") @RequiresPermissionsDesc(menu = {"系统管理", "管理员管理"}, button = "删除") @PostMapping("/delete") public Object delete(@RequestBody LitemallAdmin admin) { Integer anotherAdminId = admin.getId(); if (anotherAdminId == null) { return ResponseUtil.badArgument(); } // 管理员不能删除自身账号 Subject currentUser = SecurityUtils.getSubject(); LitemallAdmin currentAdmin = (LitemallAdmin) currentUser.getPrincipal(); if (currentAdmin.getId().equals(anotherAdminId)) { return ResponseUtil.fail(ADMIN_DELETE_NOT_ALLOWED, "管理员不能删除自己账号"); } adminService.deleteById(anotherAdminId); logHelper.logAuthSucceed("删除管理员", admin.getUsername()); return ResponseUtil.ok(); } }
package org.linlinjava.litemall.admin.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface RequiresPermissionsDesc { String[] menu(); String button(); }