如何让 Linkedin OAuth 在 Spring Boot 中工作

Posted

技术标签:

【中文标题】如何让 Linkedin OAuth 在 Spring Boot 中工作【英文标题】:How to get Linkedin OAuth working in Spring Boot 【发布时间】:2020-03-04 17:31:25 【问题描述】:

在使用 Spring OAuth2 的 Spring boot 2.1.6.RELEASE 项目中拥有 Login with Linkedin 需要帮助。 Java版本是11

Google 和 Github 非常简单,并且在同一个项目中工作。我在 Spring-Social 中尝试了几个示例代码,但由于 Spring 引导版本不同,它们失败了。

下面的application.properties 不起作用(也试过client-authentication-method=post),并在从linkedin 检索授权码后被重定向回来(授权码是有效的,我可以使用它从Postman 获取访问令牌)。

spring.security.oauth2.client.registration.linkedin.provider=linkedin
spring.security.oauth2.client.registration.linkedin.client-name=Linkedin
spring.security.oauth2.client.registration.linkedin.client-id=******
spring.security.oauth2.client.registration.linkedin.client-secret=******
spring.security.oauth2.client.registration.linkedin.redirect-uri=*****
spring.security.oauth2.client.registration.linkedin.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.linkedin.client-authentication-method=form
spring.security.oauth2.client.registration.linkedin.scope=r_emailaddress,r_liteprofile
spring.security.oauth2.client.provider.linkedin.authorization-uri=https://www.linkedin.com/oauth/v2/authorization
spring.security.oauth2.client.provider.linkedin.token-uri=https://www.linkedin.com/oauth/v2/accessToken
spring.security.oauth2.client.provider.linkedin.user-info-uri=https://api.linkedin.com/v2/me
spring.security.oauth2.client.provider.linkedin.user-info-authentication-method=post

SecurityConfig 类(也尝试不使用antMatchers):

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter 

    @Override
    protected void configure(HttpSecurity http) throws Exception 
        http.authorizeRequests().antMatchers("*linkedin*").permitAll()
                .anyRequest().authenticated()
                .and()
                .csrf().disable()
                .oauth2Login();
    

没有报错,code查询参数与state一起返回Spring后,重定向回Spring登录。

谢谢

【问题讨论】:

【参考方案1】:

我的工作配置:

spring:
  security:
    oauth2:
      client:
        registration:
          linkedin:
            client-id: ????
            client-secret: ????
            scope:  r_liteprofile, r_emailaddress
            authorization-grant-type: authorization_code
            redirect-uri: "baseUrl/login/oauth2/code/registrationId"
            client-name: LinkedIn
            client-authentication-method: post

        provider:
          linkedin:
            authorization-uri: https://www.linkedin.com/oauth/v2/authorization
            token-uri: https://www.linkedin.com/uas/oauth2/accessToken
            user-info-uri: https://api.linkedin.com/v2/me
            jwk-set-uri:
            user-name-attribute: id

最近的春天: org.springframework.security:spring-security-oauth2-client:5.2.2.RELEASE

【讨论】:

在我的情况下它不起作用,您能否分享一些它的源代码。谢谢 我被困在http://localhost:8080/oauth2/code/linkedin?code=AQSV... 流没有进一步移动。任何帮助表示赞赏。谢谢。【参考方案2】:

问题/原因是缺少 tokenType。

要重现在org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter这个地方设置断点:

    catch (AuthenticationException failed) 
        // Authentication failed
        unsuccessfulAuthentication(request, response, failed);

        return;
    

然后你会看到failed = 这个异常:

org.springframework.security.oauth2.core.OAuth2AuthenticationException: [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]; 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

对此的一个“解决方案”是添加一个添加 tokenType 的转换器。 看看https://github.com/spring-projects/spring-security/issues/5983#issuecomment-430620308 并添加这个调用.tokenEndpoint().accessTokenResponseClient(authorizationCodeTokenResponseClient()) 加上authorizationCodeTokenResponseClient() 的实现在提到的github-link (https://github.com/jzheaux/messaging-app/blob/master/client-app/src/main/java/sample/config/SecurityConfig.java#L71) 加上这个类https://github.com/jzheaux/messaging-app/blob/master/client-app/src/main/java/sample/web/CustomAccessTokenResponseConverter.java。

...为了完整起见:这是我在 application.properties 中的配置 sn-p:

spring.security.oauth2.client.registration.linkedin.client-id: ????
spring.security.oauth2.client.registration.linkedin.client-secret: ????
spring.security.oauth2.client.registration.linkedin.scope=r_emailaddress,r_liteprofile
spring.security.oauth2.client.registration.linkedin.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.linkedin.redirect-uri=baseUrl/login/oauth2/code/registrationId
spring.security.oauth2.client.registration.linkedin.client-name = LinkedIn
spring.security.oauth2.client.registration.linkedin.provider=linkedin
spring.security.oauth2.client.registration.linkedin.client-authentication-method = post
spring.security.oauth2.client.provider.linkedin.authorization-uri=https://www.linkedin.com/oauth/v2/authorization
spring.security.oauth2.client.provider.linkedin.token-uri=https://www.linkedin.com/oauth/v2/accessToken
spring.security.oauth2.client.provider.linkedin.user-info-uri=https://api.linkedin.com/v2/me
spring.security.oauth2.client.provider.linkedin.jwk-set-uri = 
spring.security.oauth2.client.provider.linkedin.user-name-attribute = id
spring.security.oauth2.client.provider.linkedin.user-info-authentication-method=post

【讨论】:

【参考方案3】:

这个问题可以通过WebClientReactiveAuthorizationCodeTokenResponseClient bean 自定义轻松解决——您需要修改响应并在其中添加"token_type: "Bearer" 字段。这可以使用 ExchangeFilterFunction 来实现。

所以,这里举个例子,让我们创建一个简单的 DTO:

@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy::class)
data class Oauth2TokenResponseDTO(

        val accessToken: String,
        val expiresIn: Int,
        val tokenType: String = OAuth2AccessToken.TokenType.BEARER.value

)

然后,在您的响应式安全配置中添加下一个:

private fun modifyResponse(): ExchangeFilterFunction 
        return ExchangeFilterFunction.ofResponseProcessor  response: ClientResponse ->
            response.bodyToMono(Oauth2TokenResponseDTO::class.java)
                    .map  objectMapper.writeValueAsString(it) 
                    .map  ClientResponse.from(response).body(it).build() 
        
    

最后一个:

@Bean
fun webClientReactiveAuthorizationCodeTokenResponseClient(): WebClientReactiveAuthorizationCodeTokenResponseClient 
    val tokenResponseClient = WebClientReactiveAuthorizationCodeTokenResponseClient()
    val webClient = WebClient.builder()
            .clientConnector(ReactorClientHttpConnector(HttpClient.create().wiretap(true)))
            .filter(modifyResponse())
            .build()
    tokenResponseClient.setWebClient(webClient)
    return tokenResponseClient

P.S.:请注意,如果您有其他 oauth2 提供程序 - 您需要在 ExchangeFilterFunction 中实现过滤器方法。

【讨论】:

以上是关于如何让 Linkedin OAuth 在 Spring Boot 中工作的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 iOS SDK 保存 LinkedIn 访问令牌?

无法添加 OAuth 2.0 范围 LinkedIn

无法添加 OAuth 2.0 范围 LinkedIn

Linkedin Oauth Javascript 授权“哦哦!”

Android中LinkedIn的Oauth 2.0授权

Linkedin oauth 访问令牌刷新不会增加过期时间