Browse Source

SpringBoot中引入SpEL,优雅搞定复杂权限控制!

pull/2/head
程序员蜗牛 1 year ago
parent
commit
e2d64d5e6a
  1. 33
      SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/.gitignore
  2. 98
      SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/pom.xml
  3. 15
      SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/SampleApplication.java
  4. 91
      SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/utils/ClassUtil.java
  5. 85
      SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/www/AopService.java
  6. 124
      SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/www/AuthFun.java
  7. 28
      SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/www/PreAuth.java
  8. 26
      SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/www/PreController.java
  9. 5
      SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/www/ResultCode.java
  10. 8
      SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/www/RoleConstant.java
  11. 14
      SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/www/SecureException.java
  12. 11
      SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/java/com/springboot/sample/www/UserVO.java
  13. 10
      SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/src/main/resources/application.yml

33
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/

98
SpringBoot中引入SpEL,优雅搞定复杂权限控制!/speldemo/sample/pom.xml

@ -0,0 +1,98 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.springboot.sample</groupId>
<artifactId>sample</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>sample</name>
<description>Spring Boot Sample</description>
<packaging>jar</packaging>
<properties>
<java.version>1.8</java.version>
<spring.boot.version>2.1.15.RELEASE</spring.boot.version>
<spring.cloud.alibaba.version>2.1.2.RELEASE</spring.cloud.alibaba.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring.boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>${spring.boot.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.16</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

15
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);
}
}

91
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 <A> 泛型标记
* @return {Annotation}
*/
public static <A extends Annotation> A getAnnotation(Method method, Class<A> 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 <A> 泛型标记
* @return {Annotation}
*/
public static <A extends Annotation> A getAnnotation(HandlerMethod handlerMethod, Class<A> annotationType) {
// 先找方法,再找方法上的类
A annotation = handlerMethod.getMethodAnnotation(annotationType);
if (null != annotation) {
return annotation;
}
// 获取类上面的Annotation,可能包含组合注解,故采用spring的工具类
Class<?> beanType = handlerMethod.getBeanType();
return AnnotatedElementUtils.findMergedAnnotation(beanType, annotationType);
}
}

85
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;
}
}

124
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<String> roles = Arrays.stream(userRole.split(",")).collect(Collectors.toList());
for (String r : role) {
if (CollectionUtil.contains(roles, r)) {
return true;
}
}
return false;
}
}

28
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();
}

26
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;
}
}

5
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 {
}

8
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";
}

14
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() {
}
}

11
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;
}

10
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
Loading…
Cancel
Save