[英]Spring Boot with Spring Security and Keycloak only providing Anonymous Authentication Token
so I followed a guide which set up Keycloak to secure my Spring Boot REST API with Spring Security.所以我按照指南设置了 Keycloak 来保护我的 Spring Boot REST API 和 Spring Security。 Previously, my application relied on a standard user details service to authenticate users.以前,我的应用程序依赖于标准用户详细信息服务来验证用户身份。 My code relies on being able to access the currently logged-in user's username.我的代码依赖于能够访问当前登录用户的用户名。 However, now that I switched to Keycloak, I only receive the username 'anonymousUser'.但是,现在我切换到 Keycloak,我只收到用户名“anonymousUser”。
How do I configure this correctly for the following code to return the preferred username:如何为以下代码正确配置它以返回首选用户名:
public static String getCurrentUsername() {
return (String) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}
I tried to modify the configuration of the client and user on Keycloak, but even for example, adding roles has no effect.我尝试在Keycloak上修改客户端和用户的配置,但即使是例如,添加角色也没有效果。 Spring Boot only returns the role of [ANONYMOUS_USER]. Spring 引导仅返回 [ANONYMOUS_USER] 的角色。
There is no chance you get any info about current user from AnonymousAuthenticationToken
: this implementation of Authentication
is put in security-context when the request was not successfully authorized (user could not be identified).您不可能从AnonymousAuthenticationToken
获得有关当前用户的任何信息:当请求未成功授权(无法识别用户)时,此Authentication
实现将置于安全上下文中。 This means that either:这意味着:
For basic info on how to configure Spring-boot 3 resource-server (and client) with Keycloak, see my answer to this question: Use Keycloak Spring Adapter with Spring Boot 3有关如何使用 Keycloak 配置 Spring-boot 3 资源服务器(和客户端)的基本信息,请参阅我对这个问题的回答: Use Keycloak Spring Adapter with Spring Boot 3
Spring-security default Authentication
for successful OAuth2 authorization in resource-servers is JwtAuthenticationToken
which return a Jwt
instance as principal
. Spring-security 资源服务器中成功的 OAuth2 授权的默认Authentication
是JwtAuthenticationToken
,它返回一个Jwt
实例作为principal
。 You can get any access-token claim from this Jwt
instance.您可以从此Jwt
实例获取任何访问令牌声明。 Here is a sample with preferred_username
.这是带有preferred_username
的示例。 Open an access-token in a tool like https://jwt.io to find the name of the claim with the username value you are looking for (it could be as well sub
, email
or any private claim you configured in Keycloak)在诸如https://jwt.io之类的工具中打开一个访问令牌,以查找具有您要查找的用户名值的声明的名称(它也可以是sub
, email
或您在 Keycloak 中配置的任何私人声明)
((Jwt) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getClaims().get(StandardClaimNames.PREFERRED_USERNAME);
If you want getPrincipal()
to return the username instead of a Jwt
instance, you'll have to provide your own Authentication
implementation.如果您希望getPrincipal()
返回用户名而不是Jwt
实例,则必须提供您自己的Authentication
实现。 You might use JwtAuthenticationToken
as a base (but this not the best option, see the note below):您可以使用JwtAuthenticationToken
作为基础(但这不是最佳选择,请参阅下面的注释):
public class MyAuthentication extends JwtAuthenticationToken {
public MyAuthentication(Jwt jwt, Collection<? extends GrantedAuthority> authorities) {
super(jwt, authorities);
}
@Override
public String getPrincipal() {
return getToken().getClaimAsString(StandardClaimNames.PREFERRED_USERNAME);
}
}
Just adapt the Jwt2AuthenticationConverter
from the answer I linked above只需根据我上面链接的答案改编Jwt2AuthenticationConverter
@Bean
public Jwt2AuthenticationConverter authenticationConverter(Jwt2AuthoritiesConverter authoritiesConverter) {
return jwt -> new MyAuthentication(jwt, authoritiesConverter.convert(jwt));
}
You'd better use authentication.getName()
instead of authentication.getPrincipal()
to access username.您最好使用authentication.getName()
而不是authentication.getPrincipal()
来访问用户名。 principal
is typed as Object
in Authentication
which makes your expressions very fragile: you can get absolutely any type of data as principal (depending on the type of authentication in the security context) and there are cases where you don't really control it (for instance with the AnonymousAuthenticationToken
instance you currently have because of unauthorized request). principal
在Authentication
中的类型为Object
,这使得您的表达式非常脆弱:您绝对可以获得任何类型的数据作为 principal(取决于安全上下文中的身份验证类型)并且在某些情况下您无法真正控制它(例如由于未经授权的请求,您当前拥有的AnonymousAuthenticationToken
实例)。
However, JwtAuthenticationToken::getName
returns subject ( sub
claim), so you'll still have to provide your own Authentication
implementation for successful authorizations to return preferred_username
in a new @Override of getName()
.但是, JwtAuthenticationToken::getName
返回主题( sub
声明),因此您仍然必须提供自己的Authentication
实现才能成功授权以在新的 @Override getName()
中返回preferred_username
。 MyAuthentication
would then be: MyAuthentication
将是:
public class MyAuthentication extends JwtAuthenticationToken {
public MyAuthentication(Jwt jwt, Collection<? extends GrantedAuthority> authorities) {
super(jwt, authorities);
}
//Note that this time getName() is overriden instead of getPrincipal()
@Override
public String getName() {
return getToken().getClaimAsString(StandardClaimNames.PREFERRED_USERNAME);
}
}
Use the same Jwt2AuthenticationConverter
bean to switch from default JwtAuthenticationToken
to MyAuthentication
in case of successful request authorization.在请求授权成功的情况下,使用相同的Jwt2AuthenticationConverter
bean 从默认的JwtAuthenticationToken
切换到MyAuthentication
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.