使用框架 重构

This commit is contained in:
wangjianhong
2025-07-16 16:15:33 +08:00
parent 1ee4f97ec2
commit ca7bf71474
8 changed files with 187 additions and 61 deletions

53
pom.xml
View File

@@ -32,22 +32,26 @@
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<spring.boot.version>3.2.0</spring.boot.version>
<slf4j.version>2.0.9</slf4j.version>
<spring.boot.version>3.5.3</spring.boot.version>
<arrokoth.version>1.1.0.RELEASE</arrokoth.version>
<arrokoth.bom.version>1.1.0.RELEASE</arrokoth.bom.version>
</properties>
<dependencies>
<dependency>
<groupId>com.arrokoth.framework.boot</groupId>
<artifactId>arrokoth-framework-starter</artifactId>
<version>${arrokoth.version}</version>
</dependency>
<!-- 通用认证模块 -->
<dependency>
<groupId>com.arrokoth.framework</groupId>
<artifactId>basic-authorization-server</artifactId>
<version>1.0.1-RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
@@ -122,8 +126,21 @@
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.arrokoth.framework.boot</groupId>
<artifactId>arrokoth-framework-springboot-dependencies</artifactId>
<version>${arrokoth.bom.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<directory>${project.basedir}/target</directory>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
@@ -139,7 +156,8 @@
<executions>
<execution>
<goals>
<goal>build-info</goal>
<goal>build-info</goal> <!-- 生成构建信息 -->
<goal>repackage</goal> <!-- 默认 goal必须保留 -->
</goals>
</execution>
</executions>
@@ -150,20 +168,27 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<parameters>true</parameters>
<parameters>true</parameters> <!-- 支持获取方法参数名 -->
<source>17</source>
<target>17</target>
<fork>true</fork>
<verbose>true</verbose>
<encoding>UTF-8</encoding>
<showWarnings>false</showWarnings>
<fork>true</fork> <!-- 使用独立 JVM 编译 -->
<verbose>true</verbose> <!-- 输出详细编译信息 -->
<encoding>UTF-8</encoding> <!-- 源码编码 -->
<showWarnings>false</showWarnings> <!-- 不显示警告 -->
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<!-- JUnit 5 requires Surefire version 2.22.0 or higher -->
<version>2.22.0</version>
<configuration>
<!-- 可选配置项 -->
<failIfNoTests>false</failIfNoTests> <!-- 即使没有测试也不失败 -->
<includes>
<include>**/*Test.java</include>
<include>**/*Tests.java</include>
</includes>
</configuration>
</plugin>
</plugins>
</build>

View File

@@ -1,12 +1,12 @@
package com.arrokoth.standalone.authorization;
import org.mybatis.spring.annotation.MapperScan;
import com.arrokoth.basic.annotation.EnableArrokothBasicModule;
import com.arrokoth.framework.boot.annotation.EnableGracefulRestResponse;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@MapperScan("com.arrokoth.basic.mapper")
@ComponentScan(basePackages = "com.arrokoth")
@EnableGracefulRestResponse
@EnableArrokothBasicModule
@SpringBootApplication
public class StandaloneServerApplication {

View File

@@ -12,10 +12,7 @@ import org.springframework.security.config.annotation.method.configuration.Enabl
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
@@ -76,19 +73,6 @@ public class AuthorizationServerAutoConfigurer {
}
@Bean
public AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder()
.authorizationEndpoint(authorizationServerProperties.getAuthorizationEndpoint()) // 授权端点路径
.issuer(authorizationServerProperties.getIssuer()) // 授权服务器的唯一标识符Issuer
.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(); // 仅用于演示生产环境请使用BCryptPasswordEncoder
}
}

View File

@@ -0,0 +1,30 @@
package com.arrokoth.standalone.authorization.config;
import com.arrokoth.basic.properties.AuthorizationServerProperties;
import com.arrokoth.basic.properties.SecurityWebProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
@Configuration
public class OAuth2Config {
@Bean
public AuthorizationServerSettings authorizationServerSettings(AuthorizationServerProperties authorizationServerProperties,
SecurityWebProperties securityWebProperties) {
return AuthorizationServerSettings.builder()
.authorizationEndpoint(authorizationServerProperties.getAuthorizationEndpoint()) // 授权端点路径
.issuer(authorizationServerProperties.getIssuer()) // 授权服务器的唯一标识符Issuer
.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(); // 仅用于演示生产环境请使用BCryptPasswordEncoder
}
}

View File

