Читаємо Google-таблиці з web-додатки

Google має кілька версій API для доступу до електронних таблиць. Розберемося з тим, як прочитати дані з spreadsheet таблиці в web-додатку на java використовуючи API версії 4.

Google-додаток
Створимо новий проект через google консоль.

створення нового проекту google
Активуємо Sheets API.

пошук API для активації
Щоб використовувати вибраний API, потрібно створити облікові дані. Викликати API будемо з браузера.

створення облікових даних
Створимо ідентифікатор клієнта OAuth 2 і задамо обмеження по URL. Вказувати треба як продуктивні, так і розробницькі url.

створення ідентифікатора клієнта
Вікно запиту доступу теж можна підналаштувати, вказавши коротке ім'я, логотип і ліцензію.

створення облікових даних 2
На виході отримати файл облікових даних client_secrets.json. Готовий файл згодом необхідно розмістити в ресурсах свого проекту.

Сценарії для авторизації
Йдемо далі. Google Sheets API v4 підтримує різні сценарії для авторизації з використанням авторизаційного коду:

  • Для web-серверних додатків. Необхідно реалізувати кілька певних сервлетів і додати їх в свій web.xml
  • Службові облікові записи. Службові облікові записи ведуться на google і надають клієнтам доступ до своїх ресурсів, а не до ресурсів.
  • Для встановлених програм. Приклад роботи з API з консольного додатку.
  • Для клієнтських додатків. У браузері формуємо запит на отримання коду доступу, який потім можна обміняти на токени.
  • Для android.
Для нас підходять сценарії для web — і для клієнтських додатків. Схема роботи для них загальна:

процес использвание OAuth 2.0 для 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
Потім запит на доступ 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. Різні підходи — по різним гілкам.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.