gpt4 book ai didi

spring-boot - Spring Boot Data JPA @CreatedBy和@UpdatedBy未使用OIDC身份验证进行填充

转载 作者:行者123 更新时间:2023-12-04 17:06:46 29 4
gpt4 key购买 nike

我想让Spring JPA审核与Spring Boot一起使用,我正在使用Spring Security的最新功能通过Keycloak进行身份验证。

springBootVersion = '2.1.0.RC1'

我正在跟踪 Spring 安全团队 https://github.com/jzheaux/messaging-app/tree/springone2018-demo/resource-server的示例

ResourceServerConfig.kt
@EnableWebSecurity
class OAuth2ResourceServerSecurityConfiguration(val resourceServerProperties: OAuth2ResourceServerProperties) : WebSecurityConfigurerAdapter() {

@Throws(Exception::class)
override fun configure(http: HttpSecurity) {
http
.authorizeRequests()
.antMatchers("/api/**").authenticated()
.anyRequest().anonymous()
.and()
.oauth2ResourceServer()
.authenticationEntryPoint(MoreInformativeAuthenticationEntryPoint())
.jwt()
.jwtAuthenticationConverter(GrantedAuthoritiesExtractor())
.decoder(jwtDecoder())

}

private fun jwtDecoder(): JwtDecoder {
val issuerUri = this.resourceServerProperties.jwt.issuerUri

val jwtDecoder = JwtDecoders.fromOidcIssuerLocation(issuerUri) as NimbusJwtDecoderJwkSupport

val withIssuer = JwtValidators.createDefaultWithIssuer(issuerUri)
val withAudience = DelegatingOAuth2TokenValidator(withIssuer, AudienceValidator())
jwtDecoder.setJwtValidator(withAudience)

return jwtDecoder
}
}

class MoreInformativeAuthenticationEntryPoint : AuthenticationEntryPoint {
private val delegate = BearerTokenAuthenticationEntryPoint()

private val mapper = ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL)

@Throws(IOException::class, ServletException::class)
override fun commence(request: HttpServletRequest, response: HttpServletResponse,
reason: AuthenticationException) {

this.delegate.commence(request, response, reason)

if (reason.cause is JwtValidationException) {
val validationException = reason.cause as JwtValidationException
val errors = validationException.errors
this.mapper.writeValue(response.writer, errors)
}
}
}

class GrantedAuthoritiesExtractor : JwtAuthenticationConverter() {
override fun extractAuthorities(jwt: Jwt): Collection<GrantedAuthority> {
val scopes = jwt.claims["scope"].toString().split(" ")
return scopes.map { SimpleGrantedAuthority(it) }
}
}

class AudienceValidator : OAuth2TokenValidator<Jwt> {

override fun validate(token: Jwt): OAuth2TokenValidatorResult {
val audience = token.audience
return if (!CollectionUtils.isEmpty(audience) && audience.contains("mobile-client")) {
OAuth2TokenValidatorResult.success()
} else {
OAuth2TokenValidatorResult.failure(MISSING_AUDIENCE)
}
}

companion object {
private val MISSING_AUDIENCE = BearerTokenError("invalid_token", HttpStatus.UNAUTHORIZED,
"The token is missing a required audience.", null)
}
}

application.yaml
spring:
application:
name: sociter
datasource:
url: jdbc:postgresql://localhost:5432/sociter
username: postgres
password: 123123
driver-class-name: org.postgresql.Driver
jpa:
hibernate:
ddl-auto: update
security:
oauth2:
resourceserver:
jwt:
jwk-set-uri: http://localhost:8080/auth/realms/sociter/protocol/openid-connect/certs
issuer-uri: http://localhost:8080/auth/realms/sociter

JpaAuditingConfiguration.kt
@Configuration
@EnableJpaAuditing
(auditorAwareRef = "auditorProvider")
class JpaAuditingConfiguration {

@Bean
fun auditorProvider(): AuditorAware<String> {
return if (SecurityContextHolder.getContext().authentication != null) {
val oauth2 = SecurityContextHolder.getContext().authentication as JwtAuthenticationToken
val claims = oauth2.token.claims
val userId = claims["sub"]
AuditorAware { Optional.of(userId.toString()) }
} else
AuditorAware { Optional.of("Unknown") }
}
}

BaseEntity.kt
@MappedSuperclass
@JsonIgnoreProperties(value = ["createdOn, updatedOn"], allowGetters = true)
@EntityListeners(AuditingEntityListener::class)
abstract class BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
val id: UUID = UUID.randomUUID()

@Column(nullable = false, updatable = false)
@CreatedDate
var createdOn: LocalDateTime = LocalDateTime.now()

@Column(nullable = true)
@LastModifiedDate
var updatedOn: LocalDateTime? = null

@Column(nullable = true, updatable = false)
@CreatedBy
var createdBy: String? = null

@Column(nullable = true)
@LastModifiedBy
var updatedBy: String? = null
}

我正在将createdBy和UpdatedBy设置为Unknown。在调试过程中,auditProvider bean被调用,并将用户设置为Unknown,但如果条件仍然为false,则在传递access_token时。

不知道我在想什么。

最佳答案

我能够复制您的问题,但是使用等效的Java设置。问题出在您的JpaAuditingConfiguration类中。如果您仔细观察当前的JpaAuditingConfiguration类,则会发生以下情况:

  • 在Spring初始化期间,auditorProvider()函数将尝试生成Bean。
  • 会先在应用程序启动期间(在应用程序启动期间)检查身份验证条件,并且该线程(启动Spring Boot App的线程)根本不是经过身份验证的线程。因此,它将返回一个AuditorAware实例,该实例将始终返回Unknown

  • 您需要按如下方式更改此类(抱歉,我是用Java编写的,请将其转换为Kotlin):
    @Configuration
    @EnableJpaAuditing(auditorAwareRef = "auditorProvider")
    public class JPAAuditConfig {

    @Bean
    public AuditorAware<String> auditorProvider() {
    return new AuditorAware<String>() {
    @Override
    public String getCurrentAuditor() {
    if (SecurityContextHolder.getContext().getAuthentication() != null) {
    OAuth2Authentication auth = (OAuth2Authentication) SecurityContextHolder.getContext().getAuthentication();
    Object principal = auth.getUserAuthentication().getPrincipal();
    CustomUserDetails userDetails = (CustomUserDetails) principal;
    return userDetails.getUsername();
    } else {
    return "Unknown";
    }
    }
    };
    }
    }
    你可以试试看另外,我怀疑使用当前的设置,您会正确填充updatedOn和createdOn。如果是,那意味着所有的JPA和EntityListener魔术实际上都在起作用。您只需要在运行时返回 AuditorAware的正确实现即可。
    另请注意,我的配置未使用 JwtAuthenticationToken,而是使用了 CustomUserDetails实现。但这与您的问题无关,您当然可以使用当前的 token 类型( JwtAuthenticationToken)。就是这样,我有一个自己的小应用程序正在运行,并在其中复制了您的问题。

    关于spring-boot - Spring Boot Data JPA @CreatedBy和@UpdatedBy未使用OIDC身份验证进行填充,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52907096/

    29 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com