You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Spring Data JPA审计中用外键替代用户名

解决方案

1. 调整审计模型字段类型

首先修改AuditModel.kt中createdBy和lastModifiedBy的类型,从原来的String(存储username)改为用户主键类型(比如Long,对应EmployeeModel的外键):

import jakarta.persistence.*
import org.springframework.data.annotation.CreatedBy
import org.springframework.data.annotation.CreatedDate
import org.springframework.data.annotation.LastModifiedBy
import org.springframework.data.annotation.LastModifiedDate
import java.time.LocalDateTime

abstract class AuditModel {
    @CreatedBy
    var createdBy: Long? = null  // 改为用户ID类型(如Long)

    @CreatedDate
    @Temporal(TemporalType.TIMESTAMP)
    var createdDate: LocalDateTime? = null

    @LastModifiedBy
    var lastModifiedBy: Long? = null  // 同步修改类型

    @LastModifiedDate
    @Temporal(TemporalType.TIMESTAMP)
    var lastModifiedDate: LocalDateTime? = null
}

如果需要直接关联EmployeeModel实体而非仅存ID,可改为关联映射:

abstract class AuditModel {
    @CreatedBy
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "created_by")
    var createdBy: EmployeeModel? = null

    @LastModifiedBy
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "last_modified_by")
    var lastModifiedBy: EmployeeModel? = null

    // 日期字段保持不变
}

2. 自定义UserDetails携带用户ID

确保登录后的认证信息包含用户主键,需要自定义UserDetails实现类,在登录时将用户ID/实体注入:

import org.springframework.security.core.GrantedAuthority
import org.springframework.security.core.userdetails.UserDetails

class EmployeeUserDetails(val employee: EmployeeModel) : UserDetails {
    override fun getAuthorities(): MutableCollection<out GrantedAuthority> {
        // 根据实际需求返回用户权限集合
        return mutableListOf()
    }

    override fun getPassword(): String = employee.password
    override fun getUsername(): String = employee.username
    override fun isAccountNonExpired(): Boolean = true
    override fun isAccountNonLocked(): Boolean = true
    override fun isCredentialsNonExpired(): Boolean = true
    override fun isEnabled(): Boolean = true

    // 提供获取用户ID的方法
    fun getEmployeeId(): Long = employee.id
}

3. 修改AuditorAwareImpl获取用户ID

调整AuditorAwareImpl.kt的泛型为用户ID类型(或实体类型),从SecurityContextHolder中提取当前登录用户的主键:

import org.springframework.data.domain.AuditorAware
import org.springframework.security.core.context.SecurityContextHolder
import java.util.Optional

@Component
class AuditorAwareImpl : AuditorAware<Long> {  // 泛型匹配审计字段类型(Long或EmployeeModel)
    override fun getCurrentAuditor(): Optional<Long> {
        val authentication = SecurityContextHolder.getContext().authentication
        
        // 过滤未认证或默认匿名用户的情况
        if (authentication == null || 
            !authentication.isAuthenticated || 
            authentication.principal == "anonymousUser") {
            return Optional.empty()
        }

        // 从自定义UserDetails中提取用户ID
        val userDetails = authentication.principal as EmployeeUserDetails
        return Optional.of(userDetails.getEmployeeId())
    }
}

如果是关联实体的情况,泛型改为EmployeeModel,返回实体即可:

class AuditorAwareImpl : AuditorAware<EmployeeModel> {
    override fun getCurrentAuditor(): Optional<EmployeeModel> {
        // 认证判断逻辑同上
        val userDetails = authentication.principal as EmployeeUserDetails
        return Optional.of(userDetails.employee)
    }
}

4. 确认JPA审计配置

确保配置类中启用JPA审计,并指定auditorAware的Bean引用:

import org.springframework.context.annotation.Configuration
import org.springframework.data.jpa.repository.config.EnableJpaAuditing

@Configuration
@EnableJpaAuditing(auditorAwareRef = "auditorAwareImpl")
class JpaConfig

常见问题排查

  • 若之前获取失败,大概率是SecurityContext中的principal不是自定义的EmployeeUserDetails(比如用了默认内存用户),需确保登录逻辑中返回自定义的UserDetails实例。
  • 若字段类型不匹配会导致JPA存储失败,需严格保证AuditorAware的泛型与AuditModel中对应字段类型一致。

内容的提问来源于stack exchange,提问作者Pedro Barros

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 19:30:38