Microsoft MVP성태의 닷넷 이야기
.NET Framework: 190. 트위터 계정으로 로그인하는 C# 웹 사이트 제작 [링크 복사], [링크+제목 복사]
조회: 29835
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 1개 있습니다.)
트위터 계정으로 로그인하는 C# 웹 사이트 제작

사이트 만들 때 마다 사용자 로그인을 위한 DB를 유지해야 하는데요. 때로는, 그냥 유명 사이트의 계정과 연동하는 것이 여러 모로 이득이 되는 경우가 있습니다. 이번 글에서는, 우리가 흔히 알고 있는 사이트 중에서 "트위터(Twitter.com)"의 계정으로 인증 받을 수 있는 ASP.NET 웹 사이트를 제작해 보겠습니다.

이를 위해서 .NET에서 트위터 API 연동을 쉽게 해주는 라이브러리를 사용할 텐데요. 저는 Twitterizer를 선택했습니다. 다음의 웹 사이트에서 다운로드 받을 수 있고, 간단하게 Twitterizer2.dll을 참조하기만 하면 끝입니다.

Twitterizer - A .NET library for Twitter folk
; http://www.twitterizer.net/

Full - download
; http://www.twitterizer.net/files/Twitterizer2-2.3.zip

본격적인 구현에 앞서, 여러분들의 웹 사이트에서 트위터 연동 기능을 제공하려면 반드시 트위터 사이트에서 해당 (웹) 응용 프로그램을 등록시켜 주어야 합니다. 이에 대해서는 다음의 사이트에서 잘 설명해 주고 있습니다.

트위터 twitter API 어플리케이션 등록하기
; http://smok95.tistory.com/213

위의 글에서는 "Application Type"을 "Client"로 설정했는데, 이번 글의 예제를 위해서는 "Browser"로 바꿨으나 그다지 큰 의미는 없어 보입니다.

등록과정을 거치면 ConsumerKey와 CustomerSecret를 할당받게 되는데, 여기까지 완료하셨으면 "개발 준비" 단계까지는 완료되었다고 볼 수 있습니다. ^^




자,,, 그럼 한번 트위터 계정 로그인에 묻어가는 코드를 작성해 볼까요?

우선, "로그인" 버튼을 만들어야겠지요. ^^ default.aspx에 로그인 버튼을 하나 만들고 그에 대해 서버 측 Click 이벤트 핸들러를 등록합니다.

==== default.aspx ====

<asp:Button ID="Button1" runat="server" Text="Button" onclick="Button1_Click" />

==== default.aspx.cs ====

protected void Button1_Click(object sender, EventArgs e)
{
    string consumerKey = ConfigurationManager.AppSettings["ConsumerKey"];
    string consumerSecret = ConfigurationManager.AppSettings["ConsumerSecret"];

    OAuthTokenResponse otr = OAuthUtility.GetRequestToken(consumerKey, consumerSecret,
                                 "http://127.0.0.1:48236/oauth_twitter.aspx");
    Uri uri = OAuthUtility.BuildAuthorizationUri(otr.Token);
    Response.Redirect(uri.AbsoluteUri);
}

소스 코드를 찬찬히 들여다 보면, 일단, consumerKey, consumerSecret 문자열은 트위터에 응용 프로그램 등록하면서 받았던 문자열입니다. 위의 코드에서는 appSettings에 넣어두었던 것을 그대로 재사용한 것이고.

다음으로, RequestToken 값을 OAuthUtility.GetRequestToken 메서드를 이용해서 받아올 수 있습니다. 트위터 계정과 연동하는 것이기 때문에 반드시 트위터 웹 사이트에서 "사용자가 직접 로그인"하는 과정을 거치게 됩니다. 이를 위해서 트위터로 사용자 방문을 시켜야하는데, 트위터 측의 로그인 URL을 얻기 위해서 OAuthUtility.BuildAuthorizationUri 메서드를 호출하여 줍니다. 그런 다음, Response.Redirect시키면 사용자의 웹 브라우저는 트위터의 로그인 화면을 보여줍니다.

이제 사용자는 로그인을 하게 되고, ("Remember me" 옵션을 이전에 체크하고 로그인을 했다거나, 같은 브라우저 세션에서 이미 로그인을 했었다면 로그인 과정은 생략됨) 아래와 같이 사용자로 하여금 "consumerKey"에 해당하는 그 응용 프로그램을 신뢰할 것인지를 묻게 됩니다.

