spring-attic / spring-social

Allows you to connect your applications with SaaS providers such as Facebook and Twitter.
http://projects.spring.io/spring-social
Apache License 2.0
619 stars 351 forks source link

[spring-social-twitter] Could not authenticate you. with spring 5.1.5.RELEASE #317

Open leonchen83 opened 5 years ago

leonchen83 commented 5 years ago

Summary

spring-social-twitter post tweet failed with dependency spring 5.1.5.RELEASE

Actual Behavior

The exception stack is following.

Could not authenticate you.
org.springframework.social.MissingAuthorizationException: Authorization is required for the operation, but the API binding was created without authorization.
at org.springframework.social.twitter.api.impl.TwitterErrorHandler.handleClientErrors(TwitterErrorHandler.java:100)
at org.springframework.social.twitter.api.impl.TwitterErrorHandler.handleError(TwitterErrorHandler.java:60)
at org.springframework.web.client.ResponseErrorHandler.handleError(ResponseErrorHandler.java:63)
at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:778)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:736)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:710)
at org.springframework.web.client.RestTemplate.postForObject(RestTemplate.java:436)
at org.springframework.social.twitter.api.impl.TimelineTemplate.updateStatus(TimelineTemplate.java:198)
at org.springframework.social.twitter.api.impl.TimelineTemplate.updateStatus(TimelineTemplate.java:160)
at cn.nextop.erebor.batch.job.social.twitter.AbstractTwitterTasklet.post(AbstractTwitterTasklet.java:21)
at cn.nextop.erebor.batch.job.social.twitter.TwitterIndicatorTasklet.issue(TwitterIndicatorTasklet.java:167)
at cn.nextop.erebor.batch.job.social.twitter.TwitterIndicatorTasklet.doExecute(TwitterIndicatorTasklet.java:114)
at cn.nextop.erebor.core.batch.domain.spring.BatchTasklet.execute(BatchTasklet.java:206)
at org.springframework.batch.core.step.tasklet.TaskletStepChunkTransactionCallback.doInTransaction(TaskletStep.java:407)
at org.springframework.batch.core.step.tasklet.TaskletStepChunkTransactionCallback.doInTransaction(TaskletStep.java:331)
at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140)
at org.springframework.batch.core.step.tasklet.TaskletStep2.doInChunkContext(TaskletStep.java:273)
at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:82)
at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:375)
at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215)
at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:145)
at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:258)
at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:203)
at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:148)
at org.springframework.batch.core.job.AbstractJob.handleStep(AbstractJob.java:399)
at org.springframework.batch.core.job.SimpleJob.doExecute(SimpleJob.java:135)
at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:313)
at org.springframework.batch.core.launch.support.SimpleJobLauncher1.run(SimpleJobLauncher.java:144)
at java.util.concurrent.ExecutorsRunnableAdapter.call(Executors.java:511)

at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at cn.nextop.erebor.common.util.concurrent.future.impl.XRunnableFuture.run(XRunnableFuture.java:62)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutorWorker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)

Expected Behavior

no exception stack and tweet success.

Configuration

NOP

Version

from spring-social-core-1.1.0.RELEASE to recent version spring version: 5.1.5.RELEASE

Sample

final String key = "key";
final String token ="token";
final String secret1 = "secret1";
final String secret2 = "secret2";
final TwitterTemplate t = new TwitterTemplate(key, secret1, token, secret2);
t.timelineOperations().updateStatus("some content");

The root cause is in org.springframework.social.oauth1.SigningSupport line 191

if (bodyType != null && bodyType.equals(MediaType.APPLICATION_FORM_URLENCODED))

but int spring 5.1.5.RELEASE the default content-type change to application/x-www-form-urlencoded;charset:UTF-8 so sign the wrong signature.

workaround with spring 5.1.5.RELEASE

import static org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED;

public class ContentTypeInterceptor implements ClientHttpRequestInterceptor {
    @Override
    public ClientHttpResponse intercept ( HttpRequest request, byte[] body, ClientHttpRequestExecution execution )
    throws IOException {
        request.getHeaders().setContentType(APPLICATION_FORM_URLENCODED); return execution.execute(request, body);
    }
}

final String key = "your key";
final String token = "your token";
final String secret1 = "your secret1";
final String secret2 = "your secret2";
final TwitterTemplate t = new TwitterTemplate(key, secret1, token, secret2);
t.getRestTemplate().getInterceptors().add (0, new ContentTypeInterceptor());
t.timelineOperations().updateStatus("content")
HugovdWel commented 4 years ago

My IDE wasn't cooperating with the APPLICATION_FORM_URLENCODED. In case anyone else has a problem with the import, here it is:

import static org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED;

abhisekpadhi commented 3 years ago

Works for me with kotlin + spring.

Here is an example with kotlin + spring:

class ContentTypeInterceptor: ClientHttpRequestInterceptor {
    override fun intercept(
        request: HttpRequest,
        body: ByteArray,
        execution: ClientHttpRequestExecution
    ): ClientHttpResponse {
        request.headers.contentType = MediaType.APPLICATION_FORM_URLENCODED
        return execution.execute(request, body)
    }
}
...
val twitter = TwitterTemplate(a, b, c, d)
twitter.restTemplate.interceptors.add(0, ContentTypeInterceptor())