我有一个使用 OAuth 1.0 保护某些资源的外部合作伙伴。我需要访问这些资源,我想使用 Spring Boot 和 Spring Security OAuth 来做到这一点。因为我不想使用 XML 配置,所以我已经搜索了一种通过 Java 配置设置所有内容的方法。我发现这个线程提供了一个如何做到这一点的例子。但是关于 OAuth 1.0 流程的一些事情对我来说并不清楚。
我的合作伙伴为 OAuth 提供了四个端点:一个提供消费者令牌的端点、一个request_token
端点、一个授权端点和一个access_token
端点。使用我当前的设置(如下所示),我可以获得请求令牌并调用授权端点。但是,授权端点不要求确认,而是期望作为 URL 参数的电子邮件和密码,并在检查凭据后返回以下内容:
oauth_verifier=a02ebdc5433242e2b6e582e17b84e313
这就是 OAuth 流程卡住的地方。
在阅读了一些关于 OAuth 1.0 的文章之后,通常的流程是这样的:
- 获取消费者令牌/密钥
request_token
通过端点使用消费者令牌获取 oauth 令牌- 重定向到授权 URL 并要求用户确认
- 使用验证者令牌重定向到消费者
- 用户验证令牌和 oauth 令牌通过
access_token
端点获取访问令牌
首先:我不清楚第 3 步和第 4 步。我找到了Spring Security OAuth 示例,但我不清楚在确认访问后如何将用户/验证者令牌发送回消费者。有人可以解释一下这是怎么做到的吗?
第二:鉴于我的合作伙伴端点不要求确认,而是立即返回一个 oauth 验证器,我如何在此设置中使用 Spring Security OAuth?我正在考虑实现我自己的授权端点,该端点调用我的合作伙伴的授权端点,然后以某种方式让我的消费者知道验证者,但我不确定如何做后面的部分。
这是到目前为止的代码(在上面提到的线程的帮助下;ConsumerTokenDto
因为它是微不足道的,所以被忽略了):
应用
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
端点
@RestController
public class Endpoint {
@Autowired
private OAuthRestTemplate oAuthRestTemplate;
private String url = "https://....";
@RequestMapping("/public/v1/meters")
public String getMeters() {
try {
return oAuthRestTemplate.getForObject(URI.create(url), String.class);
} catch (Exception e) {
LOG.error("Exception", e);
return "";
}
}
}
OAuth 配置
@Configuration
@EnableWebSecurity
public class OAuthConfig extends WebSecurityConfigurerAdapter {
@Autowired
private RestTemplateBuilder restTemplateBuilder;
private ConsumerTokenDto consumerTokenDto;
private static final String ID = "meters";
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/**").permitAll();
http.addFilterAfter(this.oauthConsumerContextFilter(), SwitchUserFilter.class);
http.addFilterAfter(this.oauthConsumerProcessingFilter(), OAuthConsumerContextFilterImpl.class);
}
private OAuthConsumerContextFilter oauthConsumerContextFilter() {
OAuthConsumerContextFilter filter = new OAuthConsumerContextFilter();
filter.setConsumerSupport(this.consumerSupport());
return filter;
}
private OAuthConsumerProcessingFilter oauthConsumerProcessingFilter() {
OAuthConsumerProcessingFilter filter = new OAuthConsumerProcessingFilter();
filter.setProtectedResourceDetailsService(this.prds());
LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> map = new LinkedHashMap<>();
// one entry per oauth:url element in xml
map.put(
new AntPathRequestMatcher("/public/v1/**", null),
Collections.singletonList(new SecurityConfig(ID)));
filter.setObjectDefinitionSource(new DefaultFilterInvocationSecurityMetadataSource(map));
return filter;
}
@Bean
OAuthConsumerSupport consumerSupport() {
CoreOAuthConsumerSupport consumerSupport = new CoreOAuthConsumerSupport();
consumerSupport.setProtectedResourceDetailsService(prds());
return consumerSupport;
}
@Bean
ProtectedResourceDetailsService prds() {
InMemoryProtectedResourceDetailsService service = new InMemoryProtectedResourceDetailsService();
Map<String, ProtectedResourceDetails> store = new HashMap<>();
store.put(ID, prd());
service.setResourceDetailsStore(store);
return service;
}
ProtectedResourceDetails prd() {
ConsumerTokenDto consumerToken = getConsumerToken();
BaseProtectedResourceDetails resourceDetails = new BaseProtectedResourceDetails();
resourceDetails.setId(ID);
resourceDetails.setConsumerKey(consumerToken.getKey());
resourceDetails.setSharedSecret(new SharedConsumerSecretImpl(consumerToken.getSecret()));
resourceDetails.setRequestTokenURL("https://.../request_token");
// the authorization URL does not prompt for confirmation but immediately returns an OAuth verifier
resourceDetails.setUserAuthorizationURL(
"https://.../authorize?email=mail&password=pw");
resourceDetails.setAccessTokenURL("https://.../access_token");
resourceDetails.setSignatureMethod(HMAC_SHA1SignatureMethod.SIGNATURE_NAME);
return resourceDetails;
}
// get consumer token from provider
private ConsumerTokenDto getConsumerToken() {
if (consumerTokenDto == null) {
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("client", "Client");
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(body, headers);
RestTemplate restTemplate = restTemplateBuilder.setConnectTimeout(1000).setReadTimeout(1000).build();
restTemplate.getInterceptors().add(interceptor);
restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()));
ResponseEntity<ConsumerTokenDto> response = restTemplate
.exchange("https://.../consumer_token", HttpMethod.POST, request,
ConsumerTokenDto.class);
consumerTokenDto = response.getBody();
}
return consumerTokenDto;
}
// create oauth rest template
@Bean
public OAuthRestTemplate oAuthRestTemplate() {
OAuthRestTemplate oAuthRestTemplate = new OAuthRestTemplate(prd());
oAuthRestTemplate.getInterceptors().add(interceptor);
return oAuthRestTemplate;
}
}