From f738bed680fd4e12d3bbdf0a118a0f40b9257333 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=86=B7=E5=86=B7?= <2270033969@qq.com> Date: Thu, 15 Aug 2024 13:20:51 +0800 Subject: [PATCH] =?UTF-8?q?refactor(common):=20security=20=20=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E9=85=8D=E7=BD=AE=E9=A1=B9=EF=BC=8C=E5=A6=82=E6=9E=9C?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=20skipPublicUrl=20true=20=E5=A6=82=E6=9E=9C?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E5=AF=B9=E5=A4=96=E8=AE=BE=E7=BD=AE=EF=BC=8C?= =?UTF-8?q?=E5=8D=B3=E4=BD=BF=E6=90=BA=E5=B8=A6token=20=E4=B9=9F=E4=B8=8D?= =?UTF-8?q?=E9=89=B4=E6=9D=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../component/PermitAllUrlProperties.java | 57 +++++--- .../component/PigxBearerTokenExtractor.java | 133 +++++++++--------- 2 files changed, 100 insertions(+), 90 deletions(-) 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 index 32b44a7be..c71fd7e3a 100755 --- 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 @@ -42,33 +42,46 @@ import java.util.regex.Pattern; @ConfigurationProperties(prefix = "security.oauth2.client") public class PermitAllUrlProperties implements InitializingBean { - private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}"); + private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}"); - private static final String[] DEFAULT_IGNORE_URLS = new String[] { "/actuator/**", "/error", "/v3/api-docs" }; + private static final String[] DEFAULT_IGNORE_URLS = new String[]{"/actuator/**", "/error", "/v3/api-docs"}; - @Getter - @Setter - private List ignoreUrls = new ArrayList<>(); + @Getter + @Setter + private List ignoreUrls = new ArrayList<>(); - @Override - public void afterPropertiesSet() { - ignoreUrls.addAll(Arrays.asList(DEFAULT_IGNORE_URLS)); - RequestMappingHandlerMapping mapping = SpringContextHolder.getBean("requestMappingHandlerMapping"); - Map map = mapping.getHandlerMethods(); + /** + * 跳过对外 URL + *

+ * 比如某个接口是对外开放的,即使携带 token,也不鉴权 + */ + @Getter + @Setter + private boolean skipPublicUrl = false; - map.keySet().forEach(info -> { - HandlerMethod handlerMethod = map.get(info); + @Override + public void afterPropertiesSet() { + ignoreUrls.addAll(Arrays.asList(DEFAULT_IGNORE_URLS)); + RequestMappingHandlerMapping mapping = SpringContextHolder.getBean("requestMappingHandlerMapping"); + Map map = mapping.getHandlerMethods(); - // 获取方法上边的注解 替代path variable 为 * - Inner method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Inner.class); - Optional.ofNullable(method).ifPresent(inner -> Objects.requireNonNull(info.getPathPatternsCondition()) - .getPatternValues().forEach(url -> ignoreUrls.add(ReUtil.replaceAll(url, PATTERN, "*")))); + map.keySet().forEach(info -> { + HandlerMethod handlerMethod = map.get(info); - // 获取类上边的注解, 替代path variable 为 * - Inner controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Inner.class); - Optional.ofNullable(controller).ifPresent(inner -> Objects.requireNonNull(info.getPathPatternsCondition()) - .getPatternValues().forEach(url -> ignoreUrls.add(ReUtil.replaceAll(url, PATTERN, "*")))); - }); - } + // 获取方法上边的注解 替代path variable 为 * + Inner method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Inner.class); + Optional.ofNullable(method) + .ifPresent(inner -> Objects.requireNonNull(info.getPathPatternsCondition()) + .getPatternValues() + .forEach(url -> ignoreUrls.add(ReUtil.replaceAll(url, PATTERN, "*")))); + + // 获取类上边的注解, 替代path variable 为 * + Inner controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Inner.class); + Optional.ofNullable(controller) + .ifPresent(inner -> Objects.requireNonNull(info.getPathPatternsCondition()) + .getPatternValues() + .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/PigxBearerTokenExtractor.java b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PigxBearerTokenExtractor.java index cc62cc62a..0649dc936 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 @@ -17,7 +17,6 @@ package com.pig4cloud.pigx.common.security.component; import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; import org.springframework.security.oauth2.core.OAuth2AuthenticationException; import org.springframework.security.oauth2.server.resource.BearerTokenError; import org.springframework.security.oauth2.server.resource.BearerTokenErrors; @@ -36,86 +35,84 @@ import java.util.regex.Pattern; */ public class PigxBearerTokenExtractor implements BearerTokenResolver { - private static final Pattern authorizationPattern = Pattern.compile("^Bearer (?[a-zA-Z0-9-:._~+/]+=*)$", - Pattern.CASE_INSENSITIVE); + private static final Pattern authorizationPattern = Pattern.compile("^Bearer (?[a-zA-Z0-9-:._~+/]+=*)$", + Pattern.CASE_INSENSITIVE); - private boolean allowFormEncodedBodyParameter = false; - private boolean allowUriQueryParameter = true; + private String bearerTokenHeaderName = HttpHeaders.AUTHORIZATION; - private String bearerTokenHeaderName = HttpHeaders.AUTHORIZATION; + private final PathMatcher pathMatcher = new AntPathMatcher(); - private final PathMatcher pathMatcher = new AntPathMatcher(); + private final PermitAllUrlProperties urlProperties; - private final PermitAllUrlProperties urlProperties; + public PigxBearerTokenExtractor(PermitAllUrlProperties urlProperties) { + this.urlProperties = urlProperties; + } - public PigxBearerTokenExtractor(PermitAllUrlProperties urlProperties) { - this.urlProperties = urlProperties; - } + /** + * 解析令牌 + * + * @param request 请求 + * @return {@link String } + */ + @Override + public String resolve(HttpServletRequest request) { + String requestUri = request.getRequestURI(); + String relativePath = requestUri.substring(request.getContextPath().length()); - @Override - public String resolve(HttpServletRequest request) { - String requestUri = request.getRequestURI(); - String relativePath = requestUri.substring(request.getContextPath().length()); + if (urlProperties.isSkipPublicUrl()) { + boolean match = urlProperties.getIgnoreUrls().stream().anyMatch(url -> pathMatcher.match(url, relativePath)); - boolean match = urlProperties.getIgnoreUrls().stream().anyMatch(url -> pathMatcher.match(url, relativePath)); + if (match) { + return null; + } + } - if (match) { - return null; - } - final String authorizationHeaderToken = resolveFromAuthorizationHeader(request); - final String parameterToken = isParameterTokenSupportedForRequest(request) - ? resolveFromRequestParameters(request) : null; - if (authorizationHeaderToken != null) { - if (parameterToken != null) { - final BearerTokenError error = BearerTokenErrors - .invalidRequest("Found multiple bearer tokens in the request"); - throw new OAuth2AuthenticationException(error); - } - return authorizationHeaderToken; - } - if (parameterToken != null && isParameterTokenEnabledForRequest(request)) { - return parameterToken; - } - return null; - } + final String authorizationHeaderToken = resolveFromAuthorizationHeader(request); - private String resolveFromAuthorizationHeader(HttpServletRequest request) { - String authorization = request.getHeader(this.bearerTokenHeaderName); - if (!StringUtils.startsWithIgnoreCase(authorization, "bearer")) { - return null; - } - Matcher matcher = authorizationPattern.matcher(authorization); - if (!matcher.matches()) { - BearerTokenError error = BearerTokenErrors.invalidToken("Bearer token is malformed"); - throw new OAuth2AuthenticationException(error); - } - return matcher.group("token"); - } + if (authorizationHeaderToken != null) { + return authorizationHeaderToken; + } - private static String resolveFromRequestParameters(HttpServletRequest request) { - String[] values = request.getParameterValues("access_token"); - if (values == null || values.length == 0) { - return null; - } - if (values.length == 1) { - return values[0]; - } - BearerTokenError error = BearerTokenErrors.invalidRequest("Found multiple bearer tokens in the request"); - throw new OAuth2AuthenticationException(error); - } + return resolveFromRequestParameters(request); + } - private boolean isParameterTokenSupportedForRequest(final HttpServletRequest request) { - return (("POST".equals(request.getMethod()) - && MediaType.APPLICATION_FORM_URLENCODED_VALUE.equals(request.getContentType())) - || "GET".equals(request.getMethod())); - } + /** + * 从授权标头解析 + * + * @param request 请求 + * @return {@link String } + */ + private String resolveFromAuthorizationHeader(HttpServletRequest request) { + String authorization = request.getHeader(this.bearerTokenHeaderName); + if (!StringUtils.startsWithIgnoreCase(authorization, "bearer")) { + return null; + } + Matcher matcher = authorizationPattern.matcher(authorization); + if (!matcher.matches()) { + BearerTokenError error = BearerTokenErrors.invalidToken("Bearer token is malformed"); + throw new OAuth2AuthenticationException(error); + } + return matcher.group("token"); + } - private boolean isParameterTokenEnabledForRequest(final HttpServletRequest request) { - return ((this.allowFormEncodedBodyParameter && "POST".equals(request.getMethod()) - && MediaType.APPLICATION_FORM_URLENCODED_VALUE.equals(request.getContentType())) - || (this.allowUriQueryParameter && "GET".equals(request.getMethod()))); - } + /** + * 从请求参数解析 + * + * @param request 请求 + * @return {@link String } + */ + private static String resolveFromRequestParameters(HttpServletRequest request) { + String[] values = request.getParameterValues("access_token"); + if (values == null || values.length == 0) { + return null; + } + if (values.length == 1) { + return values[0]; + } + BearerTokenError error = BearerTokenErrors.invalidRequest("Found multiple bearer tokens in the request"); + throw new OAuth2AuthenticationException(error); + } }