用户登录后如何获取刷新令牌?

问题描述 投票:0回答:1

这是我想要实现的场景:

  1. 用户进入网站页面,到达受保护页面后,他会被重定向到使用 oauth2 登录。
  2. 服务器收到新连接后,如果需要,它会存储刷新令牌
  3. 稍后,如果需要,@scheduled 任务会从远程服务器获取感兴趣的数据,并使用刷新令牌将它们存储在数据库中。

我能够正确配置 oauth2 :

  • application.yaml 包含 spring.security.oauth2.client.provider 和使用该提供程序的注册。
  • 我添加了一个 bean 来配置安全性:
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests(authorize -> authorize
                        .requestMatchers("/", "/index").permitAll()
                        .anyRequest().authenticated())
                .oauth2Login(Customizer.withDefaults());
        return http.build();
    }

现在,当我获取 localhost:8080/ 时,我没有问题,如果我获取例如 localhost:8080/user ,它会正确重定向到 oauth 然后我可以访问该资源。

  • 我添加了另一个服务来拦截连接:
@Service
@RequiredArgsConstructor(onConstructor = @__(@Lazy))
public class Oauth2UserService extends DefaultOAuth2UserService {

    @Override
    public OAuth2User loadUser(OAuth2UserRequest oAuth2UserRequest) {
        OAuth2User oAuth2User = super.loadUser(oAuth2UserRequest);
        return processOAuth2User(oAuth2UserRequest, oAuth2User);
    }

    private OAuth2User processOAuth2User(OAuth2UserRequest oAuth2UserRequest, OAuth2User oAuth2User) {
        System.out.println("user attributes : " + oAuth2User.getAttributes());
        System.out.println("access token is " + oAuth2UserRequest.getAccessToken().getTokenValue());
        System.out.println(
                "refresh token is : " + oAuth2UserRequest.getAdditionalParameters().get(OAuth2ParameterNames.REFRESH_TOKEN));
        System.out.println(
                "additional parameters are : " + oAuth2UserRequest.getAdditionalParameters());
        return oAuth2User;
    }
}

但是,当我这样做时,我只检索访问令牌,该令牌的过期时间很短,因此不适合长期更新。刷新令牌为空,附加参数为空映射。 我尝试了几种(很多)用户配置,但似乎没有任何效果(我没有跟踪它,因为我已经尝试了好几天了)。 包括https://stackoverflow.com/a/70548497/23229663

这是我的问题:

  1. 你对我迄今为止做的不好的事情有什么看法吗?在我看来,spring 发展得如此之快,以至于我发现的一些东西可能在 2 年后就过时了,通常是 @Autowired 。基本上请评论:D
  2. 我怎样才能获得刷新令牌?我尝试了在 httpRequest.oauth2login() 配置中创建转换器的恶作剧,但没有成功。也许是我做的不好?另外其他示例也未编译
  3. 这是一个不同的问题,我的代码应该放在一个库中,但到目前为止我需要在每个库 MyLib 中创建一个带有 @Configuration 、 @componentScan 等的 MyLibRefence 类,并用
  4. 注释我的主应用程序类
@Import({
        MyLibReference1.class,
        MyLibReference2.class})

您能否将我重定向到不再需要该指南的指南?也就是说,当我将我的 lib 声明为 MainApp 项目的 Maven 依赖项时,它会自动扫描该 lib 的包。我找到了一些线索,但我想我迷路了。虽然这是低优先级的,到目前为止它很乏味但它有效。 (但由于它很乏味,以后会产生错误) 4. 同样不重要,优先级不高,如何将提供程序存储在库中,而不必写入 application.yaml ?

感谢您的宝贵时间:)

链接: 我想实现这个的实际代码:https://github.com/guiguilechat/JCELechat/tree/mevetic/programs/spring/mevetic 主类和导入示例:https://github.com/guiguilechat/JCELechat/blob/mevetic/programs/spring/eveproxy/src/main/java/fr/guiguilechat/jcelechat/programs/spring/eveproxy/EveProxyApp。爪哇 库 Libreference 的示例:https://github.com/guiguilechat/JCELechat/blob/mevetic/libs/spring/mer/src/main/java/fr/guiguilechat/jcelechat/libs/spring/mer/MerReference.java

编辑:我再次研究了另一个解决方案,并且可以使其工作。 作为参考,这里是添加在 SecurityConfig 上的有效代码:

@Configuration
@EnableWebSecurity
public class SecurityConfig {

// actually no change on the filterChain because it's not needed

    @Bean
    public OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient() {
        DefaultAuthorizationCodeTokenResponseClient accessTokenResponseClient = new DefaultAuthorizationCodeTokenResponseClient();

        OAuth2AccessTokenResponseHttpMessageConverter tokenResponseHttpMessageConverter = new OAuth2AccessTokenResponseHttpMessageConverter();
        tokenResponseHttpMessageConverter.setAccessTokenResponseConverter(new CustomTokenResponseConverter());
        RestTemplate restTemplate = new RestTemplate(Arrays.asList(
                new FormHttpMessageConverter(), tokenResponseHttpMessageConverter));
        restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());

