Skip to content

Commit db0c14f

Browse files
feat(openapi): Add OpenAPI code generation and enhance user context support
- Add Maven OpenAPI code generation plugin with swagger dependencies now that we can use automatically generated schemas which predefined in apollo-openapi.yaml - Enhance SpringSecurityUserInfoHolder to support OpenAPI consumer context - Update AuthConfiguration to inject ConsumerService with lazy loading - Add Maven local repository to .gitignore This change enables automatic OpenAPI client/server code generation from external spec and provides dual user context support for both regular web users and OpenAPI consumers."
1 parent 4fa881a commit db0c14f

File tree

4 files changed

+186
-37
lines changed

4 files changed

+186
-37
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,6 @@ target
2727
*.orig
2828
.flattened-pom.xml
2929

30+
# Maven local repository
31+
.m2/
32+

apollo-portal/pom.xml

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
<artifactId>apollo-portal</artifactId>
2828
<name>Apollo Portal</name>
2929
<properties>
30+
<apollo.openapi.spec.url>https://raw.githubusercontent.com/tacklequestions/apollo-openapi/main/apollo-openapi.yaml</apollo.openapi.spec.url>
3031
<github.path>${project.artifactId}</github.path>
3132
<maven.build.timestamp.format>yyyyMMddHHmmss</maven.build.timestamp.format>
3233
</properties>
@@ -44,6 +45,21 @@
4445
<groupId>com.ctrip.framework.apollo</groupId>
4546
<artifactId>apollo-openapi</artifactId>
4647
</dependency>
48+
<dependency>
49+
<groupId>org.openapitools</groupId>
50+
<artifactId>jackson-databind-nullable</artifactId>
51+
<version>0.2.6</version>
52+
</dependency>
53+
<dependency>
54+
<groupId>io.swagger.core.v3</groupId>
55+
<artifactId>swagger-annotations-jakarta</artifactId>
56+
<version>2.2.22</version>
57+
</dependency>
58+
<dependency>
59+
<groupId>io.swagger.core.v3</groupId>
60+
<artifactId>swagger-models-jakarta</artifactId>
61+
<version>2.2.22</version>
62+
</dependency>
4763
<dependency>
4864
<groupId>com.ctrip.framework.apollo</groupId>
4965
<artifactId>apollo-audit-spring-boot-starter</artifactId>
@@ -198,6 +214,68 @@
198214
</replacements>
199215
</configuration>
200216
</plugin>
217+
<!-- 1) OpenAPI 代码生成:绑定 generate-sources 阶段 -->
218+
<plugin>
219+
<groupId>org.openapitools</groupId>
220+
<artifactId>openapi-generator-maven-plugin</artifactId>
221+
<version>7.15.0</version>
222+
<executions>
223+
<execution>
224+
<id>generate-openapi-sources</id>
225+
<phase>generate-sources</phase>
226+
<goals>
227+
<goal>generate</goal>
228+
</goals>
229+
<configuration>
230+
<!-- 你的 OpenAPI 契约文件 -->
231+
<inputSpec>${apollo.openapi.spec.url}</inputSpec>
232+
233+
<!-- 生成 Spring 接口/模型 -->
234+
<generatorName>spring</generatorName>
235+
236+
<!-- 输出目录(注意 openapi 会在下面再加一层 src/main/java) -->
237+
<output>${project.build.directory}/generated-sources/openapi</output>
238+
239+
<!-- 包名 -->
240+
<apiPackage>com.ctrip.framework.apollo.openapi.api</apiPackage>
241+
<modelPackage>com.ctrip.framework.apollo.openapi.model</modelPackage>
242+
<invokerPackage>com.ctrip.framework.apollo.openapi.invoker</invokerPackage>
243+
244+
<!-- 常用开关:只出接口 & 按 tag 分组,使用 java8 时间类型 -->
245+
<configOptions>
246+
<interfaceOnly>true</interfaceOnly>
247+
<useTags>true</useTags>
248+
<dateLibrary>java8</dateLibrary>
249+
</configOptions>
250+
251+
<!-- 可选:跳过严格校验(契约不完全时更宽容) -->
252+
<skipValidateSpec>true</skipValidateSpec>
253+
</configuration>
254+
</execution>
255+
</executions>
256+
</plugin>
257+
258+
<!-- 2) 把生成目录标记为源码目录,让 IDE 直接参与编译 -->
259+
<plugin>
260+
<groupId>org.codehaus.mojo</groupId>
261+
<artifactId>build-helper-maven-plugin</artifactId>
262+
<version>3.5.0</version>
263+
<executions>
264+
<execution>
265+
<id>add-openapi-sources</id>
266+
<phase>generate-sources</phase>
267+
<goals>
268+
<goal>add-source</goal>
269+
</goals>
270+
<configuration>
271+
<sources>
272+
<!-- 注意这层 src/main/java -->
273+
<source>${project.build.directory}/generated-sources/openapi/src/main/java</source>
274+
</sources>
275+
</configuration>
276+
</execution>
277+
</executions>
278+
</plugin>
201279
</plugins>
202280
</build>
203281
</project>

apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/spi/configuration/AuthConfiguration.java

Lines changed: 32 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,33 +17,9 @@
1717

1818
package com.ctrip.framework.apollo.portal.spi.configuration;
1919

20-
import com.ctrip.framework.apollo.common.condition.ConditionalOnMissingProfile;
21-
import com.ctrip.framework.apollo.core.utils.StringUtils;
22-
import com.ctrip.framework.apollo.portal.repository.AuthorityRepository;
23-
import com.ctrip.framework.apollo.portal.repository.UserRepository;
24-
import com.ctrip.framework.apollo.portal.spi.LogoutHandler;
25-
import com.ctrip.framework.apollo.portal.spi.SsoHeartbeatHandler;
26-
import com.ctrip.framework.apollo.portal.spi.UserInfoHolder;
27-
import com.ctrip.framework.apollo.portal.spi.UserService;
28-
import com.ctrip.framework.apollo.portal.spi.defaultimpl.DefaultLogoutHandler;
29-
import com.ctrip.framework.apollo.portal.spi.defaultimpl.DefaultSsoHeartbeatHandler;
30-
import com.ctrip.framework.apollo.portal.spi.defaultimpl.DefaultUserInfoHolder;
31-
import com.ctrip.framework.apollo.portal.spi.defaultimpl.DefaultUserService;
32-
import com.ctrip.framework.apollo.portal.spi.ldap.ApolloLdapAuthenticationProvider;
33-
import com.ctrip.framework.apollo.portal.spi.ldap.FilterLdapByGroupUserSearch;
34-
import com.ctrip.framework.apollo.portal.spi.ldap.LdapUserService;
35-
import com.ctrip.framework.apollo.portal.spi.oidc.ExcludeClientCredentialsClientRegistrationRepository;
36-
import com.ctrip.framework.apollo.portal.spi.oidc.OidcAuthenticationSuccessEventListener;
37-
import com.ctrip.framework.apollo.portal.spi.oidc.OidcLocalUserService;
38-
import com.ctrip.framework.apollo.portal.spi.oidc.OidcLocalUserServiceImpl;
39-
import com.ctrip.framework.apollo.portal.spi.oidc.OidcLogoutHandler;
40-
import com.ctrip.framework.apollo.portal.spi.oidc.OidcUserInfoHolder;
41-
import com.ctrip.framework.apollo.portal.spi.springsecurity.ApolloPasswordEncoderFactory;
42-
import com.ctrip.framework.apollo.portal.spi.springsecurity.SpringSecurityUserInfoHolder;
43-
import com.ctrip.framework.apollo.portal.spi.springsecurity.SpringSecurityUserService;
44-
4520
import java.text.MessageFormat;
4621
import java.util.Collections;
22+
4723
import javax.persistence.EntityManagerFactory;
4824
import javax.sql.DataSource;
4925

@@ -56,6 +32,7 @@
5632
import org.springframework.context.annotation.Bean;
5733
import org.springframework.context.annotation.Configuration;
5834
import org.springframework.context.annotation.DependsOn;
35+
import org.springframework.context.annotation.Lazy;
5936
import org.springframework.context.annotation.Profile;
6037
import org.springframework.core.annotation.Order;
6138
import org.springframework.core.env.Environment;
@@ -78,6 +55,32 @@
7855
import org.springframework.security.provisioning.JdbcUserDetailsManager;
7956
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
8057

58+
import com.ctrip.framework.apollo.common.condition.ConditionalOnMissingProfile;
59+
import com.ctrip.framework.apollo.core.utils.StringUtils;
60+
import com.ctrip.framework.apollo.openapi.service.ConsumerService;
61+
import com.ctrip.framework.apollo.portal.repository.AuthorityRepository;
62+
import com.ctrip.framework.apollo.portal.repository.UserRepository;
63+
import com.ctrip.framework.apollo.portal.spi.LogoutHandler;
64+
import com.ctrip.framework.apollo.portal.spi.SsoHeartbeatHandler;
65+
import com.ctrip.framework.apollo.portal.spi.UserInfoHolder;
66+
import com.ctrip.framework.apollo.portal.spi.UserService;
67+
import com.ctrip.framework.apollo.portal.spi.defaultimpl.DefaultLogoutHandler;
68+
import com.ctrip.framework.apollo.portal.spi.defaultimpl.DefaultSsoHeartbeatHandler;
69+
import com.ctrip.framework.apollo.portal.spi.defaultimpl.DefaultUserInfoHolder;
70+
import com.ctrip.framework.apollo.portal.spi.defaultimpl.DefaultUserService;
71+
import com.ctrip.framework.apollo.portal.spi.ldap.ApolloLdapAuthenticationProvider;
72+
import com.ctrip.framework.apollo.portal.spi.ldap.FilterLdapByGroupUserSearch;
73+
import com.ctrip.framework.apollo.portal.spi.ldap.LdapUserService;
74+
import com.ctrip.framework.apollo.portal.spi.oidc.ExcludeClientCredentialsClientRegistrationRepository;
75+
import com.ctrip.framework.apollo.portal.spi.oidc.OidcAuthenticationSuccessEventListener;
76+
import com.ctrip.framework.apollo.portal.spi.oidc.OidcLocalUserService;
77+
import com.ctrip.framework.apollo.portal.spi.oidc.OidcLocalUserServiceImpl;
78+
import com.ctrip.framework.apollo.portal.spi.oidc.OidcLogoutHandler;
79+
import com.ctrip.framework.apollo.portal.spi.oidc.OidcUserInfoHolder;
80+
import com.ctrip.framework.apollo.portal.spi.springsecurity.ApolloPasswordEncoderFactory;
81+
import com.ctrip.framework.apollo.portal.spi.springsecurity.SpringSecurityUserInfoHolder;
82+
import com.ctrip.framework.apollo.portal.spi.springsecurity.SpringSecurityUserService;
83+
8184
@Configuration
8285
public class AuthConfiguration {
8386

@@ -106,8 +109,8 @@ public static PasswordEncoder passwordEncoder() {
106109

107110
@Bean
108111
@ConditionalOnMissingBean(UserInfoHolder.class)
109-
public UserInfoHolder springSecurityUserInfoHolder(UserService userService) {
110-
return new SpringSecurityUserInfoHolder(userService);
112+
public UserInfoHolder springSecurityUserInfoHolder(UserService userService, @Lazy ConsumerService consumerService) {
113+
return new SpringSecurityUserInfoHolder(userService, consumerService);
111114
}
112115

113116
@Bean
@@ -210,8 +213,8 @@ public SsoHeartbeatHandler defaultSsoHeartbeatHandler() {
210213

211214
@Bean
212215
@ConditionalOnMissingBean(UserInfoHolder.class)
213-
public UserInfoHolder springSecurityUserInfoHolder(UserService userService) {
214-
return new SpringSecurityUserInfoHolder(userService);
216+
public UserInfoHolder springSecurityUserInfoHolder(UserService userService, @Lazy ConsumerService consumerService) {
217+
return new SpringSecurityUserInfoHolder(userService, consumerService);
215218
}
216219

217220
@Bean

apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/spi/springsecurity/SpringSecurityUserInfoHolder.java

Lines changed: 73 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,34 +16,100 @@
1616
*/
1717
package com.ctrip.framework.apollo.portal.spi.springsecurity;
1818

19-
import com.ctrip.framework.apollo.portal.entity.bo.UserInfo;
20-
import com.ctrip.framework.apollo.portal.spi.UserInfoHolder;
21-
import com.ctrip.framework.apollo.portal.spi.UserService;
19+
import java.security.Principal;
20+
21+
import javax.servlet.http.HttpServletRequest;
22+
23+
import org.springframework.context.annotation.Lazy;
2224
import org.springframework.security.core.context.SecurityContextHolder;
2325
import org.springframework.security.core.userdetails.UserDetails;
26+
import org.springframework.web.context.request.RequestContextHolder;
27+
import org.springframework.web.context.request.ServletRequestAttributes;
2428

25-
import java.security.Principal;
29+
import com.ctrip.framework.apollo.openapi.entity.Consumer;
30+
import com.ctrip.framework.apollo.openapi.service.ConsumerService;
31+
import com.ctrip.framework.apollo.portal.entity.bo.UserInfo;
32+
import com.ctrip.framework.apollo.portal.spi.UserInfoHolder;
33+
import com.ctrip.framework.apollo.portal.spi.UserService;
2634

2735
public class SpringSecurityUserInfoHolder implements UserInfoHolder {
2836

37+
2938
private final UserService userService;
39+
private final ConsumerService consumerService;
3040

31-
public SpringSecurityUserInfoHolder(UserService userService) {
41+
public SpringSecurityUserInfoHolder(UserService userService,
42+
@Lazy ConsumerService consumerService) {
3243
this.userService = userService;
44+
this.consumerService = consumerService;
3345
}
3446

3547
@Override
3648
public UserInfo getUser() {
37-
String userId = this.getCurrentUsername();
38-
UserInfo userInfoFound = this.userService.findByUserId(userId);
49+
// 首先尝试从OpenAPI Consumer上下文获取用户信息
50+
UserInfo consumerUserInfo = getConsumerUserInfo();
51+
if (consumerUserInfo != null) {
52+
return consumerUserInfo;
53+
}
54+
55+
// 回退到Spring Security上下文
56+
String userId = getCurrentUsername();
57+
UserInfo userInfoFound = userService.findByUserId(userId);
3958
if (userInfoFound != null) {
4059
return userInfoFound;
4160
}
61+
4262
UserInfo userInfo = new UserInfo();
4363
userInfo.setUserId(userId);
4464
return userInfo;
4565
}
4666

67+
/**
68+
* 从OpenAPI Consumer上下文获取用户信息
69+
*/
70+
private UserInfo getConsumerUserInfo() {
71+
try {
72+
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
73+
if (attributes == null) {
74+
return null;
75+
}
76+
77+
HttpServletRequest request = attributes.getRequest();
78+
String requestURI = request.getRequestURI();
79+
80+
// 只对OpenAPI请求处理Consumer用户信息
81+
if (!requestURI.startsWith("/openapi/")) {
82+
return null;
83+
}
84+
85+
// 获取Consumer ID
86+
Object consumerIdObj = request.getAttribute("ApolloConsumerId");
87+
if (consumerIdObj == null) {
88+
return null;
89+
}
90+
91+
long consumerId = Long.parseLong(consumerIdObj.toString());
92+
Consumer consumer = consumerService.getConsumerByConsumerId(consumerId);
93+
if (consumer == null) {
94+
return null;
95+
}
96+
97+
// 构建基于Consumer的用户信息
98+
UserInfo userInfo = new UserInfo();
99+
userInfo.setUserId(consumer.getOwnerName());
100+
userInfo.setName(consumer.getName());
101+
userInfo.setEmail(consumer.getOwnerEmail());
102+
103+
return userInfo;
104+
} catch (Exception e) {
105+
// 如果获取Consumer信息失败,返回null,让系统回退到默认方式
106+
return null;
107+
}
108+
}
109+
110+
/**
111+
* 从Spring Security上下文获取用户名
112+
*/
47113
private String getCurrentUsername() {
48114
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
49115
if (principal instanceof UserDetails) {
@@ -54,5 +120,4 @@ private String getCurrentUsername() {
54120
}
55121
return String.valueOf(principal);
56122
}
57-
58123
}

0 commit comments

Comments
 (0)