Spring 带有 kotlin 的安全授权服务器 0.2.0 为授权代码流提供了 WhiteLabel 错误页面
Spring Security Authorization Server 0.2.0 with kotlin gives WhiteLabel Error Page for Authorization Code Flow
我正在尝试使用 kotlin 实现官方授权服务器模板 (https://github.com/spring-projects/spring-authorization-server/tree/main/samples/default-authorizationserver)。
内存中的用户身份验证工作得很好,但是当我尝试使用授权代码流时,我收到了一个烦人的白标签错误页面:
我正在实施的代码可在 https://github.com/RichardSobreiro/kotlin-spring-security-5-simple
复现过程如下:
使用浏览器发起GET请求:http://localhost:9000/authorize?response_type=code&scope=openid&client_id=yourClientId&state=STATE&redirect_uri=http:/ /127.0.0.1:8080/login/oauth2/code/messaging-client-oidc
您将被重定向到登录页面。
输入凭据用户名“pele”和密码“123456”后,出现 404 错误。
我已经检查了我的项目的包层次结构以避免组件扫描问题,并且还在我的 etc/host 文件 [127.0.0.1 auth-server] 中输入了以下条目,但没有任何帮助我解决我的问题。
这是我的 AuthorizationServerConfig.kt class:
package br.com.cbauthserver.authserverconfig
import br.com.cbauthserver.jwks.KeyGeneratorUtils
import com.nimbusds.jose.jwk.JWKSet
import com.nimbusds.jose.jwk.source.JWKSource
import com.nimbusds.jose.proc.SecurityContext
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.Ordered
import org.springframework.core.annotation.Order
import org.springframework.security.config.Customizer
import org.springframework.security.config.Customizer.withDefaults
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration
import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer
import org.springframework.security.core.userdetails.User
import org.springframework.security.core.userdetails.UserDetailsService
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
import org.springframework.security.oauth2.core.AuthorizationGrantType
import org.springframework.security.oauth2.core.ClientAuthenticationMethod
import org.springframework.security.oauth2.core.oidc.OidcScopes
import org.springframework.security.oauth2.jwt.JwtDecoder
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository
import org.springframework.security.oauth2.server.authorization.config.ProviderSettings
import org.springframework.security.oauth2.server.authorization.config.TokenSettings
import org.springframework.security.provisioning.InMemoryUserDetailsManager
import org.springframework.security.web.SecurityFilterChain
import java.time.Duration
import java.util.*
import org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository
@Configuration(proxyBeanMethods = false)
class AuthorizationServerConfig(
) {
private val issuerUrl = "http://auth-server:9000"
private val yourClientId = "yourClientId"
private val yourSecret = "yourSecret"
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
fun authorizationServerSecurityFilterChain(http: HttpSecurity): SecurityFilterChain {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
return http.formLogin(Customizer.withDefaults()).build();
return http.build()
}
@Bean
fun registeredClientRepository (): RegisteredClientRepository {
val registeredClient: RegisteredClient = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId(yourClientId)
.clientSecret(yourSecret)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT)
.redirectUri("http://127.0.0.1:8080/login/oauth2/code/messaging-client-oidc")
.scope(OidcScopes.OPENID)
//.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
.build()
return InMemoryRegisteredClientRepository(listOf(registeredClient))
}
@Bean
fun jwkSource(): JWKSource<SecurityContext> {
val rsaKey = KeyGeneratorUtils.generateRSAKey()
val jwkSet = JWKSet(rsaKey)
return JWKSource<SecurityContext> { jwkSelector, _ -> jwkSelector.select(jwkSet) }
}
@Bean
fun jwtDecoder(jwkSource: JWKSource<SecurityContext>): JwtDecoder {
return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource)
}
@Bean
fun providerSettings(): ProviderSettings {
return ProviderSettings.builder().issuer(issuerUrl).build()
}
@Bean
fun passwordEncoder() = BCryptPasswordEncoder(10)
@Bean
fun users(): UserDetailsService {
val user = User.builder()
.username("pele")
.password("$2a$10$b.Rm.8NeuT7hS3Qwy1RPGuuHNMzjEk01vM7ExvW/h11KAHainYBfK")
//.password("123456")
.roles("USER")
.build()
val admin = User.builder()
.username("garrincha")
.password("$2a$10$b.Rm.8NeuT7hS3Qwy1RPGuuHNMzjEk01vM7ExvW/h11KAHainYBfK")
//.password("123456")
.roles("USER", "ADMIN")
.build()
return InMemoryUserDetailsManager(user, admin)
}
}
有人可以帮我吗?
您正在混合密码编码,而没有提供可以处理多种编码的PasswordEncoder
。
您已经定义了一个 BCryptPasswordEncoder
bean,它将替换默认的密码编码器
@Bean
fun passwordEncoder() = BCryptPasswordEncoder(10)
但是,当 RegisteredClient
创建时,它使用明文密码
val registeredClient: RegisteredClient = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("yourClientId")
.clientSecret("yourSecret")
一种解决方案是对 clientSecret 进行编码
val registeredClient: RegisteredClient = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("yourClientId")
.clientSecret(passwordEncoder.encode("yourSecret"))
另一个选择是使用 DelegatingPasswordEncoder
如果您需要使用不同的编码来存储您的密码/秘密。
我正在尝试使用 kotlin 实现官方授权服务器模板 (https://github.com/spring-projects/spring-authorization-server/tree/main/samples/default-authorizationserver)。
内存中的用户身份验证工作得很好,但是当我尝试使用授权代码流时,我收到了一个烦人的白标签错误页面:
我正在实施的代码可在 https://github.com/RichardSobreiro/kotlin-spring-security-5-simple
复现过程如下:
使用浏览器发起GET请求:http://localhost:9000/authorize?response_type=code&scope=openid&client_id=yourClientId&state=STATE&redirect_uri=http:/ /127.0.0.1:8080/login/oauth2/code/messaging-client-oidc
您将被重定向到登录页面。 输入凭据用户名“pele”和密码“123456”后,出现 404 错误。
我已经检查了我的项目的包层次结构以避免组件扫描问题,并且还在我的 etc/host 文件 [127.0.0.1 auth-server] 中输入了以下条目,但没有任何帮助我解决我的问题。
这是我的 AuthorizationServerConfig.kt class:
package br.com.cbauthserver.authserverconfig
import br.com.cbauthserver.jwks.KeyGeneratorUtils
import com.nimbusds.jose.jwk.JWKSet
import com.nimbusds.jose.jwk.source.JWKSource
import com.nimbusds.jose.proc.SecurityContext
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.Ordered
import org.springframework.core.annotation.Order
import org.springframework.security.config.Customizer
import org.springframework.security.config.Customizer.withDefaults
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration
import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer
import org.springframework.security.core.userdetails.User
import org.springframework.security.core.userdetails.UserDetailsService
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
import org.springframework.security.oauth2.core.AuthorizationGrantType
import org.springframework.security.oauth2.core.ClientAuthenticationMethod
import org.springframework.security.oauth2.core.oidc.OidcScopes
import org.springframework.security.oauth2.jwt.JwtDecoder
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository
import org.springframework.security.oauth2.server.authorization.config.ProviderSettings
import org.springframework.security.oauth2.server.authorization.config.TokenSettings
import org.springframework.security.provisioning.InMemoryUserDetailsManager
import org.springframework.security.web.SecurityFilterChain
import java.time.Duration
import java.util.*
import org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository
@Configuration(proxyBeanMethods = false)
class AuthorizationServerConfig(
) {
private val issuerUrl = "http://auth-server:9000"
private val yourClientId = "yourClientId"
private val yourSecret = "yourSecret"
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
fun authorizationServerSecurityFilterChain(http: HttpSecurity): SecurityFilterChain {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
return http.formLogin(Customizer.withDefaults()).build();
return http.build()
}
@Bean
fun registeredClientRepository (): RegisteredClientRepository {
val registeredClient: RegisteredClient = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId(yourClientId)
.clientSecret(yourSecret)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT)
.redirectUri("http://127.0.0.1:8080/login/oauth2/code/messaging-client-oidc")
.scope(OidcScopes.OPENID)
//.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
.build()
return InMemoryRegisteredClientRepository(listOf(registeredClient))
}
@Bean
fun jwkSource(): JWKSource<SecurityContext> {
val rsaKey = KeyGeneratorUtils.generateRSAKey()
val jwkSet = JWKSet(rsaKey)
return JWKSource<SecurityContext> { jwkSelector, _ -> jwkSelector.select(jwkSet) }
}
@Bean
fun jwtDecoder(jwkSource: JWKSource<SecurityContext>): JwtDecoder {
return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource)
}
@Bean
fun providerSettings(): ProviderSettings {
return ProviderSettings.builder().issuer(issuerUrl).build()
}
@Bean
fun passwordEncoder() = BCryptPasswordEncoder(10)
@Bean
fun users(): UserDetailsService {
val user = User.builder()
.username("pele")
.password("$2a$10$b.Rm.8NeuT7hS3Qwy1RPGuuHNMzjEk01vM7ExvW/h11KAHainYBfK")
//.password("123456")
.roles("USER")
.build()
val admin = User.builder()
.username("garrincha")
.password("$2a$10$b.Rm.8NeuT7hS3Qwy1RPGuuHNMzjEk01vM7ExvW/h11KAHainYBfK")
//.password("123456")
.roles("USER", "ADMIN")
.build()
return InMemoryUserDetailsManager(user, admin)
}
}
有人可以帮我吗?
您正在混合密码编码,而没有提供可以处理多种编码的PasswordEncoder
。
您已经定义了一个 BCryptPasswordEncoder
bean,它将替换默认的密码编码器
@Bean
fun passwordEncoder() = BCryptPasswordEncoder(10)
但是,当 RegisteredClient
创建时,它使用明文密码
val registeredClient: RegisteredClient = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("yourClientId")
.clientSecret("yourSecret")
一种解决方案是对 clientSecret 进行编码
val registeredClient: RegisteredClient = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("yourClientId")
.clientSecret(passwordEncoder.encode("yourSecret"))
另一个选择是使用 DelegatingPasswordEncoder
如果您需要使用不同的编码来存储您的密码/秘密。