diff --git a/eiam-authentication/eiam-authentication-alipay/src/main/java/cn/topiam/employee/authentication/alipay/AlipayIdpOAuth2Config.java b/eiam-authentication/eiam-authentication-alipay/src/main/java/cn/topiam/employee/authentication/alipay/AlipayIdpOAuth2Config.java index 671fc31d..1ced91b8 100644 --- a/eiam-authentication/eiam-authentication-alipay/src/main/java/cn/topiam/employee/authentication/alipay/AlipayIdpOAuth2Config.java +++ b/eiam-authentication/eiam-authentication-alipay/src/main/java/cn/topiam/employee/authentication/alipay/AlipayIdpOAuth2Config.java @@ -1,5 +1,5 @@ /* - * eiam-authentication-gitee - Employee Identity and Access Management + * eiam-authentication-alipay - Employee Identity and Access Management * Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn) * * This program is free software: you can redistribute it and/or modify @@ -18,11 +18,14 @@ package cn.topiam.employee.authentication.alipay; import cn.topiam.employee.authentication.common.config.IdentityProviderConfig; + import lombok.Data; import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.NotBlank; + /** - * 支付宝 登录 + * 支付宝 登录配置 * * @author TopIAM * Created by support@topiam.cn on 2023/8/19 16:09 @@ -30,5 +33,10 @@ import lombok.EqualsAndHashCode; @Data @EqualsAndHashCode(callSuper = true) public class AlipayIdpOAuth2Config extends IdentityProviderConfig { + /** + * 商户ID + */ + @NotBlank(message = "商户ID不能为空") + private String appId; } diff --git a/eiam-authentication/eiam-authentication-alipay/src/main/java/cn/topiam/employee/authentication/alipay/configurer/AlipayAuthenticationConfigurer.java b/eiam-authentication/eiam-authentication-alipay/src/main/java/cn/topiam/employee/authentication/alipay/configurer/AlipayAuthenticationConfigurer.java index 1cf148bc..61321b51 100644 --- a/eiam-authentication/eiam-authentication-alipay/src/main/java/cn/topiam/employee/authentication/alipay/configurer/AlipayAuthenticationConfigurer.java +++ b/eiam-authentication/eiam-authentication-alipay/src/main/java/cn/topiam/employee/authentication/alipay/configurer/AlipayAuthenticationConfigurer.java @@ -1,5 +1,5 @@ /* - * eiam-authentication-gitee - Employee Identity and Access Management + * eiam-authentication-alipay - Employee Identity and Access Management * Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn) * * This program is free software: you can redistribute it and/or modify @@ -17,18 +17,18 @@ */ package cn.topiam.employee.authentication.alipay.configurer; -import cn.topiam.employee.authentication.alipay.filter.AlipayLoginAuthenticationFilter; -import cn.topiam.employee.authentication.common.service.UserIdpService; -import cn.topiam.employee.common.repository.authentication.IdentityProviderRepository; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer; -import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter; -import org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.OrRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.util.Assert; +import cn.topiam.employee.authentication.alipay.filter.AlipayAuthorizationRequestRedirectFilter; +import cn.topiam.employee.authentication.alipay.filter.AlipayLoginAuthenticationFilter; +import cn.topiam.employee.authentication.common.service.UserIdpService; +import cn.topiam.employee.common.repository.authentication.IdentityProviderRepository; + /** * 认证配置 * @@ -36,13 +36,13 @@ import org.springframework.util.Assert; * Created by support@topiam.cn on 2023/8/19 15:52 */ public class AlipayAuthenticationConfigurer extends - AbstractAuthenticationFilterConfigurer { + AbstractAuthenticationFilterConfigurer { private final IdentityProviderRepository identityProviderRepository; private final UserIdpService userIdpService; AlipayAuthenticationConfigurer(IdentityProviderRepository identityProviderRepository, - UserIdpService userIdpService) { + UserIdpService userIdpService) { Assert.notNull(identityProviderRepository, "identityProviderRepository must not be null"); Assert.notNull(userIdpService, "userIdpService must not be null"); this.identityProviderRepository = identityProviderRepository; @@ -61,10 +61,13 @@ public class AlipayAuthenticationConfigurer extends return new AntPathRequestMatcher(loginProcessingUrl); } - + public RequestMatcher getRequestMatcher() { + return new OrRequestMatcher(AlipayAuthorizationRequestRedirectFilter.getRequestMatcher(), + AlipayLoginAuthenticationFilter.getRequestMatcher()); + } public static AlipayAuthenticationConfigurer alipayOauth(IdentityProviderRepository identityProviderRepository, - UserIdpService userIdpService) { + UserIdpService userIdpService) { return new AlipayAuthenticationConfigurer(identityProviderRepository, userIdpService); } } diff --git a/eiam-authentication/eiam-authentication-alipay/src/main/java/cn/topiam/employee/authentication/alipay/constant/AlipayAuthenticationConstants.java b/eiam-authentication/eiam-authentication-alipay/src/main/java/cn/topiam/employee/authentication/alipay/constant/AlipayAuthenticationConstants.java index 2282a705..c622632f 100644 --- a/eiam-authentication/eiam-authentication-alipay/src/main/java/cn/topiam/employee/authentication/alipay/constant/AlipayAuthenticationConstants.java +++ b/eiam-authentication/eiam-authentication-alipay/src/main/java/cn/topiam/employee/authentication/alipay/constant/AlipayAuthenticationConstants.java @@ -1,5 +1,5 @@ /* - * eiam-authentication-gitee - Employee Identity and Access Management + * eiam-authentication-alipay - Employee Identity and Access Management * Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn) * * This program is free software: you can redistribute it and/or modify @@ -24,4 +24,10 @@ package cn.topiam.employee.authentication.alipay.constant; * Created by support@topiam.cn on 2023/8/19 15:18 */ public class AlipayAuthenticationConstants { + + public static final String AUTHORIZATION_REQUEST = "https://openauth.alipay.com/oauth2/publicAppAuthorize.htm"; + + public static final String USER_INFO_SCOPE = "auth_user"; + + public static final String APP_ID = "app_id"; } diff --git a/eiam-authentication/eiam-authentication-alipay/src/main/java/cn/topiam/employee/authentication/alipay/filter/AlipayAuthorizationRequestRedirectFilter.java b/eiam-authentication/eiam-authentication-alipay/src/main/java/cn/topiam/employee/authentication/alipay/filter/AlipayAuthorizationRequestRedirectFilter.java index bdc1a020..e9fe344b 100644 --- a/eiam-authentication/eiam-authentication-alipay/src/main/java/cn/topiam/employee/authentication/alipay/filter/AlipayAuthorizationRequestRedirectFilter.java +++ b/eiam-authentication/eiam-authentication-alipay/src/main/java/cn/topiam/employee/authentication/alipay/filter/AlipayAuthorizationRequestRedirectFilter.java @@ -1,5 +1,5 @@ /* - * eiam-authentication-gitee - Employee Identity and Access Management + * eiam-authentication-alipay - Employee Identity and Access Management * Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn) * * This program is free software: you can redistribute it and/or modify @@ -17,13 +17,42 @@ */ package cn.topiam.employee.authentication.alipay.filter; +import java.io.IOException; +import java.util.*; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; +import org.springframework.http.HttpMethod; +import org.springframework.security.crypto.keygen.Base64StringKeyGenerator; +import org.springframework.security.crypto.keygen.StringKeyGenerator; +import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository; +import org.springframework.security.oauth2.client.web.HttpSessionOAuth2AuthorizationRequestRepository; +import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; +import org.springframework.security.web.DefaultRedirectStrategy; +import org.springframework.security.web.RedirectStrategy; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.security.web.util.matcher.RequestMatcher; +import org.springframework.util.Assert; +import org.springframework.web.filter.OncePerRequestFilter; + +import com.alibaba.fastjson2.JSONObject; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + +import cn.topiam.employee.authentication.alipay.AlipayIdpOAuth2Config; +import cn.topiam.employee.common.entity.authn.IdentityProviderEntity; +import cn.topiam.employee.common.repository.authentication.IdentityProviderRepository; +import cn.topiam.employee.support.trace.TraceUtils; + import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import org.springframework.web.filter.OncePerRequestFilter; +import static org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames.CLIENT_ID; -import java.io.IOException; +import static cn.topiam.employee.authentication.alipay.constant.AlipayAuthenticationConstants.*; +import static cn.topiam.employee.authentication.common.IdentityProviderType.ALIPAY_OAUTH; +import static cn.topiam.employee.authentication.common.constant.AuthenticationConstants.PROVIDER_CODE; /** * 支付宝 登录请求重定向过滤器 @@ -33,9 +62,86 @@ import java.io.IOException; */ @SuppressWarnings("DuplicatedCode") public class AlipayAuthorizationRequestRedirectFilter extends OncePerRequestFilter { + /** + * 重定向策略 + */ + private final RedirectStrategy authorizationRedirectStrategy = new DefaultRedirectStrategy(); + + /** + * 认证请求存储库 + */ + private final AuthorizationRequestRepository authorizationRequestRepository = new HttpSessionOAuth2AuthorizationRequestRepository(); + + private static final StringKeyGenerator DEFAULT_STATE_GENERATOR = new Base64StringKeyGenerator( + Base64.getUrlEncoder()); + private final IdentityProviderRepository identityProviderRepository; + + public AlipayAuthorizationRequestRedirectFilter(IdentityProviderRepository identityProviderRepository) { + this.identityProviderRepository = identityProviderRepository; + } + + /** + * AntPathRequestMatcher + */ + public static final AntPathRequestMatcher ALIPAY_REQUEST_MATCHER = new AntPathRequestMatcher( + ALIPAY_OAUTH.getAuthorizationPathPrefix() + "/" + "{" + PROVIDER_CODE + "}", + HttpMethod.GET.name()); @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + protected void doFilterInternal(@NotNull HttpServletRequest request, + @NotNull HttpServletResponse response, + @NotNull FilterChain filterChain) throws ServletException, + IOException { + RequestMatcher.MatchResult matcher = ALIPAY_REQUEST_MATCHER.matcher(request); + if (!matcher.isMatch()) { + filterChain.doFilter(request, response); + return; + } + TraceUtils.put(UUID.randomUUID().toString()); + Map variables = matcher.getVariables(); + String providerCode = variables.get(PROVIDER_CODE); + Optional optional = identityProviderRepository + .findByCodeAndEnabledIsTrue(providerCode); + if (optional.isEmpty()) { + throw new NullPointerException("未查询到身份提供商信息"); + } + IdentityProviderEntity entity = optional.get(); + AlipayIdpOAuth2Config config = JSONObject.parseObject(entity.getConfig(), + AlipayIdpOAuth2Config.class); + Assert.notNull(config, "支付宝登录配置不能为空"); + //构建授权请求 + //@formatter:off + HashMap<@Nullable String, @Nullable Object> attributes = Maps.newHashMap(); + OAuth2AuthorizationRequest.Builder builder = OAuth2AuthorizationRequest.authorizationCode() + .clientId(config.getAppId()) + .scopes(Sets.newHashSet(USER_INFO_SCOPE)) + .authorizationUri(AUTHORIZATION_REQUEST) + .redirectUri(AlipayLoginAuthenticationFilter.getLoginUrl(optional.get().getCode())) + .state(DEFAULT_STATE_GENERATOR.generateKey()) + .attributes(attributes); + builder.parameters(parameters -> { + parameters.put(APP_ID, parameters.get(CLIENT_ID)); + parameters.remove(CLIENT_ID); + }); + //@formatter:on + this.sendRedirectForAuthorization(request, response, builder.build()); + } + /** + * getRequestMatcher + * + * @return {@link RequestMatcher} + */ + public static RequestMatcher getRequestMatcher() { + return ALIPAY_REQUEST_MATCHER; + } + + private void sendRedirectForAuthorization(HttpServletRequest request, + HttpServletResponse response, + OAuth2AuthorizationRequest authorizationRequest) throws IOException { + this.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, request, + response); + this.authorizationRedirectStrategy.sendRedirect(request, response, + authorizationRequest.getAuthorizationRequestUri()); } } diff --git a/eiam-authentication/eiam-authentication-alipay/src/main/java/cn/topiam/employee/authentication/alipay/filter/AlipayLoginAuthenticationFilter.java b/eiam-authentication/eiam-authentication-alipay/src/main/java/cn/topiam/employee/authentication/alipay/filter/AlipayLoginAuthenticationFilter.java index 74761eb5..0bf7f3f1 100644 --- a/eiam-authentication/eiam-authentication-alipay/src/main/java/cn/topiam/employee/authentication/alipay/filter/AlipayLoginAuthenticationFilter.java +++ b/eiam-authentication/eiam-authentication-alipay/src/main/java/cn/topiam/employee/authentication/alipay/filter/AlipayLoginAuthenticationFilter.java @@ -1,5 +1,5 @@ /* - * eiam-authentication-gitee - Employee Identity and Access Management + * eiam-authentication-alipay - Employee Identity and Access Management * Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn) * * This program is free software: you can redistribute it and/or modify @@ -17,16 +17,26 @@ */ package cn.topiam.employee.authentication.alipay.filter; +import java.io.IOException; + +import org.springframework.http.HttpMethod; +import org.springframework.security.authentication.AuthenticationServiceException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.security.web.util.matcher.RequestMatcher; + import cn.topiam.employee.authentication.common.filter.AbstractIdpAuthenticationProcessingFilter; import cn.topiam.employee.authentication.common.service.UserIdpService; import cn.topiam.employee.common.repository.authentication.IdentityProviderRepository; +import cn.topiam.employee.core.help.ServerHelp; +import cn.topiam.employee.support.util.HttpUrlUtils; + import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; - -import java.io.IOException; +import static cn.topiam.employee.authentication.common.IdentityProviderType.ALIPAY_OAUTH; +import static cn.topiam.employee.authentication.common.constant.AuthenticationConstants.PROVIDER_CODE; /** * 支付宝 登录过滤器 @@ -36,7 +46,10 @@ import java.io.IOException; */ @SuppressWarnings("DuplicatedCode") public class AlipayLoginAuthenticationFilter extends AbstractIdpAuthenticationProcessingFilter { - + public final static String DEFAULT_FILTER_PROCESSES_URI = ALIPAY_OAUTH + .getLoginPathPrefix() + "/" + "{" + PROVIDER_CODE + "}"; + public static final AntPathRequestMatcher REQUEST_MATCHER = new AntPathRequestMatcher( + DEFAULT_FILTER_PROCESSES_URI, HttpMethod.GET.name()); /** * Creates a new instance @@ -45,12 +58,31 @@ public class AlipayLoginAuthenticationFilter extends AbstractIdpAuthenticationPr * @param userIdpService {@link UserIdpService} * @param identityProviderRepository {@link IdentityProviderRepository} */ - protected AlipayLoginAuthenticationFilter(String defaultFilterProcessesUrl, UserIdpService userIdpService, IdentityProviderRepository identityProviderRepository) { + protected AlipayLoginAuthenticationFilter(String defaultFilterProcessesUrl, + UserIdpService userIdpService, + IdentityProviderRepository identityProviderRepository) { super(defaultFilterProcessesUrl, userIdpService, identityProviderRepository); } @Override - public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException { + public Authentication attemptAuthentication(HttpServletRequest request, + HttpServletResponse response) throws AuthenticationException, + IOException, + ServletException { + if (!REQUEST_MATCHER.matches(request)) { + throw new AuthenticationServiceException( + "Authentication method not supported: " + request.getMethod()); + } return null; } + + public static String getLoginUrl(String providerId) { + String url = ServerHelp.getPortalPublicBaseUrl() + ALIPAY_OAUTH.getLoginPathPrefix() + "/" + + providerId; + return HttpUrlUtils.format(url); + } + + public static RequestMatcher getRequestMatcher() { + return REQUEST_MATCHER; + } }