[그림 1: 응용 프로그램 측에 사용자 데이터 접근 허용]
integrate_twitter_account_1.png

이 과정에서 사용자가 "Allow" 버튼을 누르면 트위터 측에서는 이전에 OAuthUtility.GetRequestToken의 3번째 인자로 넘겨 주었던 "http://127.0.0.1:48236/oauth_twitter.aspx" 콜백 URL로 사용자 웹 브라우저를 이동시킵니다. 이 때, 인증받은 정보를 Query String 형식으로 다음과 같이 넘겨주게 됩니다.

oauth_twitter.aspx?oauth_token=...[Access 토큰값]&oauth_verifier=[PIN 정보]

특이하죠. 이 때문에, 사용자는 트위터 이외에 자신이 이용하려는 다른 웹 사이트가 계정 정보를 소유하지 못하도록 하는 효과를 갖습니다. 즉, 위의 "SysnetTestWebApp" 웹 사이트는 사용자의 트위터 ID/PW를 알 수 없고 인증되었다는 그 사실만을 알게 되는 것입니다.

"SysnetTestWebApp" 웹 사이트 측에서는, 사용자에 대한 정보나 사용자를 대신해서 트위터 서비스를 이용하려면 위의 콜백 URL에서 전달받았던 oauth_token, oauth_verifier 값을 이용해서 다음과 같이 Access 토큰 값을 얻을 수 있습니다.

==== oauth_twitter.aspx ====

public partial class oauth_twitter : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        string requestToken = Request.QueryString["oauth_token"];
        string accessVerifier = Request.QueryString["oauth_verifier"];

        string consumerKey = ConfigurationManager.AppSettings["ConsumerKey"];
        string consumerSecret = ConfigurationManager.AppSettings["ConsumerSecret"];

        OAuthTokenResponse accessTokenResponse
                = OAuthUtility.GetAccessToken(consumerKey, consumerSecret, requestToken, accessVerifier);

        string accessToken = accessTokenResponse.Token;
        string accessTokenSecret = accessTokenResponse.TokenSecret;

        TextBox1.Text = accessTokenResponse.UserId.ToString();
        TextBox2.Text = accessTokenResponse.ScreenName;
    }
}

==== 실행 결과 ====
사용자 ID: xxxxxxxx
사용자 이름: techsharer

그럼, 설명이 모두 끝난 것 같군요. 위에서 중요한 것은 accessToken, accessTokenSecret 값입니다. 만약, 다음 번에 "SysnetTestWebApp" 웹 사이트를 방문했을 때 로그인 과정을 생략하도록 "Remember Me"와 같은 기능을 제공하고 싶다면 "accessToken, accessTokenSecret" 2가지 값을 DB에 저장해 둔 후, 그 값을 조회할 수 있는 키를 사용자 측에 쿠키값으로 내려주는 방식으로 구현할 수 있습니다.

또, 어떻게 응용할 수 있을까요? 위의 웹 사이트는 전적으로 모든 인증 관리를 트위터에 맡겼지만, 이와 달리 자체 로그인 정보를 유지하는 웹 사이트가 그 사용자의 트위터 연동을 하고 싶은 경우도 있을 텐데요. 이런 경우에는 해당 사용자의 로그인 정보 테이블에 "accessToken, accessTokenSecret" 값을 모두 보관하고 있다가 트위터 연동 API를 호출해야 할 필요가 있을 때 액세스 토큰을 재 구성해서 트위터 API를 이용하는 것도 가능합니다.

실제로, 대부분의 트위터 연동 API는 "accessToken, accessTokenSecret" 값을 기반으로 하는 인증 토큰을 요구합니다. 예를 들어, 위에서처럼 로그인한 사용자의 Timeline 목록을 원한다면 다음과 같이 코딩하는 것이 가능합니다.

OAuthTokens authToken = new OAuthTokens();
authToken.AccessToken = accessToken;
authToken.AccessTokenSecret = accessTokenSecret;
authToken.ConsumerKey = consumerKey;
authToken.ConsumerSecret = consumerSecret;

TimelineOptions to = new TimelineOptions();
to.Count = 20;
to.IncludeRetweets = false;
TwitterResponse<TwitterStatusCollection> tr = TwitterTimeline.HomeTimeline(authToken, to);

