簡體   English   中英

Spring 使用 Spring 安全和 Keycloak 啟動,僅提供匿名身份驗證令牌

[英]Spring Boot with Spring Security and Keycloak only providing Anonymous Authentication Token

所以我按照指南設置了 Keycloak 來保護我的 Spring Boot REST API 和 Spring Security。 以前,我的應用程序依賴於標准用戶詳細信息服務來驗證用戶身份。 我的代碼依賴於能夠訪問當前登錄用戶的用戶名。 但是,現在我切換到 Keycloak,我只收到用戶名“anonymousUser”。

如何為以下代碼正確配置它以返回首選用戶名:

public static String getCurrentUsername() {

return (String) SecurityContextHolder.getContext().getAuthentication().getPrincipal();

}

我嘗試在Keycloak上修改客戶端和用戶的配置,但即使是例如,添加角色也沒有效果。 Spring 引導僅返回 [ANONYMOUS_USER] 的角色。

您不可能從AnonymousAuthenticationToken獲得有關當前用戶的任何信息:當請求未成功授權(無法識別用戶)時,此Authentication實現將置於安全上下文中。 這意味着:

  • 授權中缺少訪問令牌 header
  • 訪問令牌無效(已過期,由錯誤的授權服務器頒發,簽名檢查失敗,...)
  • 資源服務器配置錯誤(這很可能是因為您正在編寫 REST API 實際上是資源服務器客戶端配置)

有關如何使用 Keycloak 配置 Spring-boot 3 資源服務器(和客戶端)的基本信息,請參閱我對這個問題的回答: Use Keycloak Spring Adapter with Spring Boot 3

Spring-security 資源服務器中成功的 OAuth2 授權的默認AuthenticationJwtAuthenticationToken ,它返回一個Jwt實例作為principal 您可以從此Jwt實例獲取任何訪問令牌聲明。 這是帶有preferred_username的示例。 在諸如https://jwt.io之類的工具中打開一個訪問令牌,以查找具有您要查找的用戶名值的聲明的名稱(它也可以是subemail或您在 Keycloak 中配置的任何私人聲明)

((Jwt) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getClaims().get(StandardClaimNames.PREFERRED_USERNAME);

如果您希望getPrincipal()返回用戶名而不是Jwt實例,則必須提供您自己的Authentication實現。 您可以使用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);
    }   
}

只需根據我上面鏈接的答案改編Jwt2AuthenticationConverter

@Bean
public Jwt2AuthenticationConverter authenticationConverter(Jwt2AuthoritiesConverter authoritiesConverter) {
    return jwt -> new MyAuthentication(jwt, authoritiesConverter.convert(jwt));
}

重要的提示

您最好使用authentication.getName()而不是authentication.getPrincipal()來訪問用戶名。 principalAuthentication中的類型為Object ,這使得您的表達式非常脆弱:您絕對可以獲得任何類型的數據作為 principal(取決於安全上下文中的身份驗證類型)並且在某些情況下您無法真正控制它(例如由於未經授權的請求,您當前擁有的AnonymousAuthenticationToken實例)。

但是, JwtAuthenticationToken::getName返回主題( sub聲明),因此您仍然必須提供自己的Authentication實現才能成功授權以在新的 @Override getName()中返回preferred_username 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);
    }   
}

在請求授權成功的情況下,使用相同的Jwt2AuthenticationConverter bean 從默認的JwtAuthenticationToken切換到MyAuthentication

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM