Spring Data JPA - возможно ли обновить сущность принципалом, прежде чем сохранить его в хранилище? - PullRequest
0 голосов
/ 01 мая 2019

Я хотел бы защитить свой сервер ресурсов с помощью Spring Security 5, чтобы его можно было использовать для многих пользователей.У меня есть объект Task, который содержит поле userId.

@Entity
@Table(name = "tasks")
data class Task(
    @NotBlank
    val name: String,

    val description: String?,

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    @SequenceGenerator(name = "task_id_seq")
    val id: Long = 0,

    @UUID //custom validation annotation
    val userId: String? = null
)

Я нашел способ использовать SpEL для внедрения принципала JWT в запрос.Что я уже сделал, так это переопределил метод findAll() в моей interface TaskRepository : JpaRepository<Task, Long> аннотацией @Query:

@Query("select t from Task t where t.userId = :#{principal.claims['sub']}")
//sub is a userId in OpenID Connect 1.0, claim is a Map<String, Object>
override fun findAll(): List<Task>

Проблема в том, что я хотел бы сделать нечто подобное с методом save().Единственное, что я нашел, - это обновить уже сохраненную сущность.Я создал совершенно новый метод:

@Modifying
@Query("update Task t set t.userId = :#{principal.claims['sub']} where t.id = :id")
fun setUserId(@Param("id") id: Long)

, но проблема в том, что мне нужно вызвать этот метод в моем TaskService, который выглядит следующим образом:

@Transactional
fun createTask(taskDto: CreateTaskDto): Task {
    val task = taskRepository.save(taskDto.toEntity())
    taskRepository.setUserId(task.id)
    return task
}

Можно ли добавитьэта информация автоматически каждый раз, когда я сохраняю сущность в моей базе данных?Тогда мой код внутри TaskService будет выглядеть так:

@Transactional
fun createTask(taskDto: CreateTaskDto): Task {
    return taskRepository.save(taskDto.toEntity())
}

1 Ответ

0 голосов
/ 02 мая 2019

Решение на самом деле довольно простое, но я не знал, что я искал. Тем не менее, я не знаю, является ли это каноническим решением. Я использую Jpa Auditing для этого. Во-первых, я добавил аннотацию @CreatedBy в поле, которое я хотел обновить перед вставкой в ​​мою сущность. Класс сущности I также помечен @EntityListeners(AuditingEntityListener::class). Обратите внимание, что необходимо было изменить ключевое слово val на var (с поля final на не финальное поле в мире Java).

@Entity
@Table(name = "tasks")
@EntityListeners(AuditingEntityListener::class)
data class Task(
        @NotBlank
        val name: String,

        val description: String?,

        @Id
        @GeneratedValue(strategy = GenerationType.SEQUENCE)
        @SequenceGenerator(name = "task_id_seq")
        val id: Long = 0,

        @UUID
        @CreatedBy
        var userId: String? = null
    )

Следующим шагом было реализовать interface AuditorAware<T> и сделать его видимым для Spring с помощью @Component (или создания bean-компонента внутри класса config)

@Component
class SpringSecurityAuditorAware : AuditorAware<String> {

    override fun getCurrentAuditor(): Optional<String> {
        return Optional.ofNullable(SecurityContextHolder.getContext()?.authentication)
                .filter { authentication -> authentication.isAuthenticated }
                .map { authentication -> authentication.principal as Jwt }
                .map { jwt -> jwt.claims["sub"] as String? }
    }

}

И последнее, но не менее важное: добавление @EnableJpaAuditing для некоторого класса конфигурации:

@Configuration
@EnableJpaAuditing
class ResourceServerContext
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...