TwitterStatusCollection tsc = tr.ResponseObject;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < tsc.Count; i++)
{
    TwitterStatus ts = tsc[i];
    sb.AppendLine(ts.CreatedDate.ToString() + " : " + ts.Text + " from " + ts.User.ScreenName);
}

이렇게 해서 트위터 계정 연동을 알아봤는데요. 개인적으로 아쉬웠던 점이 2가지 정도 있었습니다. (둘다 제가 방법을 모른 걸 수도 있습니다. ^^)

첫 번째는, 원래의 트위터 웹 사이트와 SSO(Single-sign on)가 되도록 하고 싶었는데, 이것이 가능하지 않는 것 같아서 구현을 하지 못했습니다. 즉, 사용자가 트위터 웹 사이트를 보다가 "SysnetTestWebApp" 웹 사이트로 이동을 하면 자연스럽게 로그인 상태로 나오면 좋겠는데,,, 안된다는 것!

두 번째는, 사용자가 트위터 웹 사이트로 이동한 후 로그인을 정상적으로 한 다음, 매번 "[그림 1]"과 같이 "허락"을 구하는 화면이 나온다는 것입니다. 좀 귀찮을 수도 있는 단계인데요. 아마도 이를 우회하기위해서라도 연동 웹 사이트 측에서는 "Remember Me"와 같은 기능을 구현해 두는 것이 좋을 것 같습니다.

음... 어쨌든, 테스트해 보고 나니 마음에 듭니다. 이참에 "SYSNET" 웹 사이트를 아예 트위터 계정 연동으로 돌려버릴까... 고민이 좀 되는군요. ^^

첨부한 소스는 지금까지의 코드를 포함하고 있으니 참고하시면 되겠습니다. ^^ (물론, consumerKey 등의 정보는 제거되었으니, 각자가 받은 키를 web.config의 appSettings에 넣어주시면 잘 동작할 것입니다.)




마지막으로, 참고할 만한 XAuth 인증 방법!

처음, Twitterizer2를 이용하여 프로그램을 하려고 했을 때는 Twitter API에 대해 잘 감싼 형태로 되어 있을 거라 판단하고 인증 과정을 단순하게 봤습니다. 실제로 Twitterizer에는 다음과 같이 ConsumerKey와 CustomerSecret 정보와 함께 트위터 계정 정보만으로 인증하는 API가 제공되고 있습니다.

string consumerKey = ConfigurationManager.AppSettings["ConsumerKey"];
string consumerSecret = ConfigurationManager.AppSettings["ConsumerSecret"];

OAuthTokenResponse otr = 
    XAuthUtility.GetAccessTokens(consumerKey, consumerSecret,
                 "[트위터 ID]", "[트위터 비밀번호");

하지만, 정작 실행을 하면 다음과 같이 예외가 발생합니다.

Twitterizer.TwitterizerException was unhandled by user code
    Message=The remote server returned an error: (401) Unauthorized.

<?xml version="1.0" encoding="UTF-8"?>
<hash>
  <error>Client application is not permitted to use xAuth.</error>
  <request>/oauth/access_token?x_auth_username=[계정ID]&x_auth_password=[계정암호]&x_auth_mode=client_auth</request>
</hash>

왜 오류가 발생하는지는 다음의 블로그에서 설명이 되어 있습니다.

Twitter Client/Browser 개발을 위한 인증(oAuth, xAuth)
; http://dreamofblue.tistory.com/73

그리고, 전체적인 OAuth 인증 과정 이해는 다음의 글을 참조하시면 도움이 됩니다.

트위터 twitter API - liboauth를 이용한 OAuth인증 및 API사용 예
; http://smok95.tistory.com/214

앞으로의 웹 세계를 생각한다면, OAuth나 Windows Identity Management와 같은 식의 인증 처리에 익숙해지는 것이 필수가 아닐까 싶군요. ^^




[파이썬의 경우]

Twitter 검색을 위한 개발자 플랫폼에 로그인하고,

Twitter Developer Platform
; https://developer.twitter.com/

App 생성과 함께 보이는 4개의 키 값을 복사한 후,

api_key = 'Fy...[생략]...HA'
api_secret = '4p...[생략]...vE'
access_token = '17...[생략]...1c'
access_token_secret = 'mV...[생략]...OK'

