|
@@ -0,0 +1,111 @@
|
|
|
|
+package com.chelvc.framework.security.interceptor;
|
|
|
|
+
|
|
|
|
+import java.lang.reflect.Method;
|
|
|
|
+import java.util.Collections;
|
|
|
|
+import java.util.List;
|
|
|
|
+import java.util.Set;
|
|
|
|
+import java.util.stream.Collectors;
|
|
|
|
+import java.util.stream.Stream;
|
|
|
|
+import javax.servlet.http.HttpServletRequest;
|
|
|
|
+import javax.servlet.http.HttpServletResponse;
|
|
|
|
+
|
|
|
|
+import com.chelvc.framework.base.context.ApplicationContextHolder;
|
|
|
|
+import com.chelvc.framework.base.context.SessionContextHolder;
|
|
|
|
+import com.chelvc.framework.common.exception.FrameworkException;
|
|
|
|
+import com.chelvc.framework.common.util.AssertUtils;
|
|
|
|
+import com.chelvc.framework.common.util.ObjectUtils;
|
|
|
|
+import com.chelvc.framework.common.util.StringUtils;
|
|
|
|
+import com.chelvc.framework.security.annotation.Permission;
|
|
|
|
+import com.google.common.collect.Sets;
|
|
|
|
+import lombok.RequiredArgsConstructor;
|
|
|
|
+import org.springframework.aop.framework.AopProxyUtils;
|
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
+import org.springframework.boot.ApplicationArguments;
|
|
|
|
+import org.springframework.boot.ApplicationRunner;
|
|
|
|
+import org.springframework.context.ApplicationContext;
|
|
|
|
+import org.springframework.core.Ordered;
|
|
|
|
+import org.springframework.http.HttpStatus;
|
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
|
+import org.springframework.web.method.HandlerMethod;
|
|
|
|
+import org.springframework.web.servlet.HandlerInterceptor;
|
|
|
|
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
|
|
|
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * 自定义权限校验拦截器
|
|
|
|
+ *
|
|
|
|
+ * @author Woody
|
|
|
|
+ * @date 2024/5/19
|
|
|
|
+ */
|
|
|
|
+@Component
|
|
|
|
+@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
|
|
|
+public class PermissionValidateInterceptor implements ApplicationRunner, HandlerInterceptor, WebMvcConfigurer {
|
|
|
|
+ private final ApplicationContext applicationContext;
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 获取接口权限注解实例
|
|
|
|
+ *
|
|
|
|
+ * @param handler 接口处理器实例
|
|
|
|
+ * @return 接口权限注解实例
|
|
|
|
+ */
|
|
|
|
+ private Permission getPermissionAnnotation(Object handler) {
|
|
|
|
+ if (!(handler instanceof HandlerMethod)) {
|
|
|
|
+ return null;
|
|
|
|
+ }
|
|
|
|
+ HandlerMethod method = (HandlerMethod) handler;
|
|
|
|
+ Permission annotation = method.getMethodAnnotation(Permission.class);
|
|
|
|
+ if (annotation == null) {
|
|
|
|
+ return method.getMethod().getDeclaringClass().getAnnotation(Permission.class);
|
|
|
|
+ }
|
|
|
|
+ return annotation;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void run(ApplicationArguments args) throws Exception {
|
|
|
|
+ // 检查权限标识是否重复
|
|
|
|
+ List<Object> controllers = ApplicationContextHolder.lookupControllers(this.applicationContext);
|
|
|
|
+ if (ObjectUtils.isEmpty(controllers)) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ Set<String> permissions = Sets.newHashSet();
|
|
|
|
+ controllers.forEach(controller -> {
|
|
|
|
+ Class<?> clazz = AopProxyUtils.ultimateTargetClass(controller);
|
|
|
|
+ for (Method method : clazz.getDeclaredMethods()) {
|
|
|
|
+ Permission annotation = method.getAnnotation(Permission.class);
|
|
|
|
+ if ((annotation == null && (annotation = clazz.getAnnotation(Permission.class)) == null)
|
|
|
|
+ || !annotation.enabled()) {
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+ String id = StringUtils.ifEmpty(annotation.id(), method::getName);
|
|
|
|
+ AssertUtils.check(permissions.add(id), () -> "Permission id duplicated: " + id);
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
|
+ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
|
|
|
|
+ throws Exception {
|
|
|
|
+ Permission annotation = this.getPermissionAnnotation(handler);
|
|
|
|
+ if (annotation == null || !annotation.enabled()) {
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+ Set<String> authorities = SessionContextHolder.getSession().getAuthorities();
|
|
|
|
+ Set<String> permissions = ObjectUtils.isEmpty(authorities) ? Collections.emptySet() :
|
|
|
|
+ authorities.stream().flatMap(authority -> {
|
|
|
|
+ List<String> ids = ApplicationContextHolder.getSafeProperty(authority, List.class);
|
|
|
|
+ return ObjectUtils.isEmpty(ids) ? Stream.empty() : ids.stream();
|
|
|
|
+ }).collect(Collectors.toSet());
|
|
|
|
+ String id = StringUtils.ifEmpty(annotation.id(), () -> ((HandlerMethod) handler).getMethod().getName());
|
|
|
|
+ if (ObjectUtils.isEmpty(permissions) || !permissions.contains(id)) {
|
|
|
|
+ throw new FrameworkException(HttpStatus.FORBIDDEN.name(), null,
|
|
|
|
+ ApplicationContextHolder.getMessage("Forbidden"));
|
|
|
|
+ }
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void addInterceptors(InterceptorRegistry registry) {
|
|
|
|
+ registry.addInterceptor(this).addPathPatterns("/**").order(Ordered.HIGHEST_PRECEDENCE + 1);
|
|
|
|
+ }
|
|
|
|
+}
|