我们有一个使用OpenID Connect / Spring Security 5的Web应用程序来验证用户。
与我们在我们的spring Security中的开箱即用的OIDC支持非常简单。
使用OAuth2身份验证支持( oauth2login
) ,Identity提供商通过UserInfo端点公开了一些属性, 业务逻辑(最好以类型安全的方式)。这些属性在某些情况下是富有的对象的列表,使用通用 map&lt< string,object>
该userInfo端点的属性与default doken的属性合并到默认值<<<代码> oidcuserService ,然后放在 defaultoidCuser
(以便它成为 oauth2authenticationToken
)的
- 一部分包含所有这些属性的DefaultoidCuser(使用映射)
-
java.security.principal
= oauth2authenticationToken
(又包含委托人,是我们的 default> defaultoidcuser
)
-
securitycontextholder.getContext()。getAuthentication()
=再次相同 oauth2authenticationToken
,
因此要访问自定义属性,我可以
- 施放principal(or SecurityContexTholder.getContext() ))
public void doSomething(Principal principal) {
OAuth2AuthenticationToken oAuth2AuthenticationToken = (OAuth2AuthenticationToken)principal;
Map<String, Object> attributes = oAuth2AuthenticationToken.getPrincipal().getAttributes();
...
}
缺点是我需要施放,并且需要将地图转换为代码中的POJO“某个地方”,最好只有一次。
- 注入
@Authentication Principal defaultoidCuser
避免施放和访问属性。
public void doSomething(@AuthenticationPrincipal DefaultOidcUser defaultOidcUser) {
Map<String, Object> attributes = defaultOidcUser.getAttributes();
...
}
在这里,我不需要施放,但仍然没有属性作为pojo。
以类型安全的方式轻松访问这些属性的最佳方法是什么?
提到 oauth2userservice
,我们有一个可以返回可以是自定义对象的 oidcuser
的钩子,以类型的安全方式公开我们的属性。
导致我进入选项nr 3
- 创建一个自定义
oidcuserService
private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {
final OidcUserService delegate = new OidcUserService();
return (userRequest) -> {
// Load original user
OidcUser oidcUser = delegate.loadUser(userRequest);
// Parse custom claims
CustomTypedAttributes customTypedAttributes = objectMapper.convertValue(oidcUser.getClaims(), CustomTypedAttributes.class);
// Expose them as new custom user
return new CustomOidcUser(oidcUser.getAuthorities(), oidcUser.getIdToken(), oidcUser.getUserInfo(), customTypedAttributes);
};
}
优势是我们通过 code> coductoidcuser.getCustomTypedAttributes()
,对我们的自定义属性具有类型的安全访问一次(登录后)。
但是,将 oidcuser
像这样的 odcuser
揭露类型的安全属性映射并不感到“正确”。
是否有其他方法可以处理应用程序代码中的自定义属性:
- 以一种安全的方式访问自定义属性。
- 使用标准弹簧安全构建块
- 测试友好的标准弹簧安全构建块(使用Spring Security Testing),
We have a web application using OpenID Connect / Spring Security 5 to authenticate users.
Integration is really simple with the out-of-the-box OIDC support in Spring Security using the oauth2 authentication support (oauth2Login
)
The Identity Provider exposes some attributes via the userInfo endpoint that we need a lot in our business logic (preferably in a type-safe way). These attributes are in some cases lists of rich objects, something difficult to access using a generic Map<String,Object>
The attributes from that userInfo endpoint are merged with the attributes from the id token in the default OidcUserService
, and put on the DefaultOidcUser
(so that it becomes part of the OAuth2AuthenticationToken
)
@AuthenticationPrincipal DefaultOidcUser defaultOidcUser
= a DefaultOidcUser that contains all of these attributes (using Maps)
java.security.Principal
= OAuth2AuthenticationToken
(that in turn contains a principal, being our DefaultOidcUser
)
SecurityContextHolder.getContext().getAuthentication()
= again the same OAuth2AuthenticationToken
So to access the custom attributes I can
- cast the Principal (or SecurityContextHolder.getContext().getAuthentication())
public void doSomething(Principal principal) {
OAuth2AuthenticationToken oAuth2AuthenticationToken = (OAuth2AuthenticationToken)principal;
Map<String, Object> attributes = oAuth2AuthenticationToken.getPrincipal().getAttributes();
...
}
Downside here is I need to cast, and would need to convert the Map into a Pojo "somewhere" in the code, preferably only once.
- Inject a
@AuthenticationPrincipal DefaultOidcUser
to avoid casting and access the attributes.
public void doSomething(@AuthenticationPrincipal DefaultOidcUser defaultOidcUser) {
Map<String, Object> attributes = defaultOidcUser.getAttributes();
...
}
Here I don't need to cast, but still don't have the attributes as a pojo.
What would be the best way to have easy access to these attributes in a type-safe way ?
The documentation mentions the OAuth2UserService
, where we have a hook to return a OidcUser
that could be a custom object, exposing our attributes in a type safe way.
Leading me to option nr 3
- Create a custom
oidcUserService
private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {
final OidcUserService delegate = new OidcUserService();
return (userRequest) -> {
// Load original user
OidcUser oidcUser = delegate.loadUser(userRequest);
// Parse custom claims
CustomTypedAttributes customTypedAttributes = objectMapper.convertValue(oidcUser.getClaims(), CustomTypedAttributes.class);
// Expose them as new custom user
return new CustomOidcUser(oidcUser.getAuthorities(), oidcUser.getIdToken(), oidcUser.getUserInfo(), customTypedAttributes);
};
}
Advantage is we have type-safe access to our custom attributes, via customOidcUser.getCustomTypedAttributes()
, they only need to be converted once (after login).
However, it doesn't feel "right" to wrap an OidcUser
like this, just to expose a type safe attribute map.
Are there alternative ways of dealing with custom attributes in application code where the requirements are :
- Easy access to custom attributes in a type safe way
- Making use of the standard Spring Security building blocks
- Test-friendly (using spring security testing)
发布评论