Я проводил обширные исследования о том, как аутентифицировать своего клиента (Android, iOS, веб-приложение) с облачными конечными точками, не требуя от пользователя использовать их учетную запись в учетной записи Google, как документация показывает вам.
Причиной этого является то, что я хочу защитить свой API или "заблокировать его" только для моих указанных клиентов. Иногда у меня будет приложение, у которого нет входа пользователя. Мне бы не хотелось приставать к моему пользователю, чтобы теперь войти в систему, чтобы мой API был безопасным. Или в другое время, я просто хочу управлять своими собственными пользователями, например, на веб-сайте, а не использовать Google+, Facebook или другое удостоверение подлинности для входа в систему.
Для начала позвольте мне сначала показать, как вы можете аутентифицировать свое Android-приложение с помощью API Cloud Endpoints с помощью входа в учетные записи Google, как указано в documentation. После этого я покажу вам свои выводы и потенциальную область для решения, с которым мне нужна помощь.
(1) Укажите идентификаторы клиентов (clientIds) приложений, уполномоченных делать запросы к вашему серверу API, и (2) добавить параметр пользователя ко всем открытым методам, которые будут защищены авторизацией.
public class Constants {
public static final String WEB_CLIENT_ID = "1-web-apps.apps.googleusercontent.com";
public static final String ANDROID_CLIENT_ID = "2-android-apps.googleusercontent.com";
public static final String IOS_CLIENT_ID = "3-ios-apps.googleusercontent.com";
public static final String ANDROID_AUDIENCE = WEB_CLIENT_ID;
public static final String EMAIL_SCOPE = "https://www.googleapis.com/auth/userinfo.email";
}
import com.google.api.server.spi.auth.common.User; //import for the User object
@Api(name = "myApi", version = "v1",
namespace = @ApiNamespace(ownerDomain = "${endpointOwnerDomain}",
ownerName = "${endpointOwnerDomain}",
packagePath="${endpointPackagePath}"),
scopes = {Constants.EMAIL_SCOPE},
clientIds = {Constants.WEB_CLIENT_ID, Constants.ANDROID_CLIENT_ID,
Constants.IOS_CLIENT_ID,
Constants.API_EXPLORER_CLIENT_ID},
audiences = {Constants.ANDROID_AUDIENCE})
public class MyEndpoint {
/** A simple endpoint method that takes a name and says Hi back */
@ApiMethod(name = "sayHi")
public MyBean sayHi(@Named("name") String name, User user) throws UnauthorizedException {
if (user == null) throw new UnauthorizedException("User is Not Valid");
MyBean response = new MyBean();
response.setData("Hi, " + name);
return response;
}
}
(3) В Android вызывается метод API в Asynctask, который должен пройти в переменной credential
в Builder
:
class EndpointsAsyncTask extends AsyncTask<Pair<Context, String>, Void, String> {
private static MyApi myApiService = null;
private Context context;
@Override
protected String doInBackground(Pair<Context, String>... params) {
credential = GoogleAccountCredential.usingAudience(this,
"server:client_id:1-web-app.apps.googleusercontent.com");
credential.setSelectedAccountName(settings.getString(PREF_ACCOUNT_NAME, null));
if(myApiService == null) { // Only do this once
MyApi.Builder builder = new MyApi.Builder(AndroidHttp.newCompatibleTransport(),
new AndroidJsonFactory(), credential)
// options for running against local devappserver
// - 10.0.2.2 is localhost IP address in Android emulator
// - turn off compression when running against local devappserver
.setRootUrl("http://<your-app-engine-project-id-here>/_ah/api/")
.setGoogleClientRequestInitializer(new GoogleClientRequestInitializer() {
@Override
public void initialize(AbstractGoogleClientRequest<?> abstractGoogleClientRequest) throws IOException {
abstractGoogleClientRequest.setDisableGZipContent(true);
}
});
// end options for devappserver
myApiService = builder.build();
}
context = params[0].first;
String name = params[0].second;
try {
return myApiService.sayHi(name).execute().getData();
} catch (IOException e) {
return e.getMessage();
}
}
@Override
protected void onPostExecute(String result) {
Toast.makeText(context, result, Toast.LENGTH_LONG).show();
}
}
Что происходит, так это то, что в Android-приложении сначала отображается учетная запись Google, сохраняя электронную почту аккаунта Google в своих общих настройках, а затем устанавливая ее как часть объекта GoogleAccountCredential
(подробнее о том, как сделайте это здесь).
Сервер Google App Engine получает ваш запрос и проверяет его. Если Клиент Android является одним из тех, что вы указали в нотации @Api
, тогда сервер будет внедрять объект com.google.api.server.spi.auth.common.User
в ваш метод API. Теперь вы должны проверить, является ли этот объект User
null
или нет в вашем методе API. Если объект User
null
, вы должны выбросить исключение в свой метод, чтобы предотвратить его запуск. Если вы не сделаете эту проверку, ваш метод API будет выполнен (нет-нет, если вы пытаетесь ограничить доступ к нему).
Вы можете получить свой ANDROID_CLIENT_ID
, перейдя в Google Developers Console. Там вы указываете имя пакета вашего приложения Android и SHA1, который генерирует для вас идентификатор клиента Android для использования в аннотации @Api
(или помещает его в класс Constants
, как указано выше для удобства использования).
Я провел некоторое тщательное тестирование со всем вышеперечисленным, и вот что я нашел:
Если вы укажете фиктивный или недопустимый Android clientId в аннотации @Api
, объект User
будет null
в вашем методе API. Если вы выполняете проверку на if (user == null) throw new UnauthorizedException("User is Not Valid");
, тогда ваш метод API не будет запущен.
Это удивительно, потому что похоже, что в облачных конечных точках происходит некоторая проверка за кулисами, которые проверяют, действительно ли Android ClientId действителен или нет. Если он недействителен, он не вернет объект User
, даже если конечный пользователь зарегистрировался в своей учетной записи Google и GoogleAccountCredential
был действительным.
Мой вопрос: кто-нибудь знает, как я могу проверить этот тип проверки ClientId самостоятельно в своих методах Cloud Endpoints? Может ли эта информация передаваться в HttpHeader
например?
Другим введенным типом в Cloud Endpoints является javax.servlet.http.HttpServletRequest
. Вы можете получить такой запрос в своем методе API:
@ApiMethod(name = "sayHi")
public MyBean sayHi(@Named("name") String name, HttpServletRequest req) throws UnauthorizedException {
String Auth = req.getHeader("Authorization");//always null based on my tests
MyBean response = new MyBean();
response.setData("Hi, " + name);
return response;
}
}
Но я не уверен, есть ли необходимая информация или как ее получить.
Конечно, где-то должны быть некоторые данные, которые сообщают нам, является ли Клиент авторизованным и указанным в @Api
clientIds
.
Таким образом, вы можете заблокировать свой API в своем приложении для Android (и, возможно, других клиентах), даже не прибегая к тому, чтобы ваши конечные пользователи приходили на вход (или просто создали свое собственное простое имя пользователя + пароль).
Чтобы все это работало, вы должны пройти в null
в третьем аргументе вашего Builder
следующим образом:
MyApi.Builder builder = new MyApi.Builder(AndroidHttp.newCompatibleTransport(), новый AndroidJsonFactory(), null)
Затем в вашем методе API извлеките, был ли вызов получен от аутентифицированного клиента, и либо выбросил исключение, либо выполнил любой код, который вы хотели.
Я знаю, что это возможно, потому что при использовании GoogleAccountCredential
в Builder
, как-то Cloud Endpoints знает, пришел ли вызов от аутентифицированного клиента, а затем либо введет его объект User
в метод API, либо нет основываясь на этом.
Может ли эта информация быть в заголовке или теле? Если да, то как я могу получить его, чтобы позже проверить, есть ли это в моем методе API?
Примечание. Я прочитал другие сообщения по этой теме. Они предлагают способы передать свой токен аутентификации - это нормально, но ваш .apk по-прежнему не будет защищен, если кто-то его декомпилирует. Я думаю, что если моя гипотеза будет работать, вы сможете заблокировать API Cloud Endpoints для клиента без каких-либо логинов.
Пользовательская аутентификация для конечных точек Google Cloud (вместо OAuth2)
Аутентификация моего приложения " к облачным конечным точкам Google не является "пользователем"
Конечные точки Google Cloud без учетных записей Google
EDIT: Мы использовали Gold Support для Google Cloud Platform и много раз разговаривали со своей службой поддержки в течение нескольких недель. Это их окончательный ответ для нас:
"К сожалению, мне не повезло. Я спросил об этом команда и проверили всю документацию. Похоже, использование OAuth2 это ваш единственный вариант. Причина в том, что серверы конечных точек обрабатывают перед тем, как он достигнет вашего приложения. Это означает, что вы не иметь возможность разрабатывать собственный поток аутентификации и получать результаты очень похоже на то, что вы видели с помощью токенов.
Я был бы рад предоставить вам запрос на функцию. Если бы ты мог предоставить немного больше информации о том, почему поток OAuth2 не работа для ваших клиентов, я могу поместить остальную информацию вместе и отправьте его менеджеру продукта".
(хмурое лицо) - однако, возможно, это все еще возможно?