From e74d38e9ef65a7e73a086a318498f61c06cfc38a Mon Sep 17 00:00:00 2001 From: lbw Date: Fri, 1 Sep 2023 15:00:19 +0800 Subject: [PATCH] =?UTF-8?q?:recycle:=20Refactoring=20code.=20=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=20security=E6=A8=A1=E5=9D=97=E9=80=82=E9=85=8D?= =?UTF-8?q?=E6=9C=80=E6=96=B0=E7=9A=84spring=20security=20=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E6=A0=BC=E5=BC=8F=E6=A0=87=E5=87=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../annotation/EnablePigxResourceServer.java | 2 - .../PigxResourceServerConfiguration.java | 31 +++++++--- .../component/PigxSecurityInnerAspect.java | 16 +++--- .../PigxRedisOAuth2AuthorizationService.java | 25 +++++---- .../PigxRemoteRegisteredClientRepository.java | 56 ++++++++++--------- .../security/util/OAuth2EndpointUtils.java | 11 ++-- 6 files changed, 79 insertions(+), 62 deletions(-) diff --git a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/annotation/EnablePigxResourceServer.java b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/annotation/EnablePigxResourceServer.java index c18617140..375a21fb2 100644 --- a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/annotation/EnablePigxResourceServer.java +++ b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/annotation/EnablePigxResourceServer.java @@ -20,7 +20,6 @@ import com.pig4cloud.pigx.common.security.component.PigxResourceServerAutoConfig import com.pig4cloud.pigx.common.security.component.PigxResourceServerConfiguration; import com.pig4cloud.pigx.common.security.feign.PigxFeignClientConfiguration; import org.springframework.context.annotation.Import; -import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import java.lang.annotation.*; @@ -34,7 +33,6 @@ import java.lang.annotation.*; @Inherited @Target({ ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) -@EnableGlobalMethodSecurity(prePostEnabled = true) @Import({ PigxResourceServerAutoConfiguration.class, PigxResourceServerConfiguration.class, PigxFeignClientConfiguration.class }) public @interface EnablePigxResourceServer { diff --git a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PigxResourceServerConfiguration.java b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PigxResourceServerConfiguration.java index b762884c6..6ab3b5bdd 100644 --- a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PigxResourceServerConfiguration.java +++ b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PigxResourceServerConfiguration.java @@ -16,16 +16,21 @@ package com.pig4cloud.pigx.common.security.component; -import cn.hutool.core.util.ArrayUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Bean; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer; import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; + +import java.util.stream.Collectors; /** * @author lengleng @@ -35,6 +40,7 @@ import org.springframework.security.web.SecurityFilterChain; */ @Slf4j @EnableWebSecurity +@EnableMethodSecurity @RequiredArgsConstructor public class PigxResourceServerConfiguration { @@ -49,15 +55,22 @@ public class PigxResourceServerConfiguration { @Bean @Order(Ordered.HIGHEST_PRECEDENCE) SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + AntPathRequestMatcher[] requestMatchers = permitAllUrl.getIgnoreUrls() + .stream() + .map(AntPathRequestMatcher::new) + .collect(Collectors.toList()) + .toArray(new AntPathRequestMatcher[] {}); - http.authorizeHttpRequests(authorizeRequests -> authorizeRequests - .antMatchers(ArrayUtil.toArray(permitAllUrl.getIgnoreUrls(), String.class)).permitAll().anyRequest() - .authenticated()) - .oauth2ResourceServer( - oauth2 -> oauth2.opaqueToken(token -> token.introspector(customOpaqueTokenIntrospector)) - .authenticationEntryPoint(resourceAuthExceptionEntryPoint) - .bearerTokenResolver(pigxBearerTokenExtractor)) - .headers().frameOptions().disable().and().csrf().disable(); + http.authorizeHttpRequests(authorizeRequests -> authorizeRequests.requestMatchers(requestMatchers) + .permitAll() + .anyRequest() + .authenticated()) + .oauth2ResourceServer( + oauth2 -> oauth2.opaqueToken(token -> token.introspector(customOpaqueTokenIntrospector)) + .authenticationEntryPoint(resourceAuthExceptionEntryPoint) + .bearerTokenResolver(pigxBearerTokenExtractor)) + .headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable)) + .csrf(AbstractHttpConfigurer::disable); return http.build(); } 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 3d8c1ed6c..232b51dd6 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 @@ -19,18 +19,17 @@ package com.pig4cloud.pigx.common.security.component; import cn.hutool.core.util.StrUtil; import com.pig4cloud.pigx.common.core.constant.SecurityConstants; import com.pig4cloud.pigx.common.security.annotation.Inner; +import javax.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; import org.springframework.core.Ordered; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.security.access.AccessDeniedException; -import javax.servlet.http.HttpServletRequest; - /** * @author lengleng * @date 2022-06-04 @@ -45,19 +44,18 @@ public class PigxSecurityInnerAspect implements Ordered { private final HttpServletRequest request; @SneakyThrows - @Around("@within(inner) || @annotation(inner)") - public Object around(ProceedingJoinPoint point, Inner inner) { - // 实际注入的inner实体由表达式后一个注解决定,即是方法上的@Inner注解实体,若方法上无@Inner注解,则获取类上的 + @Before("@within(inner) || @annotation(inner)") + public void around(JoinPoint point, Inner inner) { + String header = request.getHeader(SecurityConstants.FROM); if (inner == null) { Class clazz = point.getTarget().getClass(); inner = AnnotationUtils.findAnnotation(clazz, Inner.class); } - String header = request.getHeader(SecurityConstants.FROM); + if (inner.value() && !StrUtil.equals(SecurityConstants.FROM_IN, header)) { log.warn("访问接口 {} 没有权限", point.getSignature().getName()); throw new AccessDeniedException("Access is denied"); } - return point.proceed(); } @Override diff --git a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxRedisOAuth2AuthorizationService.java b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxRedisOAuth2AuthorizationService.java index 266108b5a..e67997c0b 100644 --- a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxRedisOAuth2AuthorizationService.java +++ b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxRedisOAuth2AuthorizationService.java @@ -46,35 +46,38 @@ public class PigxRedisOAuth2AuthorizationService implements OAuth2AuthorizationS if (isState(authorization)) { String token = authorization.getAttribute("state"); redisTemplate.setValueSerializer(RedisSerializer.java()); - redisTemplate.opsForValue().set(buildKey(OAuth2ParameterNames.STATE, token), authorization, TIMEOUT, - TimeUnit.MINUTES); + redisTemplate.opsForValue() + .set(buildKey(OAuth2ParameterNames.STATE, token), authorization, TIMEOUT, TimeUnit.MINUTES); } if (isCode(authorization)) { OAuth2Authorization.Token authorizationCode = authorization - .getToken(OAuth2AuthorizationCode.class); + .getToken(OAuth2AuthorizationCode.class); OAuth2AuthorizationCode authorizationCodeToken = authorizationCode.getToken(); long between = ChronoUnit.MINUTES.between(authorizationCodeToken.getIssuedAt(), authorizationCodeToken.getExpiresAt()); redisTemplate.setValueSerializer(RedisSerializer.java()); - redisTemplate.opsForValue().set(buildKey(OAuth2ParameterNames.CODE, authorizationCodeToken.getTokenValue()), - authorization, between, TimeUnit.MINUTES); + redisTemplate.opsForValue() + .set(buildKey(OAuth2ParameterNames.CODE, authorizationCodeToken.getTokenValue()), authorization, + between, TimeUnit.MINUTES); } if (isRefreshToken(authorization)) { OAuth2RefreshToken refreshToken = authorization.getRefreshToken().getToken(); long between = ChronoUnit.SECONDS.between(refreshToken.getIssuedAt(), refreshToken.getExpiresAt()); redisTemplate.setValueSerializer(RedisSerializer.java()); - redisTemplate.opsForValue().set(buildKey(OAuth2ParameterNames.REFRESH_TOKEN, refreshToken.getTokenValue()), - authorization, between, TimeUnit.SECONDS); + redisTemplate.opsForValue() + .set(buildKey(OAuth2ParameterNames.REFRESH_TOKEN, refreshToken.getTokenValue()), authorization, between, + TimeUnit.SECONDS); } if (isAccessToken(authorization)) { OAuth2AccessToken accessToken = authorization.getAccessToken().getToken(); long between = ChronoUnit.SECONDS.between(accessToken.getIssuedAt(), accessToken.getExpiresAt()); redisTemplate.setValueSerializer(RedisSerializer.java()); - redisTemplate.opsForValue().set(buildKey(OAuth2ParameterNames.ACCESS_TOKEN, accessToken.getTokenValue()), - authorization, between, TimeUnit.SECONDS); + redisTemplate.opsForValue() + .set(buildKey(OAuth2ParameterNames.ACCESS_TOKEN, accessToken.getTokenValue()), authorization, between, + TimeUnit.SECONDS); // 扩展记录 access-token 、username 的关系 1::token::username::admin::xxx String tokenUsername = String.format("%s::%s::%s::%s::%s", tenantKeyStrResolver.key(), AUTHORIZATION, @@ -95,7 +98,7 @@ public class PigxRedisOAuth2AuthorizationService implements OAuth2AuthorizationS if (isCode(authorization)) { OAuth2Authorization.Token authorizationCode = authorization - .getToken(OAuth2AuthorizationCode.class); + .getToken(OAuth2AuthorizationCode.class); OAuth2AuthorizationCode authorizationCodeToken = authorizationCode.getToken(); keys.add(buildKey(OAuth2ParameterNames.CODE, authorizationCodeToken.getTokenValue())); } @@ -143,7 +146,7 @@ public class PigxRedisOAuth2AuthorizationService implements OAuth2AuthorizationS private static boolean isCode(OAuth2Authorization authorization) { OAuth2Authorization.Token authorizationCode = authorization - .getToken(OAuth2AuthorizationCode.class); + .getToken(OAuth2AuthorizationCode.class); return Objects.nonNull(authorizationCode); } diff --git a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxRemoteRegisteredClientRepository.java b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxRemoteRegisteredClientRepository.java index 80c1ba728..275e1aa60 100644 --- a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxRemoteRegisteredClientRepository.java +++ b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxRemoteRegisteredClientRepository.java @@ -57,7 +57,6 @@ public class PigxRemoteRegisteredClientRepository implements RegisteredClientRep */ @Override public void save(RegisteredClient registeredClient) { - throw new UnsupportedOperationException(); } /** @@ -89,46 +88,53 @@ public class PigxRemoteRegisteredClientRepository implements RegisteredClientRep public RegisteredClient findByClientId(String clientId) { SysOauthClientDetails clientDetails = RetOps - .of(clientDetailsService.getClientDetailsById(clientId, SecurityConstants.FROM_IN)).getData() - .orElseThrow(() -> new OAuth2AuthorizationCodeRequestAuthenticationException( - new OAuth2Error("客户端查询异常,请检查数据库链接"), null)); + .of(clientDetailsService.getClientDetailsById(clientId, SecurityConstants.FROM_IN)) + .getData() + .orElseThrow(() -> new OAuth2AuthorizationCodeRequestAuthenticationException( + new OAuth2Error("客户端查询异常,请检查数据库链接"), null)); RegisteredClient.Builder builder = RegisteredClient.withId(clientDetails.getClientId()) - .clientId(clientDetails.getClientId()) - .clientSecret(SecurityConstants.NOOP + clientDetails.getClientSecret()) - .clientAuthenticationMethods(clientAuthenticationMethods -> { - clientAuthenticationMethods.add(ClientAuthenticationMethod.CLIENT_SECRET_BASIC); - clientAuthenticationMethods.add(ClientAuthenticationMethod.CLIENT_SECRET_POST); - }); + .clientId(clientDetails.getClientId()) + .clientSecret(SecurityConstants.NOOP + clientDetails.getClientSecret()) + .clientAuthenticationMethods(clientAuthenticationMethods -> { + clientAuthenticationMethods.add(ClientAuthenticationMethod.CLIENT_SECRET_BASIC); + clientAuthenticationMethods.add(ClientAuthenticationMethod.CLIENT_SECRET_POST); + }); // 授权模式 Arrays.stream(clientDetails.getAuthorizedGrantTypes()) - .forEach(grant -> builder.authorizationGrantType(new AuthorizationGrantType(grant))); + .forEach(grant -> builder.authorizationGrantType(new AuthorizationGrantType(grant))); // 回调地址 - Optional.ofNullable(clientDetails.getWebServerRedirectUri()).ifPresent(redirectUri -> Arrays - .stream(redirectUri.split(StrUtil.COMMA)).filter(StrUtil::isNotBlank).forEach(builder::redirectUri)); + Optional.ofNullable(clientDetails.getWebServerRedirectUri()) + .ifPresent(redirectUri -> Arrays.stream(redirectUri.split(StrUtil.COMMA)) + .filter(StrUtil::isNotBlank) + .forEach(builder::redirectUri)); // scope - Optional.ofNullable(clientDetails.getScope()).ifPresent( - scope -> Arrays.stream(scope.split(StrUtil.COMMA)).filter(StrUtil::isNotBlank).forEach(builder::scope)); + Optional.ofNullable(clientDetails.getScope()) + .ifPresent(scope -> Arrays.stream(scope.split(StrUtil.COMMA)) + .filter(StrUtil::isNotBlank) + .forEach(builder::scope)); // 注入扩展配置 Optional.ofNullable(clientDetails.getAdditionalInformation()).ifPresent(ext -> { Map map = JSONUtil.parseObj(ext).toBean(Map.class); - builder.clientSettings(ClientSettings.withSettings(map).requireProofKey(false) - .requireAuthorizationConsent(!BooleanUtil.toBoolean(clientDetails.getAutoapprove())).build()); + builder.clientSettings(ClientSettings.withSettings(map) + .requireProofKey(false) + .requireAuthorizationConsent(!BooleanUtil.toBoolean(clientDetails.getAutoapprove())) + .build()); }); return builder - .tokenSettings(TokenSettings.builder().accessTokenFormat(OAuth2TokenFormat.REFERENCE) - .accessTokenTimeToLive(Duration.ofSeconds(Optional - .ofNullable(clientDetails.getAccessTokenValidity()).orElse(accessTokenValiditySeconds))) - .refreshTokenTimeToLive( - Duration.ofSeconds(Optional.ofNullable(clientDetails.getRefreshTokenValidity()) - .orElse(refreshTokenValiditySeconds))) - .build()) + .tokenSettings(TokenSettings.builder() + .accessTokenFormat(OAuth2TokenFormat.REFERENCE) + .accessTokenTimeToLive(Duration.ofSeconds( + Optional.ofNullable(clientDetails.getAccessTokenValidity()).orElse(accessTokenValiditySeconds))) + .refreshTokenTimeToLive(Duration.ofSeconds(Optional.ofNullable(clientDetails.getRefreshTokenValidity()) + .orElse(refreshTokenValiditySeconds))) + .build()) - .build(); + .build(); } diff --git a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/util/OAuth2EndpointUtils.java b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/util/OAuth2EndpointUtils.java index a9531c98c..bcdc2b984 100644 --- a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/util/OAuth2EndpointUtils.java +++ b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/util/OAuth2EndpointUtils.java @@ -28,10 +28,8 @@ public class OAuth2EndpointUtils { Map parameterMap = request.getParameterMap(); MultiValueMap parameters = new LinkedMultiValueMap<>(parameterMap.size()); parameterMap.forEach((key, values) -> { - if (values.length > 0) { - for (String value : values) { - parameters.add(key, value); - } + for (String value : values) { + parameters.add(key, value); } }); return parameters; @@ -39,7 +37,7 @@ public class OAuth2EndpointUtils { public boolean matchesPkceTokenRequest(HttpServletRequest request) { return AuthorizationGrantType.AUTHORIZATION_CODE.getValue() - .equals(request.getParameter(OAuth2ParameterNames.GRANT_TYPE)) + .equals(request.getParameter(OAuth2ParameterNames.GRANT_TYPE)) && request.getParameter(OAuth2ParameterNames.CODE) != null && request.getParameter(PkceParameterNames.CODE_VERIFIER) != null; } @@ -63,7 +61,8 @@ public class OAuth2EndpointUtils { OAuth2RefreshToken refreshToken = authentication.getRefreshToken().getToken(); OAuth2AccessTokenResponse.Builder builder = OAuth2AccessTokenResponse.withToken(accessToken.getTokenValue()) - .tokenType(accessToken.getTokenType()).scopes(accessToken.getScopes()); + .tokenType(accessToken.getTokenType()) + .scopes(accessToken.getScopes()); if (accessToken.getIssuedAt() != null && accessToken.getExpiresAt() != null) { builder.expiresIn(ChronoUnit.SECONDS.between(accessToken.getIssuedAt(), accessToken.getExpiresAt())); }