我遇到了以下问题:为了访问在线API,我需要进行身份验证。现在,我用自己的代码做了一切:
以下是代码:
@RestController
public class RandomController {
private final Random random;
public RandomController(Random random) {
this.random = random;
}
@RequestMapping(value = "/get", method = GET)
public int random(@RequestParam(value = "limit", defaultValue = "100") int limit) {
String bearerToken = getBearerToken();
int[] bounds = getBounds(bearerToken);
return computeRandom(bounds[0], bounds[1]);
}
private String getBearerToken() {
RestTemplate tokenTemplate = new RestTemplate();
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("client_id", "my id");
body.add("client_secret", "my secret");
body.add("grant_type", "client_credentials");
HttpHeaders headers = new HttpHeaders();
headers.add("Accept", "application/json");
HttpEntity<?> entity = new HttpEntity<>(body, headers);
ResponseEntity<String> res = tokenTemplate.exchange(
"https://bearer.token/get", POST, entity, String.class);
Map<String, Object> map = new BasicJsonParser().parseMap(res.getBody());
return (String) map.get("access_token");
}
private int[] getBounds(String bearerToken) {
RestTemplate configurationTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer " + bearerToken);
HttpEntity<?> entity = new HttpEntity<>(headers);
ResponseEntity<String> res = configurationTemplate.exchange(
"https://configurations.com/bounds", HttpMethod.GET, entity, String.class);
Map<String, Object> map = new BasicJsonParser().parseMap(res.getBody());
Map<String, Long> value = (Map<String, Long>) map.get("value");
int lowerBound = value.get("lower").intValue();
int upperBound = value.get("upper").intValue();
return new int[]{lowerBound, upperBound};
}
private int computeRandom(int lowerBound, int upperBound) {
int difference = upperBound - lowerBound;
int raw = random.nextInt(difference);
return raw + lowerBound;
}
}
它有效,但我在每次通话时都会浪费对令牌网址的调用。这就是我喜欢它的方式:
我可以在我的代码中执行此操作,但我已经在使用Spring Boot了。我想知道如何实现这一目标。是否有现有的过滤器,拦截器等等?
感谢您的见解。
答案 0 :(得分:1)
尝试使用Spring Security中的OAuth2RestTemplate。如果可能的话,应该注意获取令牌和缓存它。它应该在配置文件中配置,并在您调用API的任何地方注入。
答案 1 :(得分:1)
jny提到,要使用的课程是OAuth2RestTemplate。但是,它的构造函数需要OAuth2ProtectedResourceDetails的实现。
可以使用多种实现,使用client_credential
的实现是ClientCredentialsResourceDetails
。我希望只是将@EnableOAuth2Client
添加到我的配置类并在application.yml
中配置所需信息就足够了:
security:
oauth2:
client:
grant-type: client_credentials
client-id: my id
client-secret: my secret
access-token-uri: https://bearer.token/get
不幸的是,它不起作用。原因可以在课程OAuth2RestOperationsConfiguration
中找到:
@Configuration
@ConditionalOnClass(EnableOAuth2Client.class)
@Conditional(OAuth2ClientIdCondition.class)
public class OAuth2RestOperationsConfiguration {
@Configuration
@ConditionalOnNotWebApplication
protected static class SingletonScopedConfiguration {
@Bean
@ConfigurationProperties("security.oauth2.client")
@Primary
public ClientCredentialsResourceDetails oauth2RemoteResource() {
ClientCredentialsResourceDetails details = new ClientCredentialsResourceDetails();
return details;
}
...
}
...
}
似乎Spring框架假定 - 错误地说,只有非Web应用程序才能使用客户端凭据身份验证。而在我的情况下,这是提供商提供的唯一方式。
可以复制粘贴相关代码段: - )
我的最终代码如下:
@SpringBootApplication
public class RandomApplication {
public static void main(String[] args) {
SpringApplication.run(RandomApplication.class, args);
}
@Bean
@ConfigurationProperties("security.oauth2.client")
ClientCredentialsResourceDetails clientCredentialsResourceDetails() {
return new ClientCredentialsResourceDetails();
}
@Bean
OAuth2RestTemplate oAuth2RestTemplate() {
return new OAuth2RestTemplate(clientCredentialsResourceDetails());
}
@Bean
Random random() {
return new SecureRandom();
}
}
@RestController
public class RandomController {
private final OAuth2RestTemplate oAuth2RestTemplate;
private final Random random;
public RandomController(OAuth2RestTemplate oAuth2RestTemplate, Random random) {
this.oAuth2RestTemplate = oAuth2RestTemplate;
this.random = random;
}
@RequestMapping(value = "/get", method = GET)
public int random() {
int[] bounds = getBounds();
return computeRandom(bounds[0], bounds[1]);
}
private int[] getBounds() {
ResponseEntity<String> res = oAuth2RestTemplate.getForEntity(
"https://configurations.com/bounds", String.class);
Map<String, Object> map = new BasicJsonParser().parseMap(res.getBody());
Map<String, Long> value = (Map<String, Long>) map.get("value");
int lowerBound = value.get("lower").intValue();
int upperBound = value.get("upper").intValue();
return new int[]{lowerBound, upperBound};
}
private int computeRandom(int lowerBound, int upperBound) {
int difference = upperBound - lowerBound;
int raw = random.nextInt(difference);
return raw + lowerBound;
}
}
请注意,控制器的样板少得多,因为我将RestTemplate
替换为OAuth2RestTemplate
。