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). This means that either:
- the wrong type of authorization (or no authorization at all) is attached to the request. This is the case for instance when an Bearer access token is provided but a session expected
SecurityFilterChain
bean(s) is (or are) misconfigured.
A quite common mistake lately is to write client configuration and then then send requests with Bearer
header. When OAuth2 client conf (with oauth2Login
) is used, incoming requests are authorized with session cookies, not access token. If you want the requests to your REST API be authorized with access tokens provided in a Bearer
Authorization header, then you have to configure it as a resource-server.
Another common mistake relates to apps needing more than one authorization mechanism (OAuth2 client, OAuth2 resource server, Basic, etc.). In such cases, you should provide with a filter-chain bean for each. But if one of the first in @Order
intercepts a request it shouldn't (misconfigured securityMatcher
), then the security context initialization fails.
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
Spring-security default Authentication
for successful OAuth2 authorization in resource-servers is JwtAuthenticationToken
which return a Jwt
instance as principal
. You can get any access-token claim from this Jwt
instance. Here is a sample with 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)
((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. You might use JwtAuthenticationToken
as a base (but this not the best option, see the note below):
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
@Bean
public Jwt2AuthenticationConverter authenticationConverter(Jwt2AuthoritiesConverter authoritiesConverter) {
return jwt -> new MyAuthentication(jwt, authoritiesConverter.convert(jwt));
}
Important note
You'd better use authentication.getName()
instead of authentication.getPrincipal()
to access username. 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).
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()
. MyAuthentication
would then be:
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.