Spring REST Api -- 访问存储库中的用户详细信息
Spring REST Api -- access User details in repository
RESPApi 项目的设置是:
- Spring开机
- Spring 的 OAuth2
在项目中我们有很多客户,所以 SQL 查询几乎总是在 where
子句中有 "... and clientId = ?"
。
我们将 clientId
与其他用户详细信息一起存储在 SecurityContext
中(我们扩展了 Spring 的 User
class)。
问题是:如何获取@Repository
中的User
对象?
我们能想到的可能解决方案:
- 在每个存储库实现中添加
SecurityContextHolder.getContext().getAuthentication()
将结果转换为我们自定义的 UserDetails 实现并使用它。
缺点:不知何故我觉得有更好的解决方案。
- 将
@AuthenticationPrincipal
注解参数添加到控制器,然后将参数传递给服务层,然后传递给存储层。
缺点:仅通过 2 层传递参数以获得 clientId
似乎不合理。
我在@Repository
class中考虑了@Autowired
参数MyUser user
。第一次尝试是创建 @Configuration
注释 class ,其中将有一个方法
@Bean
public MyUser getUser() {
SecurityContext context = SecurityContextHolder.getContext();
if (context != null) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null) {
return (MyUser) authentication.getPrincipal();
}
}
return null;
}
但是这个bean是空的,我不能使用它。
现在我们已经得到了解决方案 nr 1,但我觉得一定有更好的方法。
有什么解决这个问题的想法吗?
如果您正在使用 Spring Data
(或有时间切换到使用它),您可以使用 SecurityEvaluationContextExtension
并在查询中直接使用 principal
:
如果没有,您可以隐藏静态访问,如果它冒犯了(或者如果您希望在将来对其进行更多控制):
@Component
public class AuthenticationHelper {
public Authentication getAuthentication() {
return SecurityContextHolder.getContext().getAuthentication();
}
}
然后将 class 注入您的存储库。
或您的服务。这可能比存储库更合适。
我喜欢让存储库保持愚蠢(最终使用 Spring 数据来避免完全写入它们)。
而且我喜欢将服务视为从 Web 层中分离出来,运行 在单独的盒子上(即使它们不是)。在那种情况下,您永远不会通过 HTTP 将身份验证详细信息从控制器传递到服务。该服务将为自己获取身份验证详细信息,而不仅仅是信任 Web 层发送给它的内容。
所以我认为服务应该自己获取详细信息,而不是控制器传递它们。
你的 bean 是空的,因为默认情况下 bean 是单例的,它们是在应用程序启动时创建的,你可以想象,那时你不会有 SecurityContext
。
尝试以这种方式声明具有请求范围的 bean:
@Bean
@Scope(value=WebApplicationContext.SCOPE_REQUEST, proxyMode=ScopedProxyMode.TARGET_CLASS)
public MyUser getUser() {
.....
}
RESPApi 项目的设置是:
- Spring开机
- Spring 的 OAuth2
在项目中我们有很多客户,所以 SQL 查询几乎总是在 where
子句中有 "... and clientId = ?"
。
我们将 clientId
与其他用户详细信息一起存储在 SecurityContext
中(我们扩展了 Spring 的 User
class)。
问题是:如何获取@Repository
中的User
对象?
我们能想到的可能解决方案:
- 在每个存储库实现中添加
SecurityContextHolder.getContext().getAuthentication()
将结果转换为我们自定义的 UserDetails 实现并使用它。
缺点:不知何故我觉得有更好的解决方案。
- 将
@AuthenticationPrincipal
注解参数添加到控制器,然后将参数传递给服务层,然后传递给存储层。
缺点:仅通过 2 层传递参数以获得 clientId
似乎不合理。
我在@Repository
class中考虑了@Autowired
参数MyUser user
。第一次尝试是创建 @Configuration
注释 class ,其中将有一个方法
@Bean
public MyUser getUser() {
SecurityContext context = SecurityContextHolder.getContext();
if (context != null) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null) {
return (MyUser) authentication.getPrincipal();
}
}
return null;
}
但是这个bean是空的,我不能使用它。
现在我们已经得到了解决方案 nr 1,但我觉得一定有更好的方法。
有什么解决这个问题的想法吗?
如果您正在使用 Spring Data
(或有时间切换到使用它),您可以使用 SecurityEvaluationContextExtension
并在查询中直接使用 principal
:
如果没有,您可以隐藏静态访问,如果它冒犯了(或者如果您希望在将来对其进行更多控制):
@Component
public class AuthenticationHelper {
public Authentication getAuthentication() {
return SecurityContextHolder.getContext().getAuthentication();
}
}
然后将 class 注入您的存储库。
或您的服务。这可能比存储库更合适。
我喜欢让存储库保持愚蠢(最终使用 Spring 数据来避免完全写入它们)。
而且我喜欢将服务视为从 Web 层中分离出来,运行 在单独的盒子上(即使它们不是)。在那种情况下,您永远不会通过 HTTP 将身份验证详细信息从控制器传递到服务。该服务将为自己获取身份验证详细信息,而不仅仅是信任 Web 层发送给它的内容。
所以我认为服务应该自己获取详细信息,而不是控制器传递它们。
你的 bean 是空的,因为默认情况下 bean 是单例的,它们是在应用程序启动时创建的,你可以想象,那时你不会有 SecurityContext
。
尝试以这种方式声明具有请求范围的 bean:
@Bean
@Scope(value=WebApplicationContext.SCOPE_REQUEST, proxyMode=ScopedProxyMode.TARGET_CLASS)
public MyUser getUser() {
.....
}