Статьи

ColdFusion и OAuth, часть 1 — Facebook

На прошлой неделе по какой-то причине у меня было несколько запросов на пример интеграции ColdFusion и OAuth. В итоге я создал быстрые демоверсии для Facebook, LinkedIn и Google. На этой неделе я буду вести блог по очереди, надеясь, что эти записи помогут другим. Сегодня я собираюсь поделиться кодом Facebook.

Прежде чем начать, я хочу предупредить людей. Я написал этот код очень быстро. Это не оптимизировано. Кроме того, человек, которому я помогал, был на ColdFusion 8. Так что код не совсем то, что я бы назвал в курсе. Конечно, он будет отлично работать с ColdFusion 10. Я обычно предполагаю, что люди берут мои примеры кода здесь как примеры кода, но я хотел быть более ясным, что этот код, вероятно, не будет наилучшей практикой.

Для начала убедитесь, что у вас есть доступ к порталу разработчиков Facebook (developer.facebook.com) и создайте новое приложение.

Вы можете называть это как хотите, но название должно как-то отражать ваш сайт. Пользователи увидят это при запуске вашего приложения, поэтому ознакомьтесь с ним. Вы можете игнорировать два других варианта.

На следующей странице запишите свой идентификатор приложения и секрет приложения:

Нажмите «Веб-сайт с Facebook Login» и введите значение для вашего сайта. Вы можете и, вероятно, должны использовать локальный домен. Другими словами, вы можете ввести что-то под localhost. Очевидно, это изменится на рабочий URL-адрес, как только вы закончите, но для тестирования, localhost подойдет. Для моего тестирования я использовал: http: // localhost / testingzone / cf8fb и нажал «Сохранить изменения».

Вот и все на стороне Facebook. Теперь давайте поговорим о процессе OAuth в целом. Я не буду вдаваться в подробности, так как OAuth уже обсуждался в другом месте, и моя цель здесь — продемонстрировать пример ColdFusion, но на высоком уровне процесс выглядит следующим образом:

  • Ваш сайт сообщает пользователю, что вы собираетесь отправить его в Facebook для аутентификации / подключения.
  • Вы создаете «специальную» ссылку, которая включает в себя некоторую необходимую ерунду в URL. Наряду с необходимой хренью, у вас будет дополнительная хрень. Так, например, многие провайдеры OAuth просят вас точно указать, что вы хотите использовать от пользователя. Т.е. сколько личных данных вам требуется. Ваша ссылка будет включать это, и Facebook тогда предупредит пользователя. То есть: «Этот сайт хочет взять с вас деньги на обед, прочитать вашу электронную почту и иметь отношения со своими близкими».
  • Пользователь нажимает и попадает на Facebook.com со специальным экраном. Смотрите предыдущий пункт о том, как этот экран может измениться.
  • Пользователь нажимает «Да» или «Нет» (или одобряет, или что-то еще).
  • Facebook отправляет вас обратно на ваш сайт. В URL будет флаг, который вы можете проверить, который сообщит вам, разрешил ли пользователь ваше приложение. Если они сделали, у вас также будет специальный код.
  • Вы берете этот код, делаете запрос (используя CFHTTP) в Facebook, чтобы получить токен секретного доступа.
  • Этот токен доступа затем позволяет вам получить материал. Что зависит от того, что вы просили (см. Второй пункт).

Вот и все — примерно — и что круто, так это то, что вы увидите такой же (по большей части) процесс и в моих следующих записях в блоге.

Имея это в виду, давайте посмотрим на код. Опять же, я хочу предупредить вас, что это немного грубо. Во-первых, Application.cfc:

<cfcomponent output="false">

  <cfset this.name = "cfb8fbG">
	<cfset this.sessionManagement = true>

	<cffunction name="onApplicationStart" output="false">
		<cfset application.fbappid = "foo">
		<cfset application.fbsecret = "goo">
		<cfset application.fbredirecturl = "http://localhost/testingzone/cf8fb/redir.cfm">
	</cffunction>

	<cffunction name="onRequestStart" output="false">
		<cfif isDefined("url.init")>
			<cfset onApplicationStart()>
		</cfif>
	</cffunction>

</cfcomponent>

Очевидно, что идентификатор приложения и секрет приложения удалены выше. URL перенаправления будет использоваться немного позже. Обратите внимание, что я также включил управление сессиями. Теперь давайте посмотрим на index.cfm.

<cfif not isDefined("session.fbcode")>

  <cfif isDefined("url.startfb")>
		<cfset session.state = createUUID()>
		<cflocation url="https://www.facebook.com/dialog/oauth?client_id=#application.fbappid#&redirect_uri=#application.fbredirecturl#&state=#session.state#&scope=friends_hometown" addtoken="false">
	</cfif>

	<a href="?startfb=1">Login with FB</a>


<cfelse>

	This is a FB user.
	<cfset session.fbAPI = createObject("component","facebook").init(session.fbaccesstoken)>


	<cfdump var="#session.fbAPI.getMe()#" label="me" expand="false">

	<cfdump var="#session.fbAPI.getFriends()#" label="friends">

</cfif>

Этот шаблон состоит из двух частей. Первая часть запускается, когда вы впервые попадаете на сайт и еще не подключились к Facebook. Я использую простую переменную Session для отслеживания этого. Я предоставляю быструю подсказку и при нажатии отправляю пользователя в Facebook. После размышлений, как я это сделал, было отчасти глупо. Я мог бы просто иметь ссылку в оригинальной HTML-ссылке.

Говоря о ссылке, обратите внимание на части к ней. client_id исходит из настроек нашего приложения. redirect_uri просто сообщает Facebook, куда отправлять пользователя, когда он OK / запрещает соединение. Как я уже сказал, вполне нормально, чтобы это было localhost во время тестирования. Переменная состояния является параметром безопасности. Я использую переменную Session для хранения UUID и обеспечения того, чтобы удаленный сайт (в данном случае Facebook) отправлял обратно то же состояние. Я также мог бы использовать его для других вещей, например, для государства. Представьте, что у меня есть 2 области моего сайта, где вы можете подключиться к Facebook. Я мог бы использовать это как способ сказать: «Я был в продуктах» или «Я был в музыке». Наконец, область действия ссылается на то, что я прошу, с точки зрения конфиденциальности пользователя. Проверьте документы Facebook для получения дополнительной информации о том, что вы можете установить в этом отношении.

Итак, что происходит, когда вы нажимаете? Вот снимок экрана.

Теперь давайте посмотрим на redir.cfm, основной обработчик результата от Facebook.

<cfif isDefined("url.code") and url.state is session.state>
  <cfset session.fbcode = url.code>

	<cfhttp url="https://graph.facebook.com/oauth/access_token?client_id=#application.fbappid#&redirect_uri=#urlEncodedFormat(application.fbredirecturl)#&client_secret=#application.fbsecret#&code=#session.fbcode#">

	<cfif findNoCase("access_token=", cfhttp.filecontent)>
		<cfset parts = listToArray(cfhttp.filecontent, "&")>
		<cfset at = parts[1]>
		<cfset session.fbaccesstoken = listGetAt(at, 2, "=")>
		<cflocation url="index.cfm" addtoken="false">
	<cfelse>
		<!---
		This is an error case. 
		--->
		<cfdump var="#cfhttp#">
	</cfif>



<cfelseif isDefined("url.error_reason")>

	<!--- Handle error here. Variables are:
	url.error_reason and error_description
	--->

</cfif>

Мы начнем с предположения, что Facebook вернул нам переменную кода в строке запроса. Мы также проверяем переменную состояния, о которой я упоминал ранее. На данный момент нам нужно получить токен доступа от Facebook. Это делается с помощью простого вызова CFHTTP. Обратите внимание, что мы возвращаемся в URI перенаправления. Мы больше не вернемся к redir.cfm, это всего лишь часть системы безопасности.

Если все работает нормально, результатом будет строка, которая выглядит следующим образом:

access_token = AAAX & истекает = 5183804

Вот где приведенный выше код разбора строки вступает в игру. Access_token — это то, что даст нам доступ к данным пользователя.

Если вы вернетесь к примеру кода выше для index.cfm, вы заметите, что у меня есть компонент Facebook, связанный с сеансом пользователя. Я могу передать токен доступа и использовать его для будущих звонков. Я написал этот компонент очень быстро. Он не имеет хорошей обработки ошибок или даже нумерации страниц, но вы можете увидеть пример получения моего профиля, а также моих друзей.

<cfcomponent output="false">

  <cffunction name="init" access="public" returnType="facebook" output="false">
		<cfargument name="accesstoken" type="string" required="true">
		<cfset variables.accesstoken = arguments.accesstoken>
		<cfreturn this>
	</cffunction>

	<cffunction name="getFriends" access="public" returnType="array" output="false">
		<cfset var httpResult = "">
		<cfset var initialResult = "">

		<cfhttp url="https://graph.facebook.com/me/friends?fields=name,hometown&access_token=#variables.accesstoken#" result="httpResult">
		<cfset initialResult = deserializeJSON(httpResult.filecontent)>
		<!---
		For now, skipping pagination.
		--->
		<cfreturn initialResult.data>

	</cffunction>

	<cffunction name="getMe" access="public" returnType="struct" output="false">
		<cfset var httpResult = "">

		<cfhttp url="https://graph.facebook.com/me?access_token=#variables.accesstoken#" result="httpResult">
		<cfreturn deserializeJSON(httpResult.filecontent)>

	</cffunction>

</cfcomponent>

Я не тратил много времени на их API, но он казался чертовски простым в использовании.

Вы можете задаться вопросом — во время тестирования — как мне отобразить этот экран разрешений? Вы хотите перейти к настройкам конфиденциальности Facebook, а затем управлять приложениями. Это может сбивать с толку, потому что есть еще одна ссылка, которая приводит вас к вашим приложениям в качестве разработчика . Вы хотите убедиться, что вы пришли сюда через настройки конфиденциальности, которые затем помогут вам управлять настройками приложения пользователя . Вот мой:

Видишь мое тестовое приложение там сверху? Если щелкнуть значок удаления (x), вы получите следующее приглашение:

Это звучит так, как будто это может полностью удалить приложение, но, чтобы было ясно, это только для конкретного пользователя . Безопасно подтвердить и протестировать вашу заявку снова.

Во всяком случае, я надеюсь, что это поможет. Я хочу пояснить, что здесь задействовано больше, чем то, что я показал, но я хотел поделиться своим примером приложения в надежде, что другие смогут его использовать.