From 27489ce1a2b0745dc70234b5b39720a7bf591007 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=86=B7=E5=86=B7?= Date: Wed, 14 Oct 2020 15:37:48 +0800 Subject: [PATCH] =?UTF-8?q?:recycle:=20Refactoring=20code.=20#1920=20@Inne?= =?UTF-8?q?r=20=E5=86=99=E5=9C=A8=E7=B1=BB=E7=BA=A7=E5=88=AB=E6=97=A0?= =?UTF-8?q?=E6=95=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../component/PermitAllUrlProperties.java | 84 ---------- .../component/PermitAllUrlResolver.java | 148 ++++++++++++++++++ .../component/PigxBearerTokenExtractor.java | 20 ++- ...xLocalResourceServerConfigurerAdapter.java | 5 +- .../PigxResourceServerConfigurerAdapter.java | 5 +- .../component/PigxSecurityInnerAspect.java | 9 +- 6 files changed, 178 insertions(+), 93 deletions(-) delete mode 100644 pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PermitAllUrlProperties.java create mode 100644 pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PermitAllUrlResolver.java diff --git a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PermitAllUrlProperties.java b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PermitAllUrlProperties.java deleted file mode 100644 index 29b37a658..000000000 --- a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PermitAllUrlProperties.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2018-2025, lengleng All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * Neither the name of the pig4cloud.com developer nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * Author: lengleng (wangiegie@gmail.com) - */ - -package com.pig4cloud.pigx.common.security.component; - -import cn.hutool.core.util.ReUtil; -import com.pig4cloud.pigx.common.security.annotation.Inner; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.annotation.AnnotationUtils; -import org.springframework.web.context.WebApplicationContext; -import org.springframework.web.method.HandlerMethod; -import org.springframework.web.servlet.mvc.method.RequestMappingInfo; -import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.regex.Pattern; - -/** - * @author lengleng - * @date 2019/4/13 - *

- * 资源服务器对外直接暴露URL,如果设置contex-path 要特殊处理 - */ -@Slf4j -@Configuration -@RequiredArgsConstructor -@ConditionalOnExpression("!'${security.oauth2.client.ignore-urls}'.isEmpty()") -@ConfigurationProperties(prefix = "security.oauth2.client") -public class PermitAllUrlProperties implements InitializingBean { - - private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}"); - - private final WebApplicationContext applicationContext; - - @Getter - @Setter - private List ignoreUrls = new ArrayList<>(); - - @Override - public void afterPropertiesSet() { - RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class); - Map map = mapping.getHandlerMethods(); - - map.keySet().forEach(info -> { - HandlerMethod handlerMethod = map.get(info); - - // 获取方法上边的注解 替代path variable 为 * - Inner method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Inner.class); - Optional.ofNullable(method).ifPresent(inner -> info.getPatternsCondition().getPatterns() - .forEach(url -> ignoreUrls.add(ReUtil.replaceAll(url, PATTERN, "*")))); - - // 获取类上边的注解, 替代path variable 为 * - Inner controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Inner.class); - Optional.ofNullable(controller).ifPresent(inner -> info.getPatternsCondition().getPatterns() - .forEach(url -> ignoreUrls.add(ReUtil.replaceAll(url, PATTERN, "*")))); - }); - - } - -} diff --git a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PermitAllUrlResolver.java b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PermitAllUrlResolver.java new file mode 100644 index 000000000..d1d9a084e --- /dev/null +++ b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PermitAllUrlResolver.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2018-2025, lengleng All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * Neither the name of the pig4cloud.com developer nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * Author: lengleng (wangiegie@gmail.com) + */ + +package com.pig4cloud.pigx.common.security.component; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ReUtil; +import cn.hutool.core.util.StrUtil; +import com.pig4cloud.pigx.common.security.annotation.Inner; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.http.HttpMethod; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.mvc.method.RequestMappingInfo; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; + +import java.lang.reflect.Method; +import java.util.*; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + * @author lengleng + * @date 2019/4/13 + *