        accessTokenResponseClient.setRestOperations(restTemplate);
        return accessTokenResponseClient;
    }

    public static class CustomTokenResponseConverter implements
            Converter<Map<String, Object>, OAuth2AccessTokenResponse> {

        @Override
        public OAuth2AccessTokenResponse convert(Map<String, Object> tokenResponseParameters) {
            String accessToken = (String) tokenResponseParameters.get(OAuth2ParameterNames.ACCESS_TOKEN);
            String refreshToken = (String) tokenResponseParameters.get(OAuth2ParameterNames.REFRESH_TOKEN);
            Object expiresInObj = tokenResponseParameters.get(OAuth2ParameterNames.EXPIRES_IN);
            long expiresIn = ((Number) expiresInObj).longValue();

            Set<String> scopes = Collections.emptySet();
            if (tokenResponseParameters.containsKey(OAuth2ParameterNames.SCOPE)) {
                String scope = (String) tokenResponseParameters.get(OAuth2ParameterNames.SCOPE);
                scopes = Arrays.stream(StringUtils.delimitedListToStringArray(scope, " "))
                        .collect(Collectors.toSet());
            }

            return OAuth2AccessTokenResponse.withToken(accessToken)
                    .tokenType(OAuth2AccessToken.TokenType.BEARER)
                    .expiresIn(expiresIn)
                    .scopes(scopes)
                    .refreshToken(refreshToken)
                    .additionalParameters(Map.of(OAuth2ParameterNames.REFRESH_TOKEN, refreshToken))
                    .build();
        }
    }

}

我遇到的问题是,在另一个答案的代码中,expiresIn是从字符串解析的,但在目前的框架中,它是地图中的一个数字(我猜很长),所以parseLong默默地失败了,我刚刚收到一个错误。

话虽如此,我不确定这是正确的方法。特别是,似乎有几种手动配置会阻止自动配置,否则自动配置会起作用。

spring spring-boot oauth-2.0 refresh-token
1个回答
0
投票

好的,这是第一个问题的答案:只需在配置类中拥有 OAuth2AccessTokenResponseClient 的 @Bean :

@Configuration
@EnableWebSecurity
public class SecurityConfig {

// actually no change on the filterChain because it's not needed

    @Bean
    public OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient() {
        DefaultAuthorizationCodeTokenResponseClient accessTokenResponseClient = new DefaultAuthorizationCodeTokenResponseClient();

        OAuth2AccessTokenResponseHttpMessageConverter tokenResponseHttpMessageConverter = new OAuth2AccessTokenResponseHttpMessageConverter();
        tokenResponseHttpMessageConverter.setAccessTokenResponseConverter(new CustomTokenResponseConverter());
        RestTemplate restTemplate = new RestTemplate(Arrays.asList(
                new FormHttpMessageConverter(), tokenResponseHttpMessageConverter));
        restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());

        accessTokenResponseClient.setRestOperations(restTemplate);
        return accessTokenResponseClient;
    }

    public static class CustomTokenResponseConverter implements
            Converter<Map<String, Object>, OAuth2AccessTokenResponse> {

        @Override
        public OAuth2AccessTokenResponse convert(Map<String, Object> tokenResponseParameters) {
            String accessToken = (String) tokenResponseParameters.get(OAuth2ParameterNames.ACCESS_TOKEN);
            String refreshToken = (String) tokenResponseParameters.get(OAuth2ParameterNames.REFRESH_TOKEN);
            Object expiresInObj = tokenResponseParameters.get(OAuth2ParameterNames.EXPIRES_IN);
            long expiresIn = ((Number) expiresInObj).longValue();

            Set<String> scopes = Collections.emptySet();
            if (tokenResponseParameters.containsKey(OAuth2ParameterNames.SCOPE)) {
                String scope = (String) tokenResponseParameters.get(OAuth2ParameterNames.SCOPE);
                scopes = Arrays.stream(StringUtils.delimitedListToStringArray(scope, " "))
                        .collect(Collectors.toSet());
            }

            return OAuth2AccessTokenResponse.withToken(accessToken)
                    .tokenType(OAuth2AccessToken.TokenType.BEARER)
                    .expiresIn(expiresIn)
                    .scopes(scopes)
                    .refreshToken(refreshToken)
                    .additionalParameters(Map.of(OAuth2ParameterNames.REFRESH_TOKEN, refreshToken))
                    .build();
        }
    }

}

这是图书馆的答案:别想旧工厂的事。现在你只需

  1. 使用@AutoConfigure为您的库创建一个配置类
  2. 创建一个 src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件,其中包含要自动配置的全命名类的列表,换行符分隔。在这种情况下,那就是 my.package.SecurityConfig

现在它不起作用,我想我必须强制我的类在 oauth2 自动配置之前加载以提供 bean。稍后再编辑。

© www.soinside.com 2019 - 2024. All rights reserved.