Как использовать OAuth2RestTemplate?
Я пытаюсь понять, как использовать объект OAuth2RestTemplate для использования моей защищенной службы REST OAuth2 (которая выполняется в другом проекте, и давайте предположим также на другом сервере и т. Д.)
моя служба отдыха:
http://localhost:8082/app/helloworld
-> Доступ к этому URL приводит к ошибке, так как я не аутентифицирован
Чтобы запросить токен, я бы пошел на:
http://localhost:8082/app/oauth/token?grant_type=password&client_id=restapp&client_secret=restapp&username=**USERNAME**&password=**PASSWORD**
После получения токена я могу подключиться к REST API, используя следующий URL (пример вставленного токена)
http://localhost:8082/app/helloworld/?access_token=**4855f557-c6ee-43b7-8617-c24591965206**
Теперь мой вопрос заключается в том, как мне реализовать второе приложение, которое может использовать этот защищенный REST API OAuth2? Я действительно не нашел ни одного рабочего примера, в котором вы предоставляете имя пользователя и пароль (например, из формы входа в систему), а затем генерируется токен, который можно использовать повторно для получения данных из API REST.
В настоящее время я пробовал что-то со следующими объектами:
BaseOAuth2ProtectedResourceDetails baseOAuth2ProtectedResourceDetails = new BaseOAuth2ProtectedResourceDetails();
baseOAuth2ProtectedResourceDetails.setClientId("restapp");
baseOAuth2ProtectedResourceDetails.setClientSecret("restapp");
baseOAuth2ProtectedResourceDetails.setGrantType("password");
// how to set user name and password ???
DefaultAccessTokenRequest accessTokenRequest = new DefaultAccessTokenRequest();
OAuth2ClientContext oAuth2ClientContext = new DefaultOAuth2ClientContext(accessTokenRequest());
OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(baseOAuth2ProtectedResourceDetails,oAuth2ClientContext);
Но это просто не работает:(
Любые идеи высоко ценятся или ссылки на рабочие примеры и учебные пособия с благодарностью.
3 ответа
Вы можете найти примеры написания клиентов Oauth здесь https://github.com/spring-projects/spring-security-oauth
В вашем случае вы не можете просто использовать стандартные или базовые классы для всего, у вас есть несколько классов, реализующих OAuth2ProtectedResourceDetails. Конфигурация зависит от того, как вы настроили свою службу Oauth, но исходя из ваших curl-соединений, я бы порекомендовал:
@EnableOAuth2Client
@Configuration
class MyConfig{
@Value("${oauth.resource:http://localhost:8082}")
private String baseUrl;
@Value("${oauth.authorize:http://localhost:8082/oauth/authorize}")
private String authorizeUrl;
@Value("${oauth.token:http://localhost:8082/oauth/token}")
private String tokenUrl
@Bean
protected OAuth2ProtectedResourceDetails resource() {
ResourceOwnerPasswordResourceDetails resource = new ResourceOwnerPasswordResourceDetails();
List scopes = new ArrayList<String>(2);
scopes.add("write");
scopes.add("read");
resource.setAccessTokenUri(tokenUrl);
resource.setClientId("restapp");
resource.setClientSecret("restapp");
resource.setGrantType("password");
resource.setScope(scopes);
resource.setUsername("**USERNAME**");
resource.setPassword("**PASSWORD**");
return resource;
}
@Bean
public OAuth2RestOperations restTemplate() {
AccessTokenRequest atr = new DefaultAccessTokenRequest();
return new OAuth2RestTemplate(resource(), new DefaultOAuth2ClientContext(atr));
}
}
@Service
@SuppressWarnings("unchecked")
class MyService {
@Autowired
private OAuth2RestOperations restTemplate;
public MyService() {
restTemplate.getAccessToken();
}
}
Не забывайте о @EnableOAuth2Client в вашем классе конфигурации, также я бы посоветовал попробовать сначала использовать URL-адреса, которые вы используете, с помощью curl, а также попытаться отследить его с помощью отладчика, поскольку множество исключений просто используется и никогда не распечатывается из-за безопасности. причины, поэтому становится немного трудно найти, где проблема. Вы должны использовать регистратор с включенным отладочным набором. Удачи
Я загрузил пример приложения Springboot на github https://github.com/mariubog/oauth-client-sample чтобы изобразить вашу ситуацию, потому что я не смог найти примеры для вашего сценария.
В ответе @mariubog ( /questions/3836065/kak-ispolzovat-oauth2resttemplate/3836076#3836076) я также использовал типы предоставления пароля, как в примере, но мне нужно было настроить схему аутентификации клиента для формы. Области не были поддержаны конечной точкой для пароля, и не было необходимости устанавливать тип предоставления, так как объект ResourceOwnerPasswordResourceDetails устанавливает это сам в конструкторе.
...
public ResourceOwnerPasswordResourceDetails() {
setGrantType("password");
}
...
Ключевым моментом для меня было то, что client_id и client_secret не добавлялись в объект формы для публикации в теле, если resource.setClientAuthenticationScheme(AuthenticationScheme.form);
не был установлен.
Смотрите переключатель в: org.springframework.security.oauth2.client.token.auth.DefaultClientAuthenticationHandler.authenticateTokenRequest()
Наконец, при подключении к конечной точке Salesforce необходимо добавить маркер пароля к паролю.
@EnableOAuth2Client
@Configuration
class MyConfig {
@Value("${security.oauth2.client.access-token-uri}")
private String tokenUrl;
@Value("${security.oauth2.client.client-id}")
private String clientId;
@Value("${security.oauth2.client.client-secret}")
private String clientSecret;
@Value("${security.oauth2.client.password-token}")
private String passwordToken;
@Value("${security.user.name}")
private String username;
@Value("${security.user.password}")
private String password;
@Bean
protected OAuth2ProtectedResourceDetails resource() {
ResourceOwnerPasswordResourceDetails resource = new ResourceOwnerPasswordResourceDetails();
resource.setAccessTokenUri(tokenUrl);
resource.setClientId(clientId);
resource.setClientSecret(clientSecret);
resource.setClientAuthenticationScheme(AuthenticationScheme.form);
resource.setUsername(username);
resource.setPassword(password + passwordToken);
return resource;
}
@Bean
public OAuth2RestOperations restTemplate() {
return new OAuth2RestTemplate(resource(), new DefaultOAuth2ClientContext(new DefaultAccessTokenRequest()));
}
}
@Service
@SuppressWarnings("unchecked")
class MyService {
@Autowired
private OAuth2RestOperations restTemplate;
public MyService() {
restTemplate.getAccessToken();
}
}
У меня другой подход, если вы хотите получить токен доступа и сделать вызов другой системе ресурсов с токеном доступа в заголовке
Spring Security поставляется с автоматической защитой: доступ к свойствам oauth2 из файла application.yml для каждого запроса и каждого запроса имеет SESSIONID, который он считывает и извлекает информацию о пользователе через принципала, поэтому вам нужно убедиться, что вы внедрили Principal в OAuthUser и получили accessToken и сделали вызов сервер ресурсов
Это ваш application.yml, измените в соответствии с вашим сервером аутентификации:
security:
oauth2:
client:
clientId: 233668646673605
clientSecret: 33b17e044ee6a4fa383f46ec6e28ea1d
accessTokenUri: https://graph.facebook.com/oauth/access_token
userAuthorizationUri: https://www.facebook.com/dialog/oauth
tokenName: oauth_token
authenticationScheme: query
clientAuthenticationScheme: form
resource:
userInfoUri: https://graph.facebook.com/me
@Component
public class OAuthUser implements Serializable {
private static final long serialVersionUID = 1L;
private String authority;
@JsonIgnore
private String clientId;
@JsonIgnore
private String grantType;
private boolean isAuthenticated;
private Map<String, Object> userDetail = new LinkedHashMap<String, Object>();
@JsonIgnore
private String sessionId;
@JsonIgnore
private String tokenType;
@JsonIgnore
private String accessToken;
@JsonIgnore
private Principal principal;
public void setOAuthUser(Principal principal) {
this.principal = principal;
init();
}
public Principal getPrincipal() {
return principal;
}
private void init() {
if (principal != null) {
OAuth2Authentication oAuth2Authentication = (OAuth2Authentication) principal;
if (oAuth2Authentication != null) {
for (GrantedAuthority ga : oAuth2Authentication.getAuthorities()) {
setAuthority(ga.getAuthority());
}
setClientId(oAuth2Authentication.getOAuth2Request().getClientId());
setGrantType(oAuth2Authentication.getOAuth2Request().getGrantType());
setAuthenticated(oAuth2Authentication.getUserAuthentication().isAuthenticated());
OAuth2AuthenticationDetails oAuth2AuthenticationDetails = (OAuth2AuthenticationDetails) oAuth2Authentication
.getDetails();
if (oAuth2AuthenticationDetails != null) {
setSessionId(oAuth2AuthenticationDetails.getSessionId());
setTokenType(oAuth2AuthenticationDetails.getTokenType());
// This is what you will be looking for
setAccessToken(oAuth2AuthenticationDetails.getTokenValue());
}
// This detail is more related to Logged-in User
UsernamePasswordAuthenticationToken userAuthenticationToken = (UsernamePasswordAuthenticationToken) oAuth2Authentication.getUserAuthentication();
if (userAuthenticationToken != null) {
LinkedHashMap<String, Object> detailMap = (LinkedHashMap<String, Object>) userAuthenticationToken.getDetails();
if (detailMap != null) {
for (Map.Entry<String, Object> mapEntry : detailMap.entrySet()) {
//System.out.println("#### detail Key = " + mapEntry.getKey());
//System.out.println("#### detail Value = " + mapEntry.getValue());
getUserDetail().put(mapEntry.getKey(), mapEntry.getValue());
}
}
}
}
}
}
public String getAuthority() {
return authority;
}
public void setAuthority(String authority) {
this.authority = authority;
}
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public String getGrantType() {
return grantType;
}
public void setGrantType(String grantType) {
this.grantType = grantType;
}
public boolean isAuthenticated() {
return isAuthenticated;
}
public void setAuthenticated(boolean isAuthenticated) {
this.isAuthenticated = isAuthenticated;
}
public Map<String, Object> getUserDetail() {
return userDetail;
}
public void setUserDetail(Map<String, Object> userDetail) {
this.userDetail = userDetail;
}
public String getSessionId() {
return sessionId;
}
public void setSessionId(String sessionId) {
this.sessionId = sessionId;
}
public String getTokenType() {
return tokenType;
}
public void setTokenType(String tokenType) {
this.tokenType = tokenType;
}
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
@Override
public String toString() {
return "OAuthUser [clientId=" + clientId + ", grantType=" + grantType + ", isAuthenticated=" + isAuthenticated
+ ", userDetail=" + userDetail + ", sessionId=" + sessionId + ", tokenType="
+ tokenType + ", accessToken= " + accessToken + " ]";
}
@RestController
public class YourController {
@Autowired
OAuthUser oAuthUser;
// In case if you want to see Profile of user then you this
@RequestMapping(value = "/profile", produces = MediaType.APPLICATION_JSON_VALUE)
public OAuthUser user(Principal principal) {
oAuthUser.setOAuthUser(principal);
// System.out.println("#### Inside user() - oAuthUser.toString() = " + oAuthUser.toString());
return oAuthUser;
}
@RequestMapping(value = "/createOrder",
method = RequestMethod.POST,
headers = {"Content-type=application/json"},
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
public FinalOrderDetail createOrder(@RequestBody CreateOrder createOrder) {
return postCreateOrder_restTemplate(createOrder, oAuthUser).getBody();
}
private ResponseEntity<String> postCreateOrder_restTemplate(CreateOrder createOrder, OAuthUser oAuthUser) {
String url_POST = "your post url goes here";
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
headers.add("Authorization", String.format("%s %s", oAuthUser.getTokenType(), oAuthUser.getAccessToken()));
headers.add("Content-Type", "application/json");
RestTemplate restTemplate = new RestTemplate();
//restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
HttpEntity<String> request = new HttpEntity<String>(createOrder, headers);
ResponseEntity<String> result = restTemplate.exchange(url_POST, HttpMethod.POST, request, String.class);
System.out.println("#### post response = " + result);
return result;
}
}
Моё простое решение. ИМХО это самый чистый.
Сначала создайте application.yml
spring.main.allow-bean-definition-overriding: true
security:
oauth2:
client:
clientId: XXX
clientSecret: XXX
accessTokenUri: XXX
tokenName: access_token
grant-type: client_credentials
Создать основной класс: Main
@SpringBootApplication
@EnableOAuth2Client
public class Main extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/").permitAll();
}
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
@Bean
public OAuth2RestTemplate oauth2RestTemplate(ClientCredentialsResourceDetails details) {
return new OAuth2RestTemplate(details);
}
}
Затем создайте класс контроллера: контроллер
@RestController
class OfferController {
@Autowired
private OAuth2RestOperations restOperations;
@RequestMapping(value = "/<your url>"
, method = RequestMethod.GET
, produces = "application/json")
public String foo() {
ResponseEntity<String> responseEntity = restOperations.getForEntity(<the url you want to call on the server>, String.class);
return responseEntity.getBody();
}
}
Maven зависимости
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.1.5.RELEASE</version>
</dependency>
</dependencies>