+ * 资源暴露处理器 + */ +@Slf4j +@Configuration +@RequiredArgsConstructor +@ConditionalOnExpression("!'${security.oauth2.client.ignore-urls}'.isEmpty()") +@ConfigurationProperties(prefix = "security.oauth2.client") +public class PermitAllUrlResolver implements InitializingBean { + + private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}"); + + private final WebApplicationContext applicationContext; + + @Getter + @Setter + private List ignoreUrls = new ArrayList<>(); + + @Override + public void afterPropertiesSet() { + RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class); + Map map = mapping.getHandlerMethods(); + + for (RequestMappingInfo info : map.keySet()) { + HandlerMethod handlerMethod = map.get(info); + + // 1. 首先获取类上边 @Inner 注解 + Inner controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Inner.class); + + // 2. 当类上不包含 @Inner 注解则获取该方法的注解 + if (controller == null) { + Inner method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Inner.class); + RequestMapping rq = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), RequestMapping.class); + Optional.ofNullable(method).ifPresent( + inner -> info.getPatternsCondition().getPatterns().forEach(url -> this.filterPath(url, rq))); + continue; + } + + // 3. 当类上包含 @Inner 注解 判断handlerMethod 是否包含在 inner 类中 + Class beanType = handlerMethod.getBeanType(); + Method[] methods = beanType.getDeclaredMethods(); + Method method = handlerMethod.getMethod(); + if (ArrayUtil.contains(methods, method)) { + RequestMapping rq = AnnotationUtils.findAnnotation(method, RequestMapping.class); + info.getPatternsCondition().getPatterns().forEach(url -> filterPath(url, rq)); + } + } + + } + + /** + * 过滤 Inner 设置 + * + *