다음과 같이 tweepy 모듈로 간단하게 코딩!

import tweepy

auth = tweepy.OAuthHandler(api_key, api_secret)
auth.set_access_token(access_token, access_token_secret)

api = tweepy.API(auth)

keyword = '.NET5'
search = []

tweets = api.search(q=keyword, count=100)
for tweet in tweets:
    search.append(tweet)

data = {}
i = 1
print('[' + keyword + '에 대한 트윗 글')

for tweet in search:
    data['text'] = tweet.text
    print(i, ':', data)
    i += 1




연관은 없지만, 마찬가지로 SNS 검색이라는 관점에서 "NAVER 블로그 검색"도 해볼까요? ^^ 우선 개발자 플랫폼에,

Documents > 서비스 API > 검색
; https://developers.naver.com/docs/serviceapi/search/blog/blog.md#%EB%B8%94%EB%A1%9C%EA%B7%B8

Open API 이용 신청을 하고, App 생성 시 발급받는 client_key와 client_secret을 복사 후,

client_key = 'Uu...[생략]...gc'
client_secret = 'lc...[생략]...1w'

이렇게 간단하게 코딩 완료!

import urllib.parse
from urllib import request, parse
import json

query = '.NET5'
encText = urllib.parse.quote(query)

blog_search_url = 'https://openapi.naver.com/v1/search/blog?query=' + encText + '&display=100'

req = request.Request(blog_search_url)
req.add_header("X-Naver-Client-Id", client_key)
req.add_header("X-Naver-Client-Secret", client_secret)

resp = request.urlopen(req)
resp_code = resp.getcode()

if resp_code == 200:
    response_body = resp.read()
    dataList = json.loads(response_body)

    for data in dataList['items']:
        print(data['title'])
        print('[' + data['description'] + ']')
else:
    print('오류코드: ' + resp_code)




[이 토픽에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 6/28/2023]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2010-11-24 04시48분
[스티플의추억] 트위터 OAuth 의 인증 토큰 획득과 인증 토큰을 사용해서 api 쓰는 과정을 분석하실 분들은

http://www.voiceoftech.com/swhitley/index.php/2009/03/twitter-oauth-with-net/
위의 링크에 있는 소스코드가 심플해서 도움이 되실듯...

제 경우에는 twitterizer의 OAuth 내부 구현이 방대하고, 복잡한 편이라...
흐름의 줄을 놓치기 쉽상이더군요.
[guest]
2010-11-24 04시53분
[스티플의추억] xAuth라는게 10월달인가에 막힌 BasicAuth를 말하는것인가 보군요.
완전히 막힌줄 알았는데, 내부 심사를 거쳐서 허용을 해주는가 보군요.
제 생각에는 기존에 BasicAuth를 쓰던 대형서비스의 마이그레이션 기간을 주기 위해서, 남겨 놓은거 같네요.
신규 서비스는 OAuth를 무조건 써야 할테니 BasicAuth는 잊어버려도 될겁니다.
[guest]
2010-11-25 12시45분
xAuth에 대해서 오해가 있으신 것 같습니다. 제 글에서 소개한 "http://dreamofblue.tistory.com/73"에서도 스마트 폰 애플리케이션의 경우에는 oAuth 적용이 그다지 매끄럽지 않다는 경험을 적고 있습니다. 실제로, GetRequestToken 메서드에 의해서 전달되는 callback URL이 스마트 폰의 애플리케이션에 직접 연관시킬 수 있는 방법이 없기 때문에 사용자는 PIN 정보를 기억해서 응용 프로그램에 적어줘야 하는 문제 제기는 올바른 것 같습니다. xAuth가 필요한 부분이죠.

그리고, 10월에 막힌 방법은 HTTP의 Basic 인증 방식을 사용한 것으로 그것이 xAuth를 의미하는 것은 아닙니다. xAuth 역시 oAuth와 같은 인증 방식입니다.

About xAuth
; http://dev.twitter.com/pages/xauth

트위터의 위의 글에서도 공식적으로 언급하고 있지만, xAuth는 데스크 탑 및 모바일 응용 프로그램을 위한 적절한 인증 방식입니다.
정성태
2010-12-15 09시47분
[스티플의추억] 아~~ 제가 용어를 잘 몰라서 혼동했었군요.
xAuth라는게 윈폼(?)의 인증을 말하는것이로군요.
[guest]
2012-04-26 10시35분
OAuth 1.0에 대해서 이렇게 잘 설명한 포스트가 있을까요? ^^

