【问题标题】:LinkedIn authentication using spring boot使用 Spring Boot 进行 LinkedIn 身份验证
【发布时间】:2023-03-19 16:22:01
【问题描述】:

我正在尝试在我的 Spring Boot 应用程序中使用 LinkedIn 身份验证,但出现以下错误

[invalid_token_response] An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response: Error while extracting response for type [class org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse] and content type [application/json;charset=utf-8]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: An error occurred reading the OAuth 2.0 Access Token Response: tokenType cannot be null; nested exception is java.lang.IllegalArgumentException: tokenType cannot be null

这是我的 application.yml

spring:
  security:
    oauth2:
      client:
        registration:
          linkedin:
            clientId: CLIENTID
            clientSecret: SECRET
            client-authentication-method: post
            authorization-grant-type: authorization_code
            redirect-uri: http://localhost:8080/login/oauth2/code/linkedin
            scope: r_liteprofile, r_emailaddress,w_member_social

            client-name: Linkedin

        provider:
          linkedin:          
            authorization-uri: https://www.linkedin.com/oauth/v2/authorization
            token-uri: https://www.linkedin.com/oauth/v2/accessToken
            user-info-uri: https://api.linkedin.com/v1/people/~?format=json
            user-name-attribute: id

任何想法如何解决这个问题或如何在 spring boot 中使用linkedin 进行身份验证

【问题讨论】:

  • 注意浏览器上的网络选项卡,以检查作为响应返回的确切内容。如果没有链接的应用程序,很难对其进行测试并确定问题。确认传递的参数是正确的,例如范围参数应按此处文档中所述的空间进行分叉。 docs.microsoft.com/en-us/linkedin/shared/authentication/…
  • 请求是spring security完成的,传递的参数都是正确的
  • 你好,你能解决这个问题吗?我也面临同样的情况
  • @vigamage 不是真的,我想不通,但我开始使用 scribejava,它更容易
    github.com/scribejava/scribejava/tree/master/scribejava-apis

标签: java spring spring-boot spring-security-oauth2 linkedin-api


【解决方案1】:

我有同样的错误。

An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response: Error while extracting response for type [class org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse] and content type [application/json]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: An error occurred reading the OAuth 2.0 Access Token Response: tokenType cannot be null; nested exception is java.lang.IllegalArgumentException: tokenType cannot be null

您在此处看到linkedin OAuth2 提供商在您请求令牌时抱怨tokenType cannot be null

Google, and certain other 3rd party identity providers, are more strict about the token type name that is sent in the headers to the user info endpoint. The default is “Bearer” which suits most providers and matches the spec, but if you need to change it you can set security.oauth2.resource.token-type. Reference 时会发生这种情况

为了有这个配置,我们需要引入spring-security-oauth2-autoconfigure

解决方案 1

依赖

  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.security.oauth.boot</groupId>
    <artifactId>spring-security-oauth2-autoconfigure</artifactId>
    <version>2.0.0.RC2</version>
  </dependency>

配置

.properties配置中,可以添加

security.oauth2.resource.token-type=Bearer

但是上面的解决方案并没有解决问题,所以我看看下一个解决方案。

解决方案 2

Reference

Reference

这是 Spring Security 5 的一个已知错误。LinkedIn 作为 OAuth2 授权提供商并不是一个非常受欢迎的选择。 Spring Security 与 Google 和其他提供商完美兼容并不奇怪,但它不符合 LinkedIn OAuth2 对客户的要求。

5.1。成功响应 授权服务器发出访问令牌和可选刷新令牌,并通过将以下参数添加到具有 200(OK)状态码的 HTTP 响应的实体主体来构造响应:
token_type 必需的。如第 7.1 节所述发行的令牌类型。值不区分大小写。

token_type 是必需参数,但 Spring Security 不会将其传递到请求中。

SecurityConfig 中,您可以通过设置.tokenEndpoint().accessTokenResponseClient(authorizationCodeTokenResponseClient()) 来指定您希望如何处理令牌响应 完整的演示代码在https://github.com/jzheaux/messaging-app/blob/master/client-app/src/main/java/sample/config/SecurityConfig.java#L61

安全配置

                .authorizationEndpoint()
                .baseUri(...)
                .authorizationRequestRepository(...)

                .and()
                .tokenEndpoint()
                .accessTokenResponseClient(authorizationCodeTokenResponseClient())

                .and()
                .redirectionEndpoint()
                .baseUri(...)

authorizationCodeTokenResponseClient()私有方法

    private OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> authorizationCodeTokenResponseClient() {
        OAuth2AccessTokenResponseHttpMessageConverter tokenResponseHttpMessageConverter =
                new OAuth2AccessTokenResponseHttpMessageConverter();
        tokenResponseHttpMessageConverter.setTokenResponseConverter(new CustomAccessTokenResponseConverter()); //https://github.com/jzheaux/messaging-app/blob/392a1eb724b7447928c750fb2e47c22ed26d144e/client-app/src/main/java/sample/web/CustomAccessTokenResponseConverter.java#L35

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

        DefaultAuthorizationCodeTokenResponseClient tokenResponseClient = new DefaultAuthorizationCodeTokenResponseClient();
        tokenResponseClient.setRestOperations(restTemplate);

        return tokenResponseClient;
    }

自定义转换器

package com.fermedu.resume.config;

import org.springframework.core.convert.converter.Converter;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class CustomAccessTokenResponseConverter implements Converter<Map<String, String>, OAuth2AccessTokenResponse> {
    private static final Set<String> TOKEN_RESPONSE_PARAMETER_NAMES = Stream.of(
            OAuth2ParameterNames.ACCESS_TOKEN,
            OAuth2ParameterNames.TOKEN_TYPE,
            OAuth2ParameterNames.EXPIRES_IN,
            OAuth2ParameterNames.REFRESH_TOKEN,
            OAuth2ParameterNames.SCOPE).collect(Collectors.toSet());

    @Override
    public OAuth2AccessTokenResponse convert(Map<String, String> tokenResponseParameters) {
        String accessToken = tokenResponseParameters.get(OAuth2ParameterNames.ACCESS_TOKEN);

        OAuth2AccessToken.TokenType accessTokenType = OAuth2AccessToken.TokenType.BEARER;

        long expiresIn = 0;
        if (tokenResponseParameters.containsKey(OAuth2ParameterNames.EXPIRES_IN)) {
            try {
                expiresIn = Long.valueOf(tokenResponseParameters.get(OAuth2ParameterNames.EXPIRES_IN));
            } catch (NumberFormatException ex) { }
        }

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

        Map<String, Object> additionalParameters = new LinkedHashMap<>();
        tokenResponseParameters.entrySet().stream()
                .filter(e -> !TOKEN_RESPONSE_PARAMETER_NAMES.contains(e.getKey()))
                .forEach(e -> additionalParameters.put(e.getKey(), e.getValue()));

        return OAuth2AccessTokenResponse.withToken(accessToken)
                .tokenType(accessTokenType)
                .expiresIn(expiresIn)
                .scopes(scopes)
                .additionalParameters(additionalParameters)
                .build();
    }
}


这个解决方案 2 对我有用。

【讨论】:

  • 我不再尝试将 SpringSecurity 与 LinkedIn OAuth2 集成。我没有找到其他 SDK,所以我放弃了 SpringSecurity,自己实现了 OAuth2。
猜你喜欢
  • 2018-08-29
  • 1970-01-01
  • 2014-09-22
  • 1970-01-01
  • 1970-01-01
  • 2022-09-28
  • 1970-01-01
  • 2015-11-22
  • 2019-01-14
相关资源
最近更新 更多