Статьи

Использование токенов авторизации для служб IBM Watson

Это удобный маленький трюк, который я обнаружил на прошлой неделе. Это определенно задокументировано ( Использование токенов со службами Watson ), но я не сталкивался с этой функцией, пока не исследовал службу Watson. Еще в феврале я написал сообщение в блоге, в котором рассказывалось, как использовать службу Visual Recognition с приложением Cordova: с  помощью новой службы Bluemix Visual Recognition в Cordova . Хотя это работало нормально, у него была большая проблема.

Чтобы мое мобильное приложение могло общаться с удаленной службой, мне нужно было вписать имя пользователя и пароль в исходный код. Это, конечно, плохо ™, и я наконец-то нашел способ исправить это несколько месяцев назад:  реальное приложение с IBM Bluemix, Node, Cordova и Ionic . Решение состояло в том, чтобы настроить сервер Node.js, который действовал как прокси между мобильными приложениями и службами Bluemix. Это, конечно, не составило особого труда, особенно с учетом того, что у нас есть отличный пакет npm  watson-developer-cloud , который упрощает общение со службами.

Оказывается — есть еще более простой способ. Сервисы Bluemix поддерживают идею авторизации токенов. Вместо того, чтобы ваше мобильное приложение обращалось к Node.js, чтобы просто прокси-сервер удаленной службы, вы можете заставить ваше мобильное приложение нажимать Node.js и запрашивать токен авторизации. Токен подходит для одного сервиса, поэтому вам нужно будет вернуть несколько токенов, если вы используете несколько сервисов. Когда у вас есть этот токен, хорошей новостью является то, что вы можете пропустить нажатие на Node.js и вместо этого напрямую общаться с удаленным сервисом. Давайте посмотрим на пример. (И я настоятельно рекомендую вам прочитать две записи блога, на которые я ссылался выше, как приложение, и его функции описаны там).

Сначала давайте покажем сервер.

/*eslint-env node*/

var express = require('express');
var bluemix = require('./lib/bluemix.js');
var watson = require('watson-developer-cloud');

var extend = require('util')._extend;


var cfenv = require('cfenv');

var app = express();
//app.use(express.static(__dirname + '/public'));

var appEnv = cfenv.getAppEnv();

var credentials = extend({
   version: 'v1',
   username: 'get from bluemix',
   password: 'ditto'
}, bluemix.getServiceCreds('visual_recognition'));

var authorization = watson.authorization({
  username: credentials.username,
  password: credentials.password,
  version: 'v1',
  url: 'https://gateway.watsonplatform.net/authorization/api'
});

app.get('/getToken', function(req, res) {
console.log('ok, lets do this');

var params = {
url: 'https://gateway.watsonplatform.net/visual-recognition-beta/api'
};

authorization.getToken(params, function (err, token) {
if (!token) {
console.log('error:', err);
res.send("0");
} else {
res.send(token);
}
});

});

app.listen(appEnv.port, '0.0.0.0', function() {
console.log("server starting on " + appEnv.url);
});

Первая часть кода обрабатывает мою учетную информацию по умолчанию. Я получаю свое имя пользователя и пароль из консоли Bluemix, но когда я развертываю свой код в Bluemix, вместо этого он выбирает переменные среды.

Теперь взгляните на раздел авторизации. По большей части это, вероятно, имеет смысл, но есть кое-что, что, я гарантирую, сбьет вас с толку. Это определенно сбило меня с толку. Посмотрите на этот раздел кода в частности:

var authorization = watson.authorization({
  username: credentials.username,
  password: credentials.password,
  version: 'v1',
  url: 'https://gateway.watsonplatform.net/authorization/api'
});

Последний параметр там, URL,  не  является URL самого API. Мы вернемся к этому через минуту. Скорее, это похоже на «группу» с точки зрения того, какую услугу вы используете. Сервисы либо «регулярные», либо «потоковые». Обычный сервис будет использовать URL, который вы видите там: https://gateway.watsonplatform.net/authorization/api. Потоковый API будет использовать https://stream.watsonplatform.net/authorization/api.

Итак, ваш следующий вопрос: если это не очевидно, как мне узнать, какой тип сервиса я использую? Ответ в URL для самого сервиса. Например, вот тот, который я использую для визуального распознавания: https://gateway.watsonplatform.net/visual-recognition-beta/api. Видите «шлюз»? Да, это ваша подсказка. Сравните это с конечной точкой для преобразования текста в речь: https://stream.watsonplatform.net/speech-to-text/api. Вы можете видеть, что у него есть «поток» в домене. Все это, вероятно, довольно очевидно, и, когда я печатаю, это, конечно, выглядит очевидным, но, как я уже сказал, это сбило меня с толку. Кроме того, я обнаружил всю эту функцию, просмотрев документы для другой службы,   в моем браузере не  было открыто написанных  документов .

Вот и все. Я установил маршрут / getToken и вызываю API авторизации. Затем я просто возвращаю токен абоненту.

Теперь давайте посмотрим на код JavaScript. Как я упоминал ранее, я не буду рассматривать все приложение, а просто сосредоточусь на аспекте, связанном с этим изменением.

.controller('MainCtrl', function($scope,$ionicPlatform,$ionicLoading,$http) {

$scope.results = [];
$scope.cordovaReady = false;

var token;
var API_URL = "https://gateway.watsonplatform.net/visual-recognition-beta/api";

$ionicPlatform.ready(function() {

$http.get('http://localhost:6006/getToken').then(function(res) {
token = res.data;
console.log(token);
$scope.cordovaReady = true;
});

});

$scope.selectPicture = function() {

var gotPic = function(fileUri) {

$scope.pic = fileUri;
$scope.results = [];

$ionicLoading.show({template:'Sending to Watson...'});

//So now we upload it
var options = new FileUploadOptions();

options.fileKey="image";
options.fileName=fileUri.split('/').pop();
options.headers = {"X-Watson-Authorization-Token":token};

var ft = new FileTransfer();
ft.upload(fileUri, API_URL+"/v1/tag/recognize", function(r) {
var result = JSON.parse(r.response);

var results = [];
for(var i=0;i<result.images[0].labels.length;i++) {
results.push(result.images[0].labels[i].label_name);
}

$scope.$apply(function() {
$scope.results = results;
});

$ionicLoading.hide();


}, function(err) {
console.log('err from watsom', err);
}, options);

};

var camErr = function(e) {
console.log("Error", e);
}

navigator.camera.getPicture(gotPic, camErr, {
sourceType:Camera.PictureSourceType.PHOTOLIBRARY,
destinationType:Camera.DestinationType.FILE_URI
});

};

})

Итак, первое изменение заключается в том, что я немедленно звоню своему серверу, чтобы получить токен. Поскольку все мое приложение — «сделай снимок и идентифицируй дерьмо в нем», я загрузил саму кнопку для этого события загрузки.

Следующее изменение относится к объекту FileTransfer. Я должен добавить заголовок с токеном и, очевидно, должен изменить URL. Наконец, я должен немного помассировать результат. Ранее мое приложение Node.js делало это для меня. Сейчас я работаю с необработанным результатом удаленного сервиса, поэтому я делаю это в результате.

И вуаля — вот и все.

Снимок экрана симулятора 13 ноября 2015 г., 10.51.51