- PVSM.RU - https://www.pvsm.ru -
Google имеет несколько версий API для доступа к своим электронным таблицам. Разберемся с тем, как прочитать данные из spreadsheet таблицы в web-приложении на java используя API версии 4.
Создадим новый проект через google консоль [1].
Активируем Sheets API.
Чтобы использовать выбранный API, нужно создать учетные данные. Вызывать API будем из браузера.
Создадим идентификатор клиента OAuth 2 и зададим ограничения по URL. Указывать надо как продуктивные, так и разработческие url.
Окно запроса доступа тоже можно поднастроить, указав отображаемое имя, логотип и лицензию.
На выходе должен получиться файл учетных данных client_secrets.json. Готовый файл впоследствии необходимо разместить в ресурсах своего проекта.
Идем дальше. Google Sheets API v4 [2] поддерживает различные сценарии для авторизации [3] с использованием авторизационного кода:
Для нас подходят сценарии для web- и для клиентских приложений. Схема работы для них общая:
Первым делом необходимо запросить авторизационный ключ в google. Пользователю будет показана форма доступа. Получив код авторизации, его нужно обменять на токен доступа, без которого нельзя общаться с Google API. Последовательность действий можно понаблюдать в OAuth 2.0 песочнице [5].
За основу web-приложения возьмем spring boot [6]. Зависимости следующие:
<!-- Google OAuth Client Library for Java. -->
<dependency>
<groupId>com.google.oauth-client</groupId>
<artifactId>google-oauth-client-java6</artifactId>
<version>${google.oauth.client.version}</version>
</dependency>
<!-- Google OAuth2 API V2 Rev124 1.22.0 -->
<dependency>
<groupId>com.google.apis</groupId>
<artifactId>google-api-services-oauth2</artifactId>
<version>${google.oauth2.version}</version>
</dependency>
<!-- Google Sheets API V4 Rev38 1.22.0 -->
<dependency>
<groupId>com.google.apis</groupId>
<artifactId>google-api-services-sheets</artifactId>
<version>${google.sheets.version}</version>
</dependency>
Создадим два сервиса. GoogleConnection будет загружать клиентские данные из локального файла и хранить идентификационные данные после их получения.
@Service
public class GoogleConnectionService implements GoogleConnection {
private static final String CLIENT_SECRETS = "/client_secrets.json";
// ..
@Override
public GoogleClientSecrets getClientSecrets() {
if (clientSecrets == null) {
try {
// load client secrets
InputStreamReader clientSecretsReader = new InputStreamReader(getSecretFile());
clientSecrets = GoogleClientSecrets.load(Global.JSON_FACTORY, clientSecretsReader);
} catch (IOException e) {
e.printStackTrace();
}
}
return clientSecrets;
}
@Override
public Credential getCredentials() {
return credential;
}
// ..
}
А GoogleSheets будет выполнять основную работу — считывать табличные данные.
@Service
public class GoogleSheetsService implements GoogleSheets {
private Sheets sheetsService = null;
@Override
public List<List<Object>> readTable(GoogleConnection connection) throws IOException {
Sheets service = getSheetsService(connection);
return readTable(service, spreadsheetId, sheetName);
}
private Sheets getSheetsService(GoogleConnection gc) throws IOException {
if (this.sheetsService == null) {
this.sheetsService = new Sheets.Builder(Global.HTTP_TRANSPORT, Global.JSON_FACTORY, gc.getCredentials())
.setApplicationName(appName).build();
}
return this.sheetsService;
}
}
Всю последовательность операций распределим между тремя контроллерами. Контроллер для авторизации.
@RestController
public class GoogleAuthorizationController {
@Autowired
private GoogleConnectionService connection;
@RequestMapping(value = "/ask", method = RequestMethod.GET)
public void ask(HttpServletResponse response) throws IOException {
// Step 1: Authorize --> ask for auth code
String url = new GoogleAuthorizationCodeRequestUrl(connection.getClientSecrets(), connection.getRedirectUrl(), Global.SCOPES).setApprovalPrompt("force").build();
response.sendRedirect(url);
}
}
Результатом его работы будет редирект на google для логина.
Затем запрос на доступ google приложения к пользовательским таблицам:
В случае успешной авторизации контроллер обратной связи обменяет код на токены и перенаправит на исходный url. За сам обмен отвечает класс GoogleAuthorizationCodeTokenRequest [7].
@RestController
public class GoogleCallbackController {
@Autowired
private GoogleConnectionService connection;
@RequestMapping(value = "/oauth2callback", method = RequestMethod.GET)
public void callback(@RequestParam("code") String code, HttpServletResponse response) throws IOException {
// Step 2: Exchange code --> access tocken
if (connection.exchangeCode(code)) {
response.sendRedirect(connection.getSourceUrl());
} else {
response.sendRedirect("/error");
}
}
}
И, собственно, рабочий контроллер, реализующий чтение табличных данных.
@RestController
public class GoogleSheetController {
@Autowired
private GoogleConnection connection;
@Autowired
private GoogleSheets sheetsService;
@RequestMapping(value = "/api/sheet", method = RequestMethod.GET)
public ResponseEntity<List<List<Object>>> read(HttpServletResponse response) throws IOException {
List<List<Object>> responseBody = sheetsService.readTable(connection);
return new ResponseEntity<List<List<Object>>>(responseBody, HttpStatus.OK);
}
}
Также нам потребуется интерцептор, чтобы невозможно было обратиться к рабочему контроллеру без аутентификации.
public class GoogleSheetsInterceptor implements HandlerInterceptor {
// ..
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object obj) throws Exception {
if (connection.getCredentials() == null) {
connection.setSourceUrl(request.getRequestURI());
response.sendRedirect("/ask");
return false;
}
return true;
}
}
По сравнению с API v3, каждая строка представляет собой список объектов.
private List<List<Object>> readTable(Sheets service, String spreadsheetId, String sheetName) throws IOException {
ValueRange table = service.spreadsheets().values().get(spreadsheetId, sheetName).execute();
List<List<Object>> values = table.getValues();
return values;
}
Используя нотацию A1 [8] считываем постранично, а не блоками. Для этого добавим параметра id страницы и имя вкладки в настройки приложения.
google.spreadsheet.id=..
google.spreadsheet.sheet.name=..
Запускаем. Проверяем.
В результате должно сложиться представление о том, какие классы Google API служат для подключения к своим сервисам по OAuth 2.0 и как их можно использовать из web-приложения.
Код приложения, отвечающий за работу с OAuth2 можно упростить за счет spring. Для этого подключим Spring Security OAuth [9].
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>
Это нам позволит спрятать рутинные операции OAuth2 под капот и защитит наше приложение.
Перенесем пользовательские секреты в application.properties.
security.oauth2.client.client-id=Enter Client Id
security.oauth2.client.client-secret=Enter Client Secret
security.oauth2.client.accessTokenUri=https://accounts.google.com/o/oauth2/token
security.oauth2.client.userAuthorizationUri=https://accounts.google.com/o/oauth2/auth
security.oauth2.client.scope=openid,profile,https://www.googleapis.com/auth/spreadsheets
security.oauth2.resource.user-info-uri=https://www.googleapis.com/oauth2/v3/userinfo
Подключив security к проекту, мы уже активировали базовую аутентификацию [10]. Заменим её на что-то более подходящее.
Раз уж мы считываем данные из Google, пусть он и занимается аутентификацией пользователей для для нашего приложения. Для этого добавим всего лишь одну аннотацию @EnableOAuth2Sso.
@EnableOAuth2Sso
@SpringBootApplication
public class Application { //.. }
Будет создана и настроена точка аутентификации SSO. Нет необходимости переопределять WebSecurityConfigurerAdapter. Нам остаётся только задать пару параметров в конфигурации.
security.ignored=/
security.basic.enabled=false
security.oauth2.sso.login-path=/oauth2callback
В данном случае login-path должен соответствовать URI для редиректа, заданный в google проекте. А параметр scope должен содержать значение profile в том числе.
Дополнительные контроллеры и интерцептор больше не нужны. Теперь их работу будет выполнять spring.
Изменим класс GoogleConnection. Credential's мы будем создавать используя авторизационный код, сохраненный после аутентификации в OAuth2 контексте. А клиентские данные будем брать из конфига приложения.
@Service
public class GoogleConnectionService implements GoogleConnection {
@Autowired
private OAuth2ClientContext oAuth2ClientContext;
private GoogleCredential googleCredentials = null;
// ..
@Override
public Credential getCredentials() {
if (googleCredentials == null) {
googleCredentials = new GoogleCredential.Builder()
.setTransport(Global.HTTP_TRANSPORT)
.setJsonFactory(Global.JSON_FACTORY)
.setClientSecrets(clientId, clientSecret)
.build()
.setAccessToken(response.getAccessToken())
.setFromTokenResponse(oAuth2ClientContext
.getAccessToken().getValue());
}
return googleCredentials;
}
}
Отображение данных в браузере, обработку ошибок, использование сессии, logout и прочие вещи оставим без рассмотрения. Их наличие и настройка будет зависеть от конкретных требований.
На этом все. Рабочие исходники есть на github'e [11]. Разные подходы — по разным веткам.
Автор: cane
Источник [12]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/java/241500
Ссылки в тексте:
[1] google консоль: https://console.developers.google.com
[2] Google Sheets API v4: https://developers.google.com/sheets/api/
[3] сценарии для авторизации: https://developers.google.com/api-client-library/java/google-api-java-client/oauth2#authorization_code_flow
[4] Пример: https://developers.google.com/sheets/api/quickstart/java
[5] песочнице: https://developers.google.com/oauthplayground/
[6] spring boot: http://spring.io/
[7] GoogleAuthorizationCodeTokenRequest: https://google.github.io/google-api-java-client/releases/1.21.0/javadoc/index.html
[8] нотацию A1: https://developers.google.com/sheets/api/guides/concepts#a1_notation
[9] Spring Security OAuth: http://projects.spring.io/spring-security-oauth/
[10] базовую аутентификацию: https://ru.wikipedia.org/wiki/%D0%90%D1%83%D1%82%D0%B5%D0%BD%D1%82%D0%B8%D1%84%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D1%8F_%D0%B2_%D0%98%D0%BD%D1%82%D0%B5%D1%80%D0%BD%D0%B5%D1%82%D0%B5
[11] github'e: https://github.com/webcane/google-spreadsheet-api-samples
[12] Источник: https://habrahabr.ru/post/321444/?utm_source=habrahabr&utm_medium=rss&utm_campaign=sandbox
Нажмите здесь для печати.