【发布时间】:2020-10-14 16:10:38
【问题描述】:
有几种情况,我想更新用户/主体数据,以便在用户保持登录状态时反映更改(我不想强制重新认证)
从会话“内部”来看,这不是问题:
@PostMapping("/updateInfo")
fun updateMyData(
@AuthenticationPrincipal user: AppUser,
@Valid @RequestBody newInfo: UpdateDataRequest
): ResponseEntity<TestUserInfo> {
val testInfo = TestUserInfo(user, newInfo)
user.info = testInfo
val updatedUser = users.save(user)
return ResponseEntity.ok(updatedUser.info!!)
}
当我允许用户例如更改他们自己的数据时,我可以轻松访问和更改@AuthenticationPrincipal - 在连续的请求中我可以观察到数据已更新。
当我需要从会话“外部”更改用户数据时,情况就不同了。
用例
有两个用例:
一)。管理员更改用户数据
乙)。用户确认他的电子邮件地址
现在 a)。显然发生在另一个 http-session 中,其中主体是具有一些管理员权限的用户。 对于 b)。您可能会问,为什么这不会在会话中发生:我想要一个简单的一次性确认链接,即获取请求。我不能假设用户是通过设备上的会话登录的,确认链接已打开。对我来说,做一个单独的预身份验证提供程序或其他东西来让用户通过身份验证是不对的 - 然后会在浏览器上打开一个不再使用的不必要的会话。
所以在这两种情况下,当我通过 JPArepository 获取用户、更新数据并将其保存回来时,数据库中的更改是最新的 - 但登录用户不知道该更改,因为他们的用户数据存储在 http 会话中,不知道需要更新。
请注意,我没有使用 redis/spring-session 任何东西 - 这只是一个普通的 http 会话,所以据我了解,我不能使用 FindByIndexNameSessionRepository。
我尝试过的
-
在spring-security issue #3849 中,rwinch 建议覆盖
SecurityContextRepository- 但是,没有关于如何准确执行此操作的更多信息 - 我试图了解界面但无法进一步了解。 -
我试图通过对以下 SO 帖子的回复: How to reload authorities on user update with Spring Security(使用 redis 忽略答案。)
- 投票最多的answer by leo 无济于事,正如那里的 cmets 所述
-
Aure77 suggests 使用
SessionRegistry,我也尝试在 bealdung 之后使用它 - 但无济于事:我无法正确会话,当登录用户有活动会话时,getallprincipals() 始终为空。如果我有正确的会话,我什至不确定如何从那里继续,因为 Aure 只是建议使用强制重新身份验证的expireNow()- 我想避免这种情况。 - @ 987654326@ 类似的东西 - 从他的角度来看,我认为spring boot 默认使用线程本地securityContextRepository,这就是我没有主体的原因。他提出了一些我还没有理解的东西 - 答案也很老了(2012 年),我对尝试理解和应用它感到不太安全
-
TwiN suggests 使用
HandlerInterceptor。 Hasler Choo suggests 带有哈希集的修改版本,似乎更接近我的需要。如下所述 - 它有它的问题。
HandlerInterceptor 基于方法
这是迄今为止我可以成功实施的唯一解决方案 - 但它似乎不是很灵活。到目前为止,我的实施仅涵盖用户角色更改。
配置:
@Configuration
class WebMvcConfig : WebMvcConfigurer {
@Autowired
private lateinit var updateUserDataInterceptor : UpdateUserDataInterceptor
override fun addInterceptors(registry: InterceptorRegistry) {
registry.addInterceptor(updateUserDataInterceptor)
}
}
HandlerInterceptor:
@Component
class UpdateUserDataInterceptor(
@Autowired
private val users: AppUserRepository
) : HandlerInterceptor {
private val usersToUpdate = ConcurrentHashMap.newKeySet<Long>()
fun markUpdate(user: AppUser) = usersToUpdate.add(user.id)
override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean {
val auth = SecurityContextHolder.getContext().authentication
(auth.principal as? AppUser)?.apply {
synchronized(usersToUpdate) {
if (id in usersToUpdate) {
role = users.findById(id).get().role
usersToUpdate.remove(id)
}
}
}
return true
}
}
我宁愿不只是更新角色,而是替换整个原则-但主体是身份验证对象中的final。
因此,每当 a 需要更新角色以外的其他内容时,都必须在此处特别提及。
剩下的问题:
- 除了
HandlerInterceptor,还有其他解决方案吗? - 是否有基于
HandlerInterceptor的解决方案,允许我完全更新主体对象
【问题讨论】:
-
@KavithakaranKanapathippillai 我添加了指向各个答案的直接链接 - 2 以下的所有选项都是对同一个 SO 帖子的答案。
-
感谢您的回答 - 我相信下一个请求应该没问题,我明天再检查一下。
标签: spring-boot kotlin spring-security