OAuth와 춤을
; http://helloworld.naver.com/helloworld/24942
정성태
2015-09-03 04시58분
정성태
2022-05-26 10시32분
정성태

1  2  3  [4]  5  6  7  8  9  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13524정성태1/12/20242063오류 유형: 890. 한국투자증권 KIS Developers OpenAPI - GW라우팅 중 오류가 발생했습니다.
13523정성태1/12/20241887오류 유형: 889. Visual Studio - error : A project with that name is already opened in the solution.
13522정성태1/11/20242019닷넷: 2200. C# - HttpClient.PostAsJsonAsync 호출 시 "Transfer-Encoding: chunked" 대신 "Content-Length" 헤더 처리
13521정성태1/11/20242082닷넷: 2199. C# - 한국투자증권 KIS Developers OpenAPI의 WebSocket Ping, Pong 처리
13520정성태1/10/20241856오류 유형: 888. C# - Unable to resolve service for type 'Microsoft.Extensions.ObjectPool.ObjectPool`....'
13519정성태1/10/20241935닷넷: 2198. C# - Reflection을 이용한 ClientWebSocket의 Ping 호출파일 다운로드1
13518정성태1/9/20242162닷넷: 2197. C# - ClientWebSocket의 Ping, Pong 처리
13517정성태1/8/20242014스크립트: 63. Python - 공개 패키지를 이용한 위성 이미지 생성 (pystac_client, odc.stac)
13516정성태1/7/20242101닷넷: 2196. IIS - AppPool의 "Disable Overlapped Recycle" 옵션의 부작용
13515정성태1/6/20242372닷넷: 2195. async 메서드 내에서 C# 7의 discard 구문 활용 사례 [1]
13514정성태1/5/20242064개발 환경 구성: 702. IIS - AppPool의 "Disable Overlapped Recycle" 옵션
13513정성태1/5/20242002닷넷: 2194. C# - WebActivatorEx / System.Web의 PreApplicationStartMethod 특성
13512정성태1/4/20241975개발 환경 구성: 701. IIS - w3wp.exe 프로세스의 ASP.NET 런타임을 항상 Warmup 모드로 유지하는 preload Enabled 설정
13511정성태1/4/20241994닷넷: 2193. C# - ASP.NET Web Application + OpenAPI(Swashbuckle) 스펙 제공
13510정성태1/3/20241933닷넷: 2192. C# - 특정 실행 파일이 있는지 확인하는 방법 (Linux)
13509정성태1/3/20241965오류 유형: 887. .NET Core 2 이하의 프로젝트에서 System.Runtime.CompilerServices.Unsafe doesn't support netcoreapp2.0.
13508정성태1/3/20241998오류 유형: 886. ORA-28000: the account is locked
13507정성태1/2/20242692닷넷: 2191. C# - IPGlobalProperties를 이용해 netstat처럼 사용 중인 Socket 목록 구하는 방법파일 다운로드1
13506정성태12/29/20232186닷넷: 2190. C# - 닷넷 코어/5+에서 달라지는 System.Text.Encoding 지원
13505정성태12/27/20232673닷넷: 2189. C# - WebSocket 클라이언트를 닷넷으로 구현하는 예제 (System.Net.WebSockets)파일 다운로드1
13504정성태12/27/20232313닷넷: 2188. C# - ASP.NET Core SignalR로 구현하는 채팅 서비스 예제파일 다운로드1
13503정성태12/27/20232184Linux: 67. WSL 환경 + mlocate(locate) 도구의 /mnt 디렉터리 검색 문제
13502정성태12/26/20232285닷넷: 2187. C# - 다른 프로세스의 환경변수 읽는 예제파일 다운로드1
13501정성태12/25/20232084개발 환경 구성: 700. WSL + uwsgi - IPv6로 바인딩하는 방법
13500정성태12/24/20232173디버깅 기술: 194. Windbg - x64 가상 주소를 물리 주소로 변환
13498정성태12/23/20232810닷넷: 2186. 한국투자증권 KIS Developers OpenAPI의 C# 래퍼 버전 - eFriendOpenAPI NuGet 패키지
1  2  3  [4]  5  6  7  8  9  10  11  12  13  14  15  ...