1. 路径转换: 如果为restful(/xx/{xx}) --> /xx/* ant 表达式

+ *

2. 构建表达式:允许暴露的接口|允许暴露的方法类型,允许暴露的方法类型 URL|GET,POST,DELETE,PUT

+ * + * @param url mapping路径 + * @param rq 请求犯法 + */ + private void filterPath(String url, RequestMapping rq) { + List methodList = Arrays.stream(rq.method()).map(RequestMethod::name).collect(Collectors.toList()); + String resultUrl = ReUtil.replaceAll(url, PATTERN, "*"); + if (CollUtil.isEmpty(methodList)) { + ignoreUrls.add(resultUrl); + } else { + ignoreUrls.add(String.format("%s|%s", resultUrl, CollUtil.join(methodList, StrUtil.COMMA))); + } + } + + /** + * 获取对外暴露的URL,注册到 spring security + * + * @param registry spring security context + */ + public void registry(ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry registry) { + for (String url : getIgnoreUrls()) { + String[] strings = StrUtil.split(url, "|"); + + // 仅配置对外暴露的URL ,则注册到 spring security的为全部方法 + if (strings.length == 1) { + registry.antMatchers(strings[0]).permitAll(); + continue; + } + + // 当配置对外的URL|GET,POST 这种形式,则获取方法列表 并注册到 spring security + if (strings.length == 2) { + for (String method : StrUtil.split(strings[1], StrUtil.COMMA)) { + registry.antMatchers(HttpMethod.valueOf(method), strings[0]); + } + continue; + } + + log.warn("{} 配置无效,无法配置对外暴露", url); + } + } + +} diff --git a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PigxBearerTokenExtractor.java b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PigxBearerTokenExtractor.java index 95b3697dc..1b1f30c5b 100644 --- a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PigxBearerTokenExtractor.java +++ b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PigxBearerTokenExtractor.java @@ -18,6 +18,8 @@ package com.pig4cloud.pigx.common.security.component; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.StrUtil; import lombok.RequiredArgsConstructor; import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.provider.authentication.BearerTokenExtractor; @@ -39,14 +41,24 @@ public class PigxBearerTokenExtractor extends BearerTokenExtractor { private final PathMatcher pathMatcher = new AntPathMatcher(); - private final PermitAllUrlProperties urlProperties; + private final PermitAllUrlResolver permitAllUrlResolver; @Override public Authentication extract(HttpServletRequest request) { - boolean match = urlProperties.getIgnoreUrls().stream() - .anyMatch(url -> pathMatcher.match(url, request.getRequestURI())); - return match ? null : super.extract(request); + // 2. 判断请求方法是否匹配 + boolean result = permitAllUrlResolver.getIgnoreUrls().stream().anyMatch(url -> { + String[] strings = StrUtil.split(url, "|"); + // 1. 判断路径是否匹配 + boolean match = pathMatcher.match(strings[0], request.getRequestURI()); + // 2. 判断方法是否匹配 + if (strings.length == 2) { + String[] methods = StrUtil.split(strings[1], StrUtil.COMMA); + return ArrayUtil.contains(methods, request.getMethod()) && match; + } + return match; + }); + return result ? null : super.extract(request); } } diff --git a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PigxLocalResourceServerConfigurerAdapter.java b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PigxLocalResourceServerConfigurerAdapter.java index d29292224..a658989f3 100644 --- a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PigxLocalResourceServerConfigurerAdapter.java +++ b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PigxLocalResourceServerConfigurerAdapter.java @@ -41,7 +41,7 @@ public class PigxLocalResourceServerConfigurerAdapter extends ResourceServerConf protected AuthenticationEntryPoint resourceAuthExceptionEntryPoint; @Autowired - private PermitAllUrlProperties permitAllUrlProperties; + private PermitAllUrlResolver permitAllUrlResolver; @Autowired private TokenExtractor tokenExtractor; @@ -60,7 +60,8 @@ public class PigxLocalResourceServerConfigurerAdapter extends ResourceServerConf httpSecurity.headers().frameOptions().disable(); ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry registry = httpSecurity .authorizeRequests(); - permitAllUrlProperties.getIgnoreUrls().forEach(url -> registry.antMatchers(url).permitAll()); + // 配置对外暴露接口 + permitAllUrlResolver.registry(registry); registry.anyRequest().authenticated().and().csrf().disable(); } diff --git a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PigxResourceServerConfigurerAdapter.java b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PigxResourceServerConfigurerAdapter.java index db7e859af..deb56e301 100644 --- a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PigxResourceServerConfigurerAdapter.java +++ b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PigxResourceServerConfigurerAdapter.java @@ -47,7 +47,7 @@ public class PigxResourceServerConfigurerAdapter extends ResourceServerConfigure protected RemoteTokenServices remoteTokenServices; @Autowired - private PermitAllUrlProperties permitAllUrlProperties; + private PermitAllUrlResolver permitAllUrlResolver; @Autowired private TokenExtractor tokenExtractor; @@ -66,7 +66,8 @@ public class PigxResourceServerConfigurerAdapter extends ResourceServerConfigure httpSecurity.headers().frameOptions().disable(); ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry registry = httpSecurity .authorizeRequests(); - permitAllUrlProperties.getIgnoreUrls().forEach(url -> registry.antMatchers(url).permitAll()); + // 配置对外暴露接口 + permitAllUrlResolver.registry(registry); registry.anyRequest().authenticated().and().csrf().disable(); } diff --git a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PigxSecurityInnerAspect.java b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PigxSecurityInnerAspect.java index 69474cc6a..956e94220 100644 --- a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PigxSecurityInnerAspect.java +++ b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PigxSecurityInnerAspect.java @@ -27,6 +27,7 @@ import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; +import org.springframework.core.annotation.AnnotationUtils; import org.springframework.security.access.AccessDeniedException; import javax.servlet.http.HttpServletRequest; @@ -45,8 +46,14 @@ public class PigxSecurityInnerAspect { private final HttpServletRequest request; @SneakyThrows - @Around("@annotation(inner)") + @Around("@within(inner) || @annotation(inner)") public Object around(ProceedingJoinPoint point, Inner inner) { + // 先判断 inner 是否为空, 为空则获取类上注解 + if (inner == null) { + Class aClass = point.getTarget().getClass(); + inner = AnnotationUtils.findAnnotation(aClass, Inner.class); + } + String header = request.getHeader(SecurityConstants.FROM); if (inner.value() && !StrUtil.equals(SecurityConstants.FROM_IN, header)) { log.warn("访问接口 {} 没有权限", inner.value());