Google имеет несколько версий API для доступа к своим электронным таблицам. Разберемся с тем, как прочитать данные из spreadsheet таблицы в web-приложении на java используя API версии 4.
Создадим новый проект через google консоль.
Активируем Sheets API.
Чтобы использовать выбранный API, нужно создать учетные данные. Вызывать API будем из браузера.
Создадим идентификатор клиента OAuth 2 и зададим ограничения по URL. Указывать надо как продуктивные, так и разработческие url.
Окно запроса доступа тоже можно поднастроить, указав отображаемое имя, логотип и лицензию.
На выходе должен получиться файл учетных данных client_secrets.json. Готовый файл впоследствии необходимо разместить в ресурсах своего проекта.
Идем дальше. Google Sheets API v4 поддерживает различные сценарии для авторизации с использованием авторизационного кода:
Для нас подходят сценарии для web- и для клиентских приложений. Схема работы для них общая:
Первым делом необходимо запросить авторизационный ключ в google. Пользователю будет показана форма доступа. Получив код авторизации, его нужно обменять на токен доступа, без которого нельзя общаться с Google API. Последовательность действий можно понаблюдать в OAuth 2.0 песочнице.
За основу web-приложения возьмем spring boot. Зависимости следующие:
Создадим два сервиса. GoogleConnection будет загружать клиентские данные из локального файла и хранить идентификационные данные после их получения.
А GoogleSheets будет выполнять основную работу — считывать табличные данные.
Всю последовательность операций распределим между тремя контроллерами. Контроллер для авторизации.
Результатом его работы будет редирект на google для логина.
Затем запрос на доступ google приложения к пользовательским таблицам:
В случае успешной авторизации контроллер обратной связи обменяет код на токены и перенаправит на исходный url. За сам обмен отвечает класс GoogleAuthorizationCodeTokenRequest.
И, собственно, рабочий контроллер, реализующий чтение табличных данных.
Также нам потребуется интерцептор, чтобы невозможно было обратиться к рабочему контроллеру без аутентификации.
По сравнению с API v3, каждая строка представляет собой список объектов.
Используя нотацию A1 считываем постранично, а не блоками. Для этого добавим параметра id страницы и имя вкладки в настройки приложения.
Запускаем. Проверяем.
В результате должно сложиться представление о том, какие классы Google API служат для подключения к своим сервисам по OAuth 2.0 и как их можно использовать из web-приложения.
Код приложения, отвечающий за работу с OAuth2 можно упростить за счет spring. Для этого подключим Spring Security OAuth.
Это нам позволит спрятать рутинные операции OAuth2 под капот и защитит наше приложение.
Перенесем пользовательские секреты в application.properties.
Подключив security к проекту, мы уже активировали базовую аутентификацию. Заменим её на что-то более подходящее.
Раз уж мы считываем данные из Google, пусть он и занимается аутентификацией пользователей для для нашего приложения. Для этого добавим всего лишь одну аннотацию @EnableOAuth2Sso.
Будет создана и настроена точка аутентификации SSO. Нет необходимости переопределять WebSecurityConfigurerAdapter. Нам остаётся только задать пару параметров в конфигурации.
В данном случае login-path должен соответствовать URI для редиректа, заданный в google проекте. А параметр scope должен содержать значение profile в том числе.
Дополнительные контроллеры и интерцептор больше не нужны. Теперь их работу будет выполнять spring.
Изменим класс GoogleConnection. Credential's мы будем создавать используя авторизационный код, сохраненный после аутентификации в OAuth2 контексте. А клиентские данные будем брать из конфига приложения.
Отображение данных в браузере, обработку ошибок, использование сессии, logout и прочие вещи оставим без рассмотрения. Их наличие и настройка будет зависеть от конкретных требований.
На этом все. Рабочие исходники есть на github'e. Разные подходы — по разным веткам.
Google-приложение
Создадим новый проект через google консоль.
Активируем Sheets API.
Чтобы использовать выбранный API, нужно создать учетные данные. Вызывать API будем из браузера.
Создадим идентификатор клиента OAuth 2 и зададим ограничения по URL. Указывать надо как продуктивные, так и разработческие url.
Окно запроса доступа тоже можно поднастроить, указав отображаемое имя, логотип и лицензию.
На выходе должен получиться файл учетных данных client_secrets.json. Готовый файл впоследствии необходимо разместить в ресурсах своего проекта.
Сценарии для авторизации
Идем дальше. Google Sheets API v4 поддерживает различные сценарии для авторизации с использованием авторизационного кода:
- Для web-серверных приложений. Необходимо реализовать пару определеных сервлетов и добавить их в свой web.xml
- Служебные учетные записи. Служебные учетные записи заводятся на google и предоставляют клиентам доступ к своим ресурсам, а не к ресурсам пользователя.
- Для установленных приложений. Пример работы с API из консольного приложения.
- Для клиентских приложений. В браузере формируем запрос на получение кода доступа, который потом можно обменять на токены.
- Для android.
Для нас подходят сценарии для web- и для клиентских приложений. Схема работы для них общая:
Первым делом необходимо запросить авторизационный ключ в google. Пользователю будет показана форма доступа. Получив код авторизации, его нужно обменять на токен доступа, без которого нельзя общаться с Google API. Последовательность действий можно понаблюдать в OAuth 2.0 песочнице.
Google oauth2 приложение
За основу web-приложения возьмем spring boot. Зависимости следующие:
<!-- 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 будет загружать клиентские данные из локального файла и хранить идентификационные данные после их получения.
GoogleConnectionService.java
@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 будет выполнять основную работу — считывать табличные данные.
GoogleSheetsService.java
@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;
}
}
Всю последовательность операций распределим между тремя контроллерами. Контроллер для авторизации.
GoogleAuthorizationController.java
@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.
GoogleCallbackController.java
@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");
}
}
}
И, собственно, рабочий контроллер, реализующий чтение табличных данных.
GoogleSheetController.java
@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);
}
}
Также нам потребуется интерцептор, чтобы невозможно было обратиться к рабочему контроллеру без аутентификации.
GoogleSheetsInterceptor.java
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 считываем постранично, а не блоками. Для этого добавим параметра id страницы и имя вкладки в настройки приложения.
google.spreadsheet.id=..
google.spreadsheet.sheet.name=..
Запускаем. Проверяем.
В результате должно сложиться представление о том, какие классы Google API служат для подключения к своим сервисам по OAuth 2.0 и как их можно использовать из web-приложения.
Spring sso приложение
Код приложения, отвечающий за работу с OAuth2 можно упростить за счет spring. Для этого подключим Spring Security OAuth.
<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 к проекту, мы уже активировали базовую аутентификацию. Заменим её на что-то более подходящее.
Раз уж мы считываем данные из 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 контексте. А клиентские данные будем брать из конфига приложения.
GoogleConnectionService.java
@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. Разные подходы — по разным веткам.
Поделиться с друзьями