From e2d64d5e6afd94f12a2ce7f0bfe151bc53b36778 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E5=91=98=E8=9C=97=E7=89=9B?= <1003271829@qq.com> Date: Sat, 20 Jan 2024 12:15:23 +0800 Subject: [PATCH] =?UTF-8?q?SpringBoot=E4=B8=AD=E5=BC=95=E5=85=A5SpEL?= =?UTF-8?q?=EF=BC=8C=E4=BC=98=E9=9B=85=E6=90=9E=E5=AE=9A=E5=A4=8D=E6=9D=82?= =?UTF-8?q?=E6=9D=83=E9=99=90=E6=8E=A7=E5=88=B6=EF=BC=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../speldemo/.gitignore | 33 +++++ .../speldemo/sample/pom.xml | 98 ++++++++++++++ .../springboot/sample/SampleApplication.java | 15 +++ .../springboot/sample/utils/ClassUtil.java | 91 +++++++++++++ .../com/springboot/sample/www/AopService.java | 85 ++++++++++++ .../com/springboot/sample/www/AuthFun.java | 124 ++++++++++++++++++ .../com/springboot/sample/www/PreAuth.java | 28 ++++ .../springboot/sample/www/PreController.java | 26 ++++ .../com/springboot/sample/www/ResultCode.java | 5 + .../springboot/sample/www/RoleConstant.java | 8 ++ .../sample/www/SecureException.java | 14 ++ .../com/springboot/sample/www/UserVO.java | 11 ++ .../sample/src/main/resources/application.yml | 10 ++ 13 files changed, 548 insertions(+) create mode 100644 SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/.gitignore create mode 100644 SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/pom.xml create mode 100644 SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/SampleApplication.java create mode 100644 SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/utils/ClassUtil.java create mode 100644 SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/www/AopService.java create mode 100644 SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/www/AuthFun.java create mode 100644 SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/www/PreAuth.java create mode 100644 SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/www/PreController.java create mode 100644 SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/www/ResultCode.java create mode 100644 SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/www/RoleConstant.java create mode 100644 SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/www/SecureException.java create mode 100644 SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/www/UserVO.java create mode 100644 SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/resources/application.yml diff --git a/SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/.gitignore b/SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/pom.xml b/SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/pom.xml new file mode 100644 index 0000000..596369e --- /dev/null +++ b/SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/pom.xml @@ -0,0 +1,98 @@ + + + 4.0.0 + com.springboot.sample + sample + 0.0.1-SNAPSHOT + sample + Spring Boot Sample + jar + + + 1.8 + 2.1.15.RELEASE + 2.1.2.RELEASE + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring.boot.version} + pom + import + + + org.springframework.boot + spring-boot-starter-web + ${spring.boot.version} + + + org.springframework.boot + spring-boot-starter-test + ${spring.boot.version} + + + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + org.springframework.boot + spring-boot-devtools + runtime + true + + + + org.aspectj + aspectjrt + + + + + commons-lang + commons-lang + 2.6 + + + + cn.hutool + hutool-all + 5.8.16 + + + + org.aspectj + aspectjweaver + + + + org.projectlombok + lombok + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/SampleApplication.java b/SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/SampleApplication.java new file mode 100644 index 0000000..4ba1ecc --- /dev/null +++ b/SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/SampleApplication.java @@ -0,0 +1,15 @@ +package com.springboot.sample; + + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + + +@SpringBootApplication +public class SampleApplication { + + public static void main(String[] args) { + SpringApplication.run(SampleApplication.class, args); + } + +} diff --git a/SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/utils/ClassUtil.java b/SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/utils/ClassUtil.java new file mode 100644 index 0000000..088f898 --- /dev/null +++ b/SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/utils/ClassUtil.java @@ -0,0 +1,91 @@ +package com.springboot.sample.utils; + +import org.springframework.core.BridgeMethodResolver; +import org.springframework.core.DefaultParameterNameDiscoverer; +import org.springframework.core.MethodParameter; +import org.springframework.core.ParameterNameDiscoverer; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.SynthesizingMethodParameter; +import org.springframework.web.method.HandlerMethod; +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +/** + * 类工具类 + */ +public class ClassUtil extends org.springframework.util.ClassUtils { + + private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer(); + + /** + * 获取方法参数信息 + * + * @param constructor 构造器 + * @param parameterIndex 参数序号 + * @return {MethodParameter} + */ + public static MethodParameter getMethodParameter(Constructor constructor, int parameterIndex) { + MethodParameter methodParameter = new SynthesizingMethodParameter(constructor, parameterIndex); + methodParameter.initParameterNameDiscovery(PARAMETER_NAME_DISCOVERER); + return methodParameter; + } + + /** + * 获取方法参数信息 + * + * @param method 方法 + * @param parameterIndex 参数序号 + * @return {MethodParameter} + */ + public static MethodParameter getMethodParameter(Method method, int parameterIndex) { + MethodParameter methodParameter = new SynthesizingMethodParameter(method, parameterIndex); + methodParameter.initParameterNameDiscovery(PARAMETER_NAME_DISCOVERER); + return methodParameter; + } + + /** + * 获取Annotation + * + * @param method Method + * @param annotationType 注解类 + * @param 泛型标记 + * @return {Annotation} + */ + public static A getAnnotation(Method method, Class annotationType) { + Class targetClass = method.getDeclaringClass(); + // The method may be on an interface, but we need attributes from the target class. + // If the target class is null, the method will be unchanged. + Method specificMethod = ClassUtil.getMostSpecificMethod(method, targetClass); + // If we are dealing with method with generic parameters, find the original method. + specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod); + // 先找方法,再找方法上的类 + A annotation = AnnotatedElementUtils.findMergedAnnotation(specificMethod, annotationType); + ; + if (null != annotation) { + return annotation; + } + // 获取类上面的Annotation,可能包含组合注解,故采用spring的工具类 + return AnnotatedElementUtils.findMergedAnnotation(specificMethod.getDeclaringClass(), annotationType); + } + + /** + * 获取Annotation + * + * @param handlerMethod HandlerMethod + * @param annotationType 注解类 + * @param 泛型标记 + * @return {Annotation} + */ + public static A getAnnotation(HandlerMethod handlerMethod, Class annotationType) { + // 先找方法,再找方法上的类 + A annotation = handlerMethod.getMethodAnnotation(annotationType); + if (null != annotation) { + return annotation; + } + // 获取类上面的Annotation,可能包含组合注解,故采用spring的工具类 + Class beanType = handlerMethod.getBeanType(); + return AnnotatedElementUtils.findMergedAnnotation(beanType, annotationType); + } + +} diff --git a/SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/www/AopService.java b/SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/www/AopService.java new file mode 100644 index 0000000..f5e6ffe --- /dev/null +++ b/SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/www/AopService.java @@ -0,0 +1,85 @@ +package com.springboot.sample.www; + +import com.springboot.sample.utils.ClassUtil; +import org.apache.commons.lang.StringUtils; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.context.expression.BeanFactoryResolver; +import org.springframework.core.MethodParameter; +import org.springframework.expression.Expression; +import org.springframework.expression.ExpressionParser; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Method; + +@Aspect +@Component +public class AopService { + + @Autowired + private ApplicationContext applicationContext; + + private static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser(); + + //切点 + @Pointcut("@annotation(com.springboot.sample.www.PreAuth) || @within(com.springboot.sample.www.PreAuth)") + private void cutMethod() { + + } + + @Around("cutMethod()") + public Object preAuth(ProceedingJoinPoint point) throws Throwable { + if (handleAuth(point)) { + return point.proceed(); + } + throw new SecureException("200"); + } + + private boolean handleAuth(ProceedingJoinPoint point) { + MethodSignature ms = point.getSignature() instanceof MethodSignature ? (MethodSignature) point.getSignature() : null; + Method method = ms.getMethod(); + // 读取权限注解,优先方法上,没有则读取类 + PreAuth preAuth = ClassUtil.getAnnotation(method, PreAuth.class);//hasRole('admin') -》 hasRole(入参) + // 判断表达式 + String condition = preAuth.value(); + if (StringUtils.isNotBlank(condition)) { + Expression expression = EXPRESSION_PARSER.parseExpression(condition); + // 方法参数值 + Object[] args = point.getArgs(); + StandardEvaluationContext context = getEvaluationContext(method, args); + //获取解析计算的结果 + return expression.getValue(context, Boolean.class); + } + return false; + } + + /** + * 获取方法上的参数 + * + * @param method 方法 + * @param args 变量 + * @return {SimpleEvaluationContext} + */ + private StandardEvaluationContext getEvaluationContext(Method method, Object[] args) { + // 初始化Spel表达式上下文,并设置 AuthFun + StandardEvaluationContext context = new StandardEvaluationContext(new AuthFun()); + // 设置表达式支持spring bean + context.setBeanResolver(new BeanFactoryResolver(applicationContext)); + for (int i = 0; i < args.length; i++) { + // 读取方法参数 + MethodParameter methodParam = ClassUtil.getMethodParameter(method, i); + // 设置方法 参数名和值 为spel变量 + context.setVariable(methodParam.getParameterName(), args[i]); + } + return context; + } + + +} diff --git a/SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/www/AuthFun.java b/SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/www/AuthFun.java new file mode 100644 index 0000000..72723ac --- /dev/null +++ b/SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/www/AuthFun.java @@ -0,0 +1,124 @@ +package com.springboot.sample.www; + +import cn.hutool.core.collection.CollectionUtil; +import org.apache.commons.lang.StringUtils; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class AuthFun { + + + /** + * 判断角色是否具有接口权限 + * + * @return {boolean} + */ + public boolean permissionAll() { + //TODO + return true; + } + + /** + * 判断角色是否具有接口权限 + * + * @param permission 权限编号,对应菜单的MENU_CODE + * @return {boolean} + */ + public boolean hasPermission(String permission) { + //TODO + return true; + } + + /** + * 放行所有请求 + * + * @return {boolean} + */ + public boolean permitAll() { + return true; + } + + /** + * 只有超管角色才可访问 + * + * @return {boolean} + */ + public boolean denyAll() { + return hasRole(RoleConstant.ADMIN); + } + + /** + * 是否已授权 + * + * @return {boolean} + */ + public boolean hasAuth() { + return true; + } + + /** + * 是否有时间授权 + * + * @param start 开始时间 + * @param end 结束时间 + * @return {boolean} + */ + public boolean hasTimeAuth(Integer start, Integer end) { + return true; + + } + + /** + * 判断是否有该角色权限 + * + * @param role 单角色 + * @return {boolean} + */ + public boolean hasRole(String role) { + return hasAnyRole(role); + } + + /** + * 判断是否具有所有角色权限 + * + * @param role 角色集合 + * @return {boolean} + */ + public boolean hasAllRole(String... role) { + for (String r : role) { + if (!hasRole(r)) { + return false; + } + } + return true; + } + + /** + * 判断是否有该角色权限 + * @param role 角色集合 + * @return {boolean} + */ + public boolean hasAnyRole(String... role) { + //获取当前登录用户 这边模拟当前用户是管理员 + UserVO user = new UserVO(); + user.setRoleName("admin,projectManager"); + + String userRole = user.getRoleName(); + if (StringUtils.isBlank(userRole)) { + return false; + } + List roles = Arrays.stream(userRole.split(",")).collect(Collectors.toList()); + for (String r : role) { + if (CollectionUtil.contains(roles, r)) { + return true; + } + } + return false; + + } + + + +} \ No newline at end of file diff --git a/SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/www/PreAuth.java b/SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/www/PreAuth.java new file mode 100644 index 0000000..9a3993a --- /dev/null +++ b/SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/www/PreAuth.java @@ -0,0 +1,28 @@ +package com.springboot.sample.www; + + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface PreAuth { + + /** + * + * permissionAll()-----只要配置了角色就可以访问 + * hasPermission("MENU.QUERY")-----有MENU.QUERY操作权限的角色可以访问 + * permitAll()-----放行所有请求 + * denyAll()-----只有超级管理员角色才可访问 + * hasAuth()-----只有登录后才可访问 + * hasTimeAuth(1,10)-----只有在1-10点间访问 + * hasRole(‘管理员’)-----具有管理员角色的人才能访问 + * hasAllRole(‘管理员’,'总工程师')-----同时具有管理员、总工程师角色的人才能访问 + * Spring el + * + */ + String value(); + +} \ No newline at end of file diff --git a/SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/www/PreController.java b/SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/www/PreController.java new file mode 100644 index 0000000..7983c48 --- /dev/null +++ b/SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/www/PreController.java @@ -0,0 +1,26 @@ +package com.springboot.sample.www; + + +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + + +@RestController +@RequestMapping("/pre") +@Slf4j +//@PreAuth(value = "hasRole('admin')") +public class PreController { + + /** + * SpringBoot中引入SpEL,优雅搞定复杂权限控制! + * @param userId + * @return + */ + @GetMapping("/{userId}") + @ResponseBody + @PreAuth(value = "hasRole('admin')") + public String getUser(@PathVariable String userId) { + log.info("hahaha"); + return userId; + } +} diff --git a/SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/www/ResultCode.java b/SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/www/ResultCode.java new file mode 100644 index 0000000..a810d6d --- /dev/null +++ b/SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/www/ResultCode.java @@ -0,0 +1,5 @@ +package com.springboot.sample.www; + + +public enum ResultCode { +} diff --git a/SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/www/RoleConstant.java b/SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/www/RoleConstant.java new file mode 100644 index 0000000..5ef0e6b --- /dev/null +++ b/SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/www/RoleConstant.java @@ -0,0 +1,8 @@ +package com.springboot.sample.www; +public class RoleConstant { + + + public static final String ADMIN = "admin"; + + +} diff --git a/SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/www/SecureException.java b/SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/www/SecureException.java new file mode 100644 index 0000000..dd74566 --- /dev/null +++ b/SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/www/SecureException.java @@ -0,0 +1,14 @@ +package com.springboot.sample.www; + +public class SecureException extends RuntimeException { + + private String code; + + public SecureException(String code) { + this.code = code; + } + + public SecureException() { + + } +} diff --git a/SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/www/UserVO.java b/SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/www/UserVO.java new file mode 100644 index 0000000..60da13b --- /dev/null +++ b/SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/www/UserVO.java @@ -0,0 +1,11 @@ +package com.springboot.sample.www; + +import lombok.Data; + +@Data +public class UserVO { + private String userId; + + // 拥有的角色名 + private String roleName; +} diff --git a/SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/resources/application.yml b/SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/resources/application.yml new file mode 100644 index 0000000..aa47760 --- /dev/null +++ b/SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/resources/application.yml @@ -0,0 +1,10 @@ +server: + port: 8080 + +spring: + jackson: + date-format: yyyy-MM-dd HH:mm:ss + time-zone: GMT+8 + serialization: + write-dates-as-timestamps: false +