diff --git a/pom.xml b/pom.xml index 5295f86..138b5f4 100644 --- a/pom.xml +++ b/pom.xml @@ -32,22 +32,26 @@ UTF-8 17 17 - 3.2.0 - 2.0.9 + 3.5.3 + + 1.1.0.RELEASE + 1.1.0.RELEASE + + + com.arrokoth.framework.boot + arrokoth-framework-starter + ${arrokoth.version} + + com.arrokoth.framework basic-authorization-server 1.0.1-RELEASE - - org.springframework.boot - spring-boot-starter-web - - org.springframework.boot @@ -122,8 +126,21 @@ + + + + com.arrokoth.framework.boot + arrokoth-framework-springboot-dependencies + ${arrokoth.bom.version} + pom + import + + + + + ${project.basedir}/target org.springframework.boot @@ -139,7 +156,8 @@ - build-info + build-info + repackage @@ -150,20 +168,27 @@ maven-compiler-plugin 3.11.0 - true + true 17 17 - true - true - UTF-8 - false + true + true + UTF-8 + false org.apache.maven.plugins maven-surefire-plugin - 2.22.0 + + + false + + **/*Test.java + **/*Tests.java + + diff --git a/src/main/java/com/arrokoth/standalone/authorization/StandaloneServerApplication.java b/src/main/java/com/arrokoth/standalone/authorization/StandaloneServerApplication.java index 31c2553..2c743ff 100644 --- a/src/main/java/com/arrokoth/standalone/authorization/StandaloneServerApplication.java +++ b/src/main/java/com/arrokoth/standalone/authorization/StandaloneServerApplication.java @@ -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 { diff --git a/src/main/java/com/arrokoth/standalone/authorization/config/AuthorizationServerAutoConfigurer.java b/src/main/java/com/arrokoth/standalone/authorization/config/AuthorizationServerAutoConfigurer.java index ae6dcd3..bc62348 100644 --- a/src/main/java/com/arrokoth/standalone/authorization/config/AuthorizationServerAutoConfigurer.java +++ b/src/main/java/com/arrokoth/standalone/authorization/config/AuthorizationServerAutoConfigurer.java @@ -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 - } } \ No newline at end of file diff --git a/src/main/java/com/arrokoth/standalone/authorization/config/OAuth2Config.java b/src/main/java/com/arrokoth/standalone/authorization/config/OAuth2Config.java new file mode 100644 index 0000000..82c3776 --- /dev/null +++ b/src/main/java/com/arrokoth/standalone/authorization/config/OAuth2Config.java @@ -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 + } + +} diff --git a/src/main/java/com/arrokoth/standalone/authorization/config/SecurityWebAutoConfigurer.java b/src/main/java/com/arrokoth/standalone/authorization/config/SecurityWebAutoConfigurer.java index 18e52c0..e69a87c 100644 --- a/src/main/java/com/arrokoth/standalone/authorization/config/SecurityWebAutoConfigurer.java +++ b/src/main/java/com/arrokoth/standalone/authorization/config/SecurityWebAutoConfigurer.java @@ -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); diff --git a/src/main/java/com/arrokoth/standalone/authorization/controller/AuthorizationController.java b/src/main/java/com/arrokoth/standalone/authorization/controller/AuthorizationController.java index d1ca468..dbb6f6a 100644 --- a/src/main/java/com/arrokoth/standalone/authorization/controller/AuthorizationController.java +++ b/src/main/java/com/arrokoth/standalone/authorization/controller/AuthorizationController.java @@ -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 withDescription(Set scopes) { + Set 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 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); + } + } + + + + } \ No newline at end of file diff --git a/src/main/java/com/arrokoth/standalone/authorization/store/RegisteredClientRepositoryStore.java b/src/main/java/com/arrokoth/standalone/authorization/store/RegisteredClientRepositoryStore.java index 8887d44..c100513 100644 --- a/src/main/java/com/arrokoth/standalone/authorization/store/RegisteredClientRepositoryStore.java +++ b/src/main/java/com/arrokoth/standalone/authorization/store/RegisteredClientRepositoryStore.java @@ -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") diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 3673b40..d18dcec 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -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: