Невозможно передать в качестве аргумента JWT refre sh token - PullRequest
1 голос
/ 12 апреля 2020

Я пытаюсь получить новый токен доступа, используя refre sh токен в Spring Boot с OAuth2. Это должно быть сделано следующим образом: POST: url/oauth/token?grant_type=refresh_token&refresh_token=....

Это прекрасно работает, если я использую InMemoryTokenStore, потому что токен крошечный и содержит только цифры / буквы, но сейчас я использую токен JWT и как вы вероятно, он состоит из 3 разных частей, которые, вероятно, нарушают код.

Я использую официальное руководство по миграции на 2,4 .

Когда я пытаюсь получить доступ к URL выше я получаю следующее сообщение:

{
    "error": "invalid_token",
    "error_description": "Cannot convert access token to JSON"
}

Как передать токен JWT в параметрах? Я попытался установить точку останова в этом сообщении, чтобы я мог видеть, каков был настоящий аргумент, но по какой-то причине он не дошел до него.

/**
 * The Authorization Server is responsible for generating tokens specific to a client.
 * Additional information can be found here: https://www.devglan.com/spring-security/spring-boot-security-oauth2-example.
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Value("${user.oauth2.client-id}")
    private String clientId;

    @Value("${user.oauth2.client-secret}")
    private String clientSecret;

    @Value("${user.oauth2.accessTokenValidity}")
    private int accessTokenValidity;

    @Value("${user.oauth2.refreshTokenValidity}")
    private int refreshTokenValidity;

    @Autowired
    private ClientDetailsService clientDetailsService;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients
                .inMemory()
                .withClient(clientId)
                .secret(bCryptPasswordEncoder.encode(clientSecret))
                .authorizedGrantTypes("password", "authorization_code", "refresh_token")
                .scopes("read", "write", "trust")
                .resourceIds("api")
                .accessTokenValiditySeconds(accessTokenValidity)
                .refreshTokenValiditySeconds(refreshTokenValidity);
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                .authenticationManager(authenticationManager)
                .tokenStore(tokenStore())
                .userApprovalHandler(userApprovalHandler())
                .accessTokenConverter(accessTokenConverter());
    }

    @Bean
    public UserApprovalHandler userApprovalHandler() {
        ApprovalStoreUserApprovalHandler userApprovalHandler = new ApprovalStoreUserApprovalHandler();
        userApprovalHandler.setApprovalStore(approvalStore());
        userApprovalHandler.setClientDetailsService(clientDetailsService);
        userApprovalHandler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService));
        return userApprovalHandler;
    }

    @Bean
    public TokenStore tokenStore() {
        JwtTokenStore tokenStore = new JwtTokenStore(accessTokenConverter());
        tokenStore.setApprovalStore(approvalStore());
        return tokenStore;
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        final RsaSigner signer = new RsaSigner(KeyConfig.getSignerKey());

        JwtAccessTokenConverter converter = new JwtAccessTokenConverter() {
            private JsonParser objectMapper = JsonParserFactory.create();

            @Override
            protected String encode(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
                String content;
                try {
                    content = this.objectMapper.formatMap(getAccessTokenConverter().convertAccessToken(accessToken, authentication));
                } catch (Exception ex) {
                    throw new IllegalStateException("Cannot convert access token to JSON", ex);
                }
                Map<String, String> headers = new HashMap<>();
                headers.put("kid", KeyConfig.VERIFIER_KEY_ID);
                return JwtHelper.encode(content, signer, headers).getEncoded();
            }
        };
        converter.setSigner(signer);
        converter.setVerifier(new RsaVerifier(KeyConfig.getVerifierKey()));
        return converter;
    }

    @Bean
    public ApprovalStore approvalStore() {
        return new InMemoryApprovalStore();
    }

    @Bean
    public JWKSet jwkSet() {
        RSAKey.Builder builder = new RSAKey.Builder(KeyConfig.getVerifierKey())
                .keyUse(KeyUse.SIGNATURE)
                .algorithm(JWSAlgorithm.RS256)
                .keyID(KeyConfig.VERIFIER_KEY_ID);
        return new JWKSet(builder.build());
    }

}

1 Ответ

1 голос
/ 13 апреля 2020

Я предполагаю, что Cannot convert access token to JSON мог быть из-за неправильно вставленного токена.

Что касается Invalid refresh token, это происходит потому, что когда JwtTokenStore читает токен refre sh, он проверяет области действия и отзыв с InMemoryApprovalStore. Однако для этой реализации утверждения регистрируются только во время авторизации через /oauth/authorize URL (предоставление кода авторизации) с помощью ApprovalStoreUserApprovalHandler.

Специально для предоставления кода авторизации (authorization_code), который вы хотите иметь эту проверку, чтобы запрос токена refre sh не вызывался с расширенной областью без ведома пользователя. Кроме того, необязательно хранить утверждения для отзыва в будущем.

Решение состоит в том, чтобы заполнить ApprovalStore списком Approval для всех владельцев ресурсов - статически или динамически. Кроме того, может отсутствовать настройка службы сведений о пользователе endpoints.userDetailsService(userDetailsService), которая используется во время процесса refre sh.

Обновление:

Это можно проверить с помощью создание предварительно заполненного InMemoryApprovalStore:

@Bean
public ApprovalStore approvalStore() {
    InMemoryApprovalStore approvalStore = new InMemoryApprovalStore();
    Date expirationDate = Date.from(Instant.now().plusSeconds(3600));
    List<Approval> approvals = Stream.of("read", "write", "trust")
            .map(scope -> new Approval("admin", "trusted", scope, expirationDate,
                    ApprovalStatus.APPROVED))
            .collect(Collectors.toList());
    approvalStore.addApprovals(approvals);
    return approvalStore;
}

Я также хотел бы взглянуть на его реализацию в storeRefreshToken() / storeAccessToken() методах JwtTokenStore, так как они имеют пустую реализацию, и Параметры метода содержат все необходимые данные.

...