@@ -1,12 +1,10 @@
package com.arrokoth.standalone.authorization.config;
import com.arrokoth.basic.autoconfiguration.BasicAutoConfiguration;
import com.arrokoth.basic.properties.SecurityWebProperties;
import com.arrokoth.standalone.authorization.filter.JwtRequestFilter;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -27,7 +25,6 @@ import java.util.List;
@Slf4j
@Configuration
@AutoConfigureBefore(BasicAutoConfiguration.class)
@AllArgsConstructor
@EnableConfigurationProperties(SecurityWebProperties.class)
public class SecurityWebAutoConfigurer {
@@ -55,10 +52,17 @@ public class SecurityWebAutoConfigurer {
.loginProcessingUrl(securityWebProperties.getLoginProcessingUrl())
.permitAll()
)
.logout(logout -> logout
.logoutUrl(SecurityWebProperties.AXIOS_LOGOUT_PROCESSING_URL)
.logoutSuccessUrl(securityWebProperties.getLogoutSuccessUrl())
.invalidateHttpSession(true) // 注销时销毁 session
.deleteCookies("JSESSIONID", "Authorization")
.permitAll()
);
return http.build();
}
@@ -72,10 +76,10 @@ public class SecurityWebAutoConfigurer {
public CorsConfigurationSource corsConfigurationSource() {
log.info("Configuring cors configuration source");
CorsConfiguration configuration = new CorsConfiguration();
configuration.setExposedHeaders(List.of("Authorization")); // 允许前端访问Authorization头
configuration.setAllowedOrigins(List.of("*")); // 替换为前端域名
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
configuration.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type", "X-Requested-With", "accept"));
configuration.setExposedHeaders(List.of("Authorization")); // 允许前端访问Authorization头
configuration.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type", "X-Requested-With", "accept", "X-XSRF-TOKEN"));
configuration.setAllowCredentials(false);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);

View File

@@ -2,14 +2,24 @@ package com.arrokoth.standalone.authorization.controller;
import com.arrokoth.standalone.authorization.service.OAuth2ConsentService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.security.Principal;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@Slf4j
@Lazy
@Controller
@RequiredArgsConstructor
public class AuthorizationController {
@@ -19,6 +29,7 @@ public class AuthorizationController {
private final OAuth2ConsentService oAuth2ConsentService;
@GetMapping("/login")
public String home() {
return "login"; // 对应templates/login.html
@@ -33,15 +44,71 @@ public class AuthorizationController {
@RequestParam("state") String state,
@RequestParam(name = "user_code", required = false) String userCode) {
OAuth2ConsentService.ConsentResponse viewModel = oAuth2ConsentService.getConsentDetails(principal, clientId, scope, state, userCode);
OAuth2ConsentService.ConsentResponse viewModel = oAuth2ConsentService.getConsentDetails(principal, clientId, scope, state, principal.getName());
model.addAttribute("clientId", clientId);
model.addAttribute("state", state);
model.addAttribute("scopes", withDescription(viewModel.scopesToApprove()));
model.addAttribute("previouslyApprovedScopes", withDescription(viewModel.previouslyApprovedScopes()));
model.addAttribute("principalName", principal.getName());
model.addAttribute("userCode", userCode);
if (StringUtils.hasText(userCode)) {
model.addAttribute("requestURI", "/oauth2/device_verification");
} else {
model.addAttribute("requestURI", "/oauth2/authorize");
}
// model.addAttribute("clientId", viewModel.clientId());
// model.addAttribute("state", viewModel.state());
// model.addAttribute("scopes", viewModel.scopes());
model.addAttribute("previouslyApprovedScopes", viewModel.previouslyApprovedScopes());
// model.addAttribute("principalName", viewModel.principalName());
// model.addAttribute("userCode", viewModel.userCode());
// model.addAttribute("requestURI", viewModel.requestURI());
return "consent";
}
private static Set<ScopeWithDescription> withDescription(Set<String> scopes) {
Set<ScopeWithDescription> scopeWithDescriptions = new HashSet<>();
for (String scope : scopes) {
scopeWithDescriptions.add(new ScopeWithDescription(scope));
}
return scopeWithDescriptions;
}
public static class ScopeWithDescription {
private static final String DEFAULT_DESCRIPTION = "UNKNOWN SCOPE - We cannot provide information about this permission, use caution when granting this.";
private static final Map<String, String> scopeDescriptions = new HashMap<>();
static {
scopeDescriptions.put(
OidcScopes.PROFILE,
"This application will be able to read your profile information."
);
scopeDescriptions.put(
"message.read",
"This application will be able to read your message."
);
scopeDescriptions.put(
"message.write",
"This application will be able to add new messages. It will also be able to edit and delete existing messages."
);
scopeDescriptions.put(
"user.read",
"This application will be able to read your user information."
);
scopeDescriptions.put(
"other.scope",
"This is another scope example of a scope description."
);
}
public final String scope;
public final String description;
ScopeWithDescription(String scope) {
this.scope = scope;
this.description = scopeDescriptions.getOrDefault(scope, DEFAULT_DESCRIPTION);
}
}
}

View File

@@ -18,12 +18,12 @@ import java.util.UUID;
@Configuration
public class RegisteredClientRepositoryStore {
@Bean
public RegisteredClientRepository registeredClientRepository() {
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
RegisteredClient oidcClient = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("messaging-client")
.clientSecret(bCryptPasswordEncoder.encode("secret"))
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
@@ -31,6 +31,7 @@ public class RegisteredClientRepositoryStore {
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.redirectUri("http://127.0.0.1:8091/login/oauth2/code/messaging-client-oidc")
.postLogoutRedirectUri("http://127.0.0.1:8080/logged-out")
.scope(OidcScopes.OPENID)
.scope(OidcScopes.PROFILE)
.scope("message.read")

View File

@@ -1,6 +1,32 @@
server:
port: 8080
arrokoth:
authorization:
server:
consent-page: /oauth2/consent
authorization-endpoint: /oauth2/authorize
issuer: http://127.0.0.1
security:
web:
swagger-ui: false
login-page: /login
logout-success-url: /login?logout
permit-urls:
- /home/login
- /login/oauth2/**
- /oauth2/token
# RestApi增强配置
graceful-rest-response:
enabled: true
banner: true
print-exception-in-global-advice: true
origin-exception-using-detail-message: false
exclude-urls:
- "/swagger-resources"
- "/v3/api-docs/*"
- "/v2/api-docs"
spring:
@@ -27,19 +53,8 @@ spring:
max-wait: 2000ms # 获取连接最大等待时间
arrokoth:
authorization:
server:
consent-page: /oauth2/consent
authorization-endpoint: /oauth2/authorize
issuer: https://www.arrokoth-info.com
security:
web:
swagger-ui: false
login-page: /login
logout-success-url: /login?logout
permit-urls:
- /home/login
logging: