You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Shell CLI应用Keycloak用户认证及令牌安全存储方案咨询

Spring Shell对接Keycloak认证的实用方案

一、简化认证管理:基于Spring Security OAuth2 Client的原生支持

Spring Security OAuth2 Client完全支持非Web(CLI)场景的认证,无需手动实现令牌获取/刷新逻辑,推荐两种适配CLI的授权流程:

1. PKCE增强的Authorization Code流程(推荐,符合OAuth2最佳实践)

适用于公开客户端(无需client-secret),通过PKCE避免授权码劫持。

  • 添加依赖:
    <!-- Maven -->
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-oauth2-client</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-oauth2-jose</artifactId>
    </dependency>
    
  • 配置application.yml:
    spring:
      security:
        oauth2:
          client:
            registration:
              keycloak-cli:
                client-id: your-cli-client-id
                authorization-grant-type: authorization_code
                redirect-uri: http://localhost:8080/login/oauth2/code/keycloak-cli # CLI监听本地端口接收回调
            provider:
              keycloak-cli:
                issuer-uri: https://your-keycloak-server/auth/realms/your-realm
    
  • 用OAuth2AuthorizedClientManager自动管理令牌:
    该组件自动处理令牌获取、过期刷新,直接注入到Feign拦截器即可:
    @Component
    public class OAuth2FeignInterceptor implements RequestInterceptor {
        private final OAuth2AuthorizedClientManager clientManager;
    
        public OAuth2FeignInterceptor(OAuth2AuthorizedClientManager clientManager) {
            this.clientManager = clientManager;
        }
    
        @Override
        public void apply(RequestTemplate template) {
            OAuth2AuthorizeRequest request = OAuth2AuthorizeRequest.withClientRegistrationId("keycloak-cli")
                    .principal(SecurityContextHolder.getContext().getAuthentication())
                    .build();
            OAuth2AuthorizedClient client = clientManager.authorize(request);
            if (client != null) {
                template.header(HttpHeaders.AUTHORIZATION, "Bearer " + client.getAccessToken().getTokenValue());
            }
        }
    }
    
    首次认证时,CLI会自动打开浏览器引导用户登录Keycloak,后续自动复用令牌。

2. Password流程(仅信任内部CLI应用使用)

如果需要直接在CLI中输入账号密码完成认证,可配置Password授权类型:

  • 修改配置:
    spring:
      security:
        oauth2:
          client:
            registration:
              keycloak-cli:
                client-id: your-cli-client-id
                client-secret: your-client-secret # 保密客户端需配置
                authorization-grant-type: password
                username: ${user.username:} # 可通过CLI参数传入
                password: ${user.password:}
    
  • 拦截器中传入用户认证信息:
    // 构造用户认证对象
    Authentication principal = new UsernamePasswordAuthenticationToken(username, password);
    OAuth2AuthorizeRequest request = OAuth2AuthorizeRequest.withClientRegistrationId("keycloak-cli")
            .principal(principal)
            .build();
    

二、令牌的安全存储方案

1. 数据库加密存储(生产环境首选)

用Spring Security提供的JdbcOAuth2AuthorizedClientService将令牌存储到数据库,同时加密敏感字段:

  • 创建存储表(Spring Security默认结构):
    CREATE TABLE oauth2_authorized_client (
        client_registration_id VARCHAR(100) NOT NULL,
        principal_name VARCHAR(200) NOT NULL,
        access_token_type VARCHAR(100) NOT NULL,
        access_token_value CLOB NOT NULL,
        access_token_issued_at TIMESTAMP NOT NULL,
        access_token_expires_at TIMESTAMP NOT NULL,
        access_token_scopes VARCHAR(1000) DEFAULT NULL,
        refresh_token_value CLOB DEFAULT NULL,
        refresh_token_issued_at TIMESTAMP DEFAULT NULL,
        PRIMARY KEY (client_registration_id, principal_name)
    );
    
  • 注册加密版的客户端服务:
    @Bean
    public OAuth2AuthorizedClientService authorizedClientService(DataSource dataSource,
                                                                 ClientRegistrationRepository repo,
                                                                 TextEncryptor encryptor) {
        JdbcOAuth2AuthorizedClientService service = new JdbcOAuth2AuthorizedClientService(dataSource, repo);
        service.setAccessTokenRowMapper(new EncryptedAccessTokenRowMapper(encryptor));
        service.setRefreshTokenRowMapper(new EncryptedRefreshTokenRowMapper(encryptor));
        return service;
    }
    
    // 自定义加密行映射器,实现令牌字段的加密/解密
    static class EncryptedAccessTokenRowMapper extends JdbcOAuth2AuthorizedClientService.AccessTokenRowMapper {
        private final TextEncryptor encryptor;
        // 实现加密逻辑
    }
    

2. 本地加密文件存储

如果无需数据库,可将令牌加密后存储到用户本地目录(如~/.your-app/tokens):

  • 核心逻辑示例:
    public class LocalTokenStorage {
        private static final String STORAGE_FILE = System.getProperty("user.home") + "/.your-app/encrypted-tokens.dat";
        private final TextEncryptor encryptor;
        private final ObjectMapper objectMapper = new ObjectMapper();
    
        public LocalTokenStorage(TextEncryptor encryptor) {
            this.encryptor = encryptor;
        }
    
        public void saveToken(OAuth2AccessToken token) throws IOException {
            String tokenJson = objectMapper.writeValueAsString(token);
            String encrypted = encryptor.encrypt(tokenJson);
            Files.write(Paths.get(STORAGE_FILE), encrypted.getBytes(StandardCharsets.UTF_8));
        }
    
        public OAuth2AccessToken loadToken() throws IOException {
            String encrypted = Files.readString(Paths.get(STORAGE_FILE), StandardCharsets.UTF_8);
            String tokenJson = encryptor.decrypt(encrypted);
            return objectMapper.readValue(tokenJson, OAuth2AccessToken.class);
        }
    }
    
    密钥可通过CLI启动参数传入,或从环境变量读取。

3. 系统原生密钥管理服务(最高安全级别)

借助操作系统的密钥存储服务,无需手动处理加密:

  • macOS:存储到Keychain,可使用com.github.javakeyring:java-keyring库简化调用
  • Windows:使用DPAPI加密或Credential Manager存储
  • Linux:利用libsecret/KWallet
    示例(java-keyring):
Keyring keyring = Keyring.create();
// 存储令牌
keyring.setPassword("your-app-name", "access-token", tokenValue);
// 读取令牌
String token = keyring.getPassword("your-app-name", "access-token");

总结

  • 优先采用Spring Security OAuth2 Client的原生方案,避免手动实现令牌逻辑,减少出错概率
  • 令牌存储根据场景选择:生产环境用数据库加密或系统密钥服务,开发测试可临时用内存存储

内容的提问来源于stack exchange,提问作者someone

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 05:17:04