將 AngularJS 應用程序中的 Facebook 登錄與 Satellizer 集成
已發表: 2022-03-11隨著 AngularJS 等功能豐富的前端框架的到來,越來越多的邏輯正在前端實現,例如數據操作/驗證、身份驗證等。 Satellizer,一個易於使用的 AngularJS 基於令牌的身份驗證模塊,簡化了在 AngularJS 中實現身份驗證機制的過程,該庫內置了對 Google、Facebook、LinkedIn、Twitter、Instagram、GitHub、Bitbucket、Yahoo、Twitch 的支持和 Microsoft (Windows Live) 帳戶。
在本文中,我們將構建一個與此處類似的非常簡單的 webapp,它允許您登錄並查看當前用戶的信息。
身份驗證與授權
一旦您的應用開始集成用戶系統,您會經常遇到這兩個可怕的詞。 根據維基百科:
身份驗證是確認實體聲稱為真的單個數據(數據)的屬性的真實性的行為。
授權是指定對與信息安全和計算機安全相關的資源的訪問權限的功能,特別是對訪問控制的訪問權限。
用外行的話來說,讓我們舉一個博客網站的例子,有些人在做這個網站。 博主撰寫文章,經理驗證內容。 每個人都可以驗證(登錄)系統,但他們的權限(授權)不同,因此博主無法驗證內容,而管理員可以。
為什麼選擇衛星
您可以按照一些教程在 AngularJS 中創建自己的身份驗證系統,例如這個非常詳細的教程:JSON Web Token Tutorial: An Example in Laravel and AngularJS。 我建議閱讀這篇文章,因為它很好地解釋了 JWT(JSON Web 令牌),並展示了一種直接使用本地存儲和 HTTP 攔截器在 AngularJS 中實現身份驗證的簡單方法。
那麼為什麼是衛星呢? 主要原因是它支持少數社交網絡登錄,如 Facebook、Twitter 等。如今,特別是對於在移動設備上使用的網站,輸入用戶名和密碼非常麻煩,用戶希望能夠毫無障礙地使用您的網站通過使用社交登錄。 由於集成每個社交網絡的 SDK 並遵循他們的文檔是相當重複的,因此以最小的努力支持這些社交登錄會很好。
此外,Satellizer 是 Github 上的一個活躍項目。 Active 是這裡的關鍵,因為這些 SDK 變化非常頻繁,你不想時不時地閱讀他們的文檔(任何使用 Facebook SDK 的人都知道它有多煩人)
帶有 Facebook 登錄的 AngularJS 應用程序
這就是事情開始變得有趣的地方。
我們將構建一個具有常規登錄/註冊(即使用用戶名、密碼)機制並支持社交登錄的網絡應用程序。 這個 webapp 非常簡單,因為它只有 3 個頁面:
- 主頁:任何人都可以看到
- 登錄頁面:輸入用戶名/密碼
- 秘密頁面:只有登錄用戶才能看到
對於後端,我們將使用 Python 和 Flask。 Python 和 Flask 框架非常有表現力,所以我希望將代碼移植到其他語言/框架不會很難。 當然,我們將使用 AngularJS 作為前端。 對於社交登錄,我們將僅與 Facebook 集成,因為它是目前最流行的社交網絡。
開始吧!
第 1 步:引導項目
以下是我們將如何構建我們的代碼:
- app.py - static/ - index.html - app.js - bower.json - partials/ - login.tpl.html - home.tpl.html - secret.tpl.html
所有後端代碼都在app.py中。 前端代碼放在 static/ 文件夾中。 默認情況下,Flask 會自動提供 static/ 文件夾的內容。 所有部分視圖都在 static/partials/ 中,並由 ui.router 模塊處理。
要開始編寫後端代碼,我們需要 Python 2.7.* 並使用 pip 安裝所需的庫。 您當然可以使用virtualenv來隔離 Python 環境。 下面是需要放入 requirements.txt 的 Python 模塊列表:
Flask==0.10.1 PyJWT==1.4.0 Flask-SQLAlchemy==1.0 requests==2.7.0
要安裝所有這些依賴項:
pip install -r requirements.txt
在app.py 中,我們有一些引導 Flask 的初始代碼(為簡潔起見,省略了導入語句):
app = Flask(__name__) @app.route('/') def index(): return flask.redirect('/static/index.html') if __name__ == '__main__': app.run(debug=True)
接下來我們初始化bower 並安裝 AngularJS 和 ui.router:
bower init # here you will need to answer some question. when in doubt, just hit enter :) bower install angular angular-ui-router --save # install and save these dependencies into bower.json
安裝這些庫後,我們需要在index.html中包含 AngularJS 和 ui-router,並為 3 個頁面創建路由:主頁、登錄和機密。
<body ng-app="DemoApp"> <a ui-sref="home">Home</a> <a ui-sref="login">Login</a> <a ui-sref="secret">Secret</a> <div ui-view></div> <script src="bower_components/angular/angular.min.js"></script> <script src="bower_components/angular-ui-router/release/angular-ui-router.min.js"></script> <script src="main.js"></script> </body>
下面是我們在 main.js 中配置路由所需的代碼:
var app = angular.module('DemoApp', ['ui.router']); app.config(function ($stateProvider, $urlRouterProvider) { $stateProvider .state('home', { url: '/home', templateUrl: 'partials/home.tpl.html' }) .state('secret', { url: '/secret', templateUrl: 'partials/secret.tpl.html', }) .state('login', { url: '/login', templateUrl: 'partials/login.tpl.html' }); $urlRouterProvider.otherwise('/home'); });
此時如果你運行服務器 python app.py ,你應該在 http://localhost:5000 有這個基本界面
此時鏈接 Home、Login 和 Secret 應該可以工作並顯示相應模板的內容。
恭喜,您剛剛完成了骨架的設置! 如果遇到任何錯誤,請查看 GitHub 上的代碼
步驟#2:登錄並註冊
在此步驟結束時,您將擁有一個 Web 應用程序,您可以使用電子郵件和密碼註冊/登錄。
第一步是配置後端。 我們需要一個用戶模型和一種為給定用戶生成 JWT 令牌的方法。 下面顯示的 User 模型非常簡化,甚至不執行任何基本檢查,例如字段電子郵件是否包含“@”,或者字段密碼是否包含至少 6 個字符等。
class User(db.Model): id = db.Column(db.Integer, primary_key=True) email = db.Column(db.String(100), nullable=False) password = db.Column(db.String(100)) def token(self): payload = { 'sub': self.id, 'iat': datetime.utcnow(), 'exp': datetime.utcnow() + timedelta(days=14) } token = jwt.encode(payload, app.config['TOKEN_SECRET']) return token.decode('unicode_escape')
我們使用python中的jwt模塊在JWT中生成payload部分。 iat 和 exp 部分對應於令牌創建和過期的時間戳。 在此代碼中,令牌將在 2 週後過期。
創建模型用戶後,我們可以添加“登錄”和“註冊”端點。 兩者的代碼非常相似,所以這裡我只展示“註冊”部分。 請注意,默認情況下,Satellizer 將分別為“登錄”和“註冊”調用端點/auth/login和/auth/signup 。
@app.route('/auth/signup', methods=['POST']) def signup(): data = request.json email = data["email"] password = data["password"] user = User(email=email, password=password) db.session.add(user) db.session.commit() return jsonify(token=user.token())
讓我們首先使用 curl 檢查端點:
curl localhost:5000/auth/signup -H "Content-Type: application/json" -X POST -d '{"email":"[email protected]","password":"xyz"}'
結果應如下所示:
{ "token": "very long string…." }
現在後端部分已經準備好了,讓我們來攻擊前端吧! 首先,我們需要安裝 satellizer 並將其作為依賴項添加到 main.js 中:
bower install satellizer --save
添加 satellizer 作為依賴項:
var app = angular.module('DemoApp', ['ui.router', 'satellizer']);
與之前的所有設置相比,在 satellizer 中登錄和註冊實際上非常簡單:
$scope.signUp = function () { $auth .signup({email: $scope.email, password: $scope.password}) .then(function (response) { // set the token received from server $auth.setToken(response); // go to secret page $state.go('secret'); }) .catch(function (response) { console.log("error response", response); }) };
如果您在設置代碼時遇到任何困難,可以查看 GitHub 上的代碼。
第 3 步:但秘密視圖並不是真正的秘密,因為任何人都可以看到它!
對,那是正確的! 到目前為止,任何人都可以在不登錄的情況下進入秘密頁面。
是時候在 AngularJS 中添加一些攔截器了,以確保如果有人進入秘密頁面並且如果該用戶沒有登錄,他們將被重定向到登錄頁面。
首先,我們應該添加一個標誌 requiredLogin 來區分秘密頁面和其他頁面。

.state('secret', { url: '/secret', templateUrl: 'partials/secret.tpl.html', controller: 'SecretCtrl', data: {requiredLogin: true} })
“數據”部分將在每次路由更改時觸發的 $stateChangeStart 事件中使用:
app.run(function ($rootScope, $state, $auth) { $rootScope.$on('$stateChangeStart', function (event, toState) { var requiredLogin = false; // check if this state need login if (toState.data && toState.data.requiredLogin) requiredLogin = true; // if yes and if this user is not logged in, redirect him to login page if (requiredLogin && !$auth.isAuthenticated()) { event.preventDefault(); $state.go('login'); } }); });
現在,用戶不登錄就無法直接進入秘密頁面。萬歲!
像往常一樣,這一步的代碼可以在這裡找到。
第 4 步:是時候獲取一些真正的秘密了!
此刻,秘密頁面中並沒有什麼真正的秘密。 讓我們把一些私人的東西放在那裡。
此步驟首先在後端創建一個端點,該端點只有經過身份驗證的用戶才能訪問,例如擁有一個有效的令牌。 下面的端點/user返回token對應的用戶的user_id和email 。
@app.route('/user') def user_info(): # the token is put in the Authorization header if not request.headers.get('Authorization'): return jsonify(error='Authorization header missing'), 401 # this header looks like this: “Authorization: Bearer {token}” token = request.headers.get('Authorization').split()[1] try: payload = jwt.decode(token, app.config['TOKEN_SECRET']) except DecodeError: return jsonify(error='Invalid token'), 401 except ExpiredSignature: return jsonify(error='Expired token'), 401 else: user_id = payload['sub'] user = User.query.filter_by(id=user_id).first() if user is None: return jsonify(error='Should not happen ...'), 500 return jsonify(id=user.id, email=user.email), 200 return jsonify(error="never reach here..."), 500
同樣,我們使用模塊jwt來解碼包含在“授權”標頭中的 JWT 令牌,並處理令牌過期或無效的情況。
讓我們使用 curl 測試這個端點。 首先,我們需要獲得一個有效的令牌:
curl localhost:5000/auth/signup -H "Content-Type: application/json" -X POST -d '{"email":"[email protected]","password":"xyz"}'
然後用這個令牌:
curl localhost:5000/user -H "Authorization: Bearer {put the token here}"
這給出了這個結果:
{ "email": "[email protected]", "id": 1 }
現在我們需要將此端點包含在 Secret Controller 中。 這非常簡單,因為我們只需要使用常規的 $http 模塊調用端點。 令牌會由 Satellizer 自動插入到標頭中,因此我們無需費心保存令牌然後將其放入正確的標頭中的所有細節。
getUserInfo(); function getUserInfo() { $http.get('/user') .then(function (response) { $scope.user = response.data; }) .catch(function (response) { console.log("getUserInfo error", response); }) }
最後,我們在秘密頁面中有一些真正私人的東西!
此步驟的代碼在 GitHub 上。
第 5 步:使用 Satellizer 登錄 Facebook
正如開頭提到的,Satellizer 的一個好處是它使集成社交登錄變得更加容易。 在此步驟結束時,用戶可以使用他們的 Facebook 帳戶登錄!
首先要做的是在 Facebook 開發者頁面上創建一個應用程序,以獲得一個application_id和一個密碼。 如果您還沒有 Facebook 開發者帳戶,請按照 developers.facebook.com/docs/apps/register 創建一個 Facebook 開發者帳戶並創建一個網站應用程序。 之後,您將擁有應用程序 ID 和應用程序密碼,如下面的屏幕截圖所示。
一旦用戶選擇連接 Facebook,Satellizer 將向端點/auth/facebook發送一個授權碼。 使用此授權代碼,後端可以從 Facebook /oauth端點檢索訪問令牌,該令牌允許調用 Facebook Graph API 以獲取用戶信息,例如位置、用戶朋友、用戶電子郵件等。
我們還需要跟踪用戶帳戶是通過 Facebook 創建的還是通過常規註冊創建的。 為此,我們將facebook_id添加到我們的用戶模型中。
facebook_id = db.Column(db.String(100))
facebook secret 是通過我們添加到app.config的環境變量 FACEBOOK_SECRET 配置的。
app.config['FACEBOOK_SECRET'] = os.environ.get('FACEBOOK_SECRET')
所以要啟動app.py ,你應該設置這個環境變量:
FACEBOOK_SECRET={your secret} python app.py
這是處理 Facebook 登錄的方法。 默認情況下,Satellizer 將調用端點/auth/facebook 。
@app.route('/auth/facebook', methods=['POST']) def auth_facebook(): access_token_url = 'https://graph.facebook.com/v2.3/oauth/access_token' graph_api_url = 'https://graph.facebook.com/v2.5/me?fields=id,email' params = { 'client_id': request.json['clientId'], 'redirect_uri': request.json['redirectUri'], 'client_secret': app.config['FACEBOOK_SECRET'], 'code': request.json['code'] } # Exchange authorization code for access token. r = requests.get(access_token_url, params=params) # use json.loads instead of urlparse.parse_qsl access_token = json.loads(r.text) # Step 2. Retrieve information about the current user. r = requests.get(graph_api_url, params=access_token) profile = json.loads(r.text) # Step 3. Create a new account or return an existing one. user = User.query.filter_by(facebook_id=profile['id']).first() if user: return jsonify(token=user.token()) u = User(facebook_id=profile['id'], email=profile['email']) db.session.add(u) db.session.commit() return jsonify(token=u.token())
為了向 Facebook 服務器發送請求,我們使用了方便的模塊 requests。 現在後端的困難部分已經完成。 在前端,添加 Facebook 登錄非常簡單。 首先,我們需要通過將這段代碼添加到app.config函數中來告訴 Satellizer 我們的facebook_id :
$authProvider.facebook({ clientId: {your facebook app id}, // by default, the redirect URI is http://localhost:5000 redirectUri: 'http://localhost:5000/static/index.html' });
要使用 Facebook 登錄,我們只需調用:
$auth.authenticate(“facebook”)
和往常一樣,你可以在 GitHub 上查看代碼
至此,webapp 的功能就已經完成了。 用戶可以使用常規電子郵件和密碼或使用 Facebook 登錄/註冊。 登錄後,用戶可以看到他的秘密頁面。
製作漂亮的界面
此時界面不是很漂亮,所以讓我們為佈局和角度烤麵包機模塊添加一點Bootstrap,以很好地處理錯誤消息,例如登錄失敗時。
這個美化部分的代碼可以在這裡找到。
結論
本文展示了在(簡單的)AngularJS webapp 中逐步集成 Satellizer。 使用 Satellizer,我們可以輕鬆添加其他社交登錄,例如 Twitter、Linkedin 等。 前端的代碼和文章裡的完全一樣。 但是,後端因社交網絡 SDK 具有不同的端點和不同的協議而有所不同。 您可以查看 https://github.com/sahat/satellizer/blob/master/examples/server/python/app.py,其中包含 Facebook、Github、Google、Linkedin、Twiter 和 Bitbucket 的示例。 如有疑問,您應該查看 https://github.com/sahat/satellizer 上的文檔。