什么是 SteemConnect
在讲这个问题之前,先说一个生活中的例子。大家都有过在淘宝上购物的经历,成功的应对了买家和卖家之间的信任问题是淘宝最成功的设计之一。我们购物时,并不了解卖家,无法信任卖家,但我们却需要在卖家发货之前付款。此时淘宝是怎么做的呢?淘宝让自己成为交易的第三方,买家把钱给了一个可以信任的第三方,就不担心自己的钱款安全了。
同样道理,我们在登录第三方的Steem应用时,把自己的用户名密码直接提交给第三方应用,特别是我们不熟悉的第三方应用是非常不安全的。所以我们也需要一个大家都可以可以信任的第三方,来保证我们的账号安全的同时,又能够把我们的部分权限授权给第三方应用。SteemConnect就是这样一个可以信任的第三方。
现在,Steem上的应用生态正变得越来越好,其中SteemConnect的出现起到了非常重要的作用。它为去中心化平台提供了一个安全的中心化授权接口,使用户与应用之间授权变得安全、简单又高效。
SteemConnect 基本原理
SteemConnect 有 V1(SC1) 和 V2(SC2) 版本,两者从架构上来讲有很大不同。SC1将加密后的私钥存储在Cookie 中,但 SC2 并没有这样做。SC2 采取了一种“多重授权(multi-authority)”的方式,只要你将相应的权限授予第三方应用,他们就可以在不使用你的私钥就可以进行经过你授权的操作,进行如发布、投票、回复等操作。你也可以在任何时候收回你对第三方的授权。
通过“多重授权”的方式,用户就能非常安全的使用第三方应用,而且 SC2 的安全性要远远高于 SC1,所以不推荐使用 SC1。以下的讲述中,如果不特殊指明, SteemConnect 特指SteemConnect V2。
SteemConnect 的“多重授权”的实现,使用了OAuth2标准。OAuth是一个关于授权的开放网络标准,在全世界得到广泛应用,目前的最新版本是2.0版。现在我们经常使用的微信、QQ注册登录第三方网站的授权都是使用的此标准。
开发者注册与设置
第三方应用要接入SteemConnect,需要事先注册,注册地址:https://v2.steemconnect.com/dashboard
登录之后,点击左下方的My Apps, 在 My Apps 界面中点击 New App:
需要花费 3 Steem 的费用来账号注册一个APP,此账号由官方管理,你接触不到私钥。账号名称没有强制要求,但惯例以app结尾,如busy.app, cnsteem.app等。
APP 注册成功之后,我们需要对 APP 进行配置,点击My Apps -> 选择 App -> 选择Edit:
最重要的是Redirect URI(s)的设置,它可以设置一个或多个,但一定要包含下文sc2.init
中设置的授权后返回的页面地址(callbackURL)。如在本地测试中(下文例程使用本地测试),用户申请授权后返回http://localhost:8080/ ,Redirect URI(s)中必须有相同的地址!
SteemConnect 应用开发
Steemit 官方为我们提供了完整的 Javascript 开发接口和示例,用户可以在github中找到完整的源码:
https://github.com/steemit/sc2-sdk
https://github.com/cnsteem/sc2-angular
本文下面以cnsteem提供的augular版本的SDK为例,来详细的讲述使用 SteemConnect 进行第三方应用的授权方法。
SteemConnect 初始化
在页面载入过程中,要对sc2进行初始化:
sc2.init({
app: 'cnsteem.app',
callbackURL: 'http://localhost:8080/',
scope: ['vote', 'comment']
});
上面的代码中,我们初始化了三个参数:
app: 此参数需要初始化为我们在 SteemConnect V2 后台注册的APP的名称。
callbackURL: 此参数为授权后跳转到的页面 URL。此 URL 必须包含在APP后台设置的 "Redirect URI(s)" 列表中。
scope: 此参数表示用户授权给APP的权限列表。权限列表及具体描述可以参照下表。
名称 描述 login 验证身份 offline 允许长期使用令牌 vote 对文章或评论点赞、踩或取消点赞 comment 发表评论或文章 comment_delete 删除评论或文章 comment_options 给文章或评论添加选项 custom_json 关注、取关、屏蔽、转发等任何 custom_json
操作claim_reward_balance 赎回奖励
除上述三个参数以外,还有一个参数 accessToken 也可以在sc2.init
中进行设置。
- accessToken: 授权成功后返回的“接入令牌”,授权成功后就可使用此 accessToken 执行相应的操作。
但在实际开发中,在进行初始化时,我们往往不知道 accessToken。这种情况下,我们可以在初始化之后使用sc2.setAccessToken(accessToken)
方法对此参数进行设置。
其他常用操作
下面的代码段使用AngularJs,在sc2.init
后,设置了accessToken
,并将读取评论、点赞、修改用户信息和登出等操作封装成函数,以方便使用。
//code area 1,对module进行初始化
var myapp = angular.module('app', [])
.config(['$locationProvider', function($locationProvider){
$locationProvider.html5Mode(true);
}]);
//code area 2,设置名为Main的controller
myapp.controller('Main', function($scope, $location) {
$scope.loading = false;
$scope.parentAuthor = 'skenan';
$scope.parentPermlink = 'steem-connect-v2';
//$location为AngularJs中对URL进行相关操作的变量
$scope.accessToken = $location.search().access_token;
$scope.expiresIn = $location.search().expires_in;
$scope.loginURL = sc2.getLoginURL();
if ($scope.accessToken) {
//设置 accessToken
sc2.setAccessToken($scope.accessToken);
//读取用户信息
sc2.me(function (err, result) {
console.log('/me', err, result);
if (!err) {
$scope.user = result.account;
$scope.metadata = JSON.stringify(result.user_metadata, null, 2);
$scope.$apply();
}
});
}
//是否通过验证
$scope.isAuth = function() {
return !!$scope.user;
};
//读取评论
$scope.loadComments = function() {
steem.api.getContentReplies($scope.parentAuthor, $scope.parentPermlink, function(err, result) {
if (!err) {
$scope.comments = result.slice(-5);
$scope.$apply();
}
});
};
//评论
$scope.comment = function() {
$scope.loading = true;
var permlink = steem.formatter.commentPermlink($scope.parentAuthor, $scope.parentPermlink);
sc2.comment($scope.parentAuthor, $scope.parentPermlink, $scope.user.name, permlink, '', $scope.message, '', function(err, result) {
console.log(err, result);
$scope.message = '';
$scope.loading = false;
$scope.$apply();
$scope.loadComments();
});
};
//点赞
$scope.vote = function(author, permlink, weight) {
sc2.vote($scope.user.name, author, permlink, weight, function (err, result) {
if (!err) {
alert('You successfully voted for @' + author + '/' + permlink);
console.log('You successfully voted for @' + author + '/' + permlink, err, result);
$scope.loadComments();
} else {
console.log(err);
}
});
};
//更新用户信息
$scope.updateUserMetadata = function(metadata) {
sc2.updateUserMetadata(metadata, function (err, result) {
if (!err) {
alert('You successfully updated user_metadata');
console.log('You successfully updated user_metadata', result);
if (!err) {
$scope.user = result.account;
$scope.metadata = JSON.stringify(result.user_metadata, null, 2);
$scope.$apply();
}
} else {
console.log(err);
}
});
};
//登出
$scope.logout = function() {
sc2.revokeToken(function (err, result) {
console.log('You successfully logged out', err, result);
delete $scope.user;
delete $scope.accessToken;
$scope.$apply();
});
};
});
Access Token在页面之间的传递
Access Token是登录成功的最重要的信息之一,用户使用Access Token可以进行相应的操作。在上文中 Access Token 是从 URL 中读取的,把Access Token放到 URL 中虽然也可以实现 Access Token 在页面之间的传递。但这种方法既不安全也不方便。在实际应用中,最常用的方法为将Access Token存储在Cookies中。
为了和原代码保持更高的一致性,使用了angular-cookie库。大家可以在github上找到这个库。话不多说,上代码:
myapp.controller('SetCookies', ['$scope', '$location', 'ipCookie', function($scope, $location, ipCookie) {
$scope.loading = false;
$scope.accessToken = $location.search().access_token;
$scope.expiresIn = $location.search().expires_in;
if ($scope.accessToken) {
sc2.setAccessToken($scope.accessToken);
ipCookie('st_access_token', $scope.accessToken,
{expirationUnit: 'seconds', expires: $scope.expiresIn * 1});
sc2.me(function (err, result) {
console.log('/me', err, result);
if (!err) {
$scope.user = result.account;
$scope.metadata = JSON.stringify(result.user_metadata, null, 2);
$scope.$apply();
}
});
}
$scope.isAuth = function() {
return !!$scope.user;
};
}]);
此段代码和上面的代码相比较,差别主要有以下三点:
使用了angular-cookie.js库
从 URL 读取Access Token之后将其存储到了Cookie中
在此设置之后,就可使用
ipCookie('st_access_token')
从Cookie中读取 Access Token。
使用示例
callback.html
该页为验证成功返回的页面,其功能主要是实现跳转及将Access Token存储在Cookie中,所以此页面使用的Controller为SetCookies。
<!DOCTYPE html> <html lang="en" ng-app="app"> <head> <title>Demo</title> <base href="/" /> <meta charset="UTF-8"> <meta id="viewport" name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> (html comment removed: [if lt IE 9]><script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]) <script src="sc2/angular.min.js"></script> <script src="sc2/angular-cookie.min.js"></script> <script src="sc2/sc2.min.js"></script> <script src="sc2/steem.min.js"></script> <script src="sc2/app.js"></script> </head> <body> <div ng-controller="SetCookies"> <div> <h3>在此页中主要是设置Cookies和跳转</h3> <a href="index.html">SteemThink</a> </div> </div> </body> </html>
index.html/testpage.html
此类页面为普通的页面,的这些页面中可以使用所用用户授权的操作。使用的Controller为Main,但已经将读取 Access Token 的位置从 URL 改为Cookie。
<!DOCTYPE html> <html lang="en" ng-app="app"> <head> <title>Demo</title> <base href="/" /> <meta charset="UTF-8"> <meta id="viewport" name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> (html comment removed: [if lt IE 9]><script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]) <script src="sc2/angular.min.js"></script> <script src="sc2/angular-cookie.min.js"></script> <script src="sc2/sc2.min.js"></script> <script src="sc2/steem.min.js"></script> <script src="sc2/app.js"></script> </head> <body> <div class="container py-5" style="max-width: 600px;" ng-controller="Main as main"> <div> <h3>登录 & 退出</h3> <b ng-show="isAuth()"><img src="//img.busy.org/@{{user.name}}?s=32" width="32" height="32"> @{{user.name}}</b> <button ng-show="isAuth()" class="ml-2 btn btn-secondary" type="submit" ng-click="logout()"> 退出 </button> <a class="btn btn-primary" ng-href="{{loginURL}}" ng-hide="isAuth()">登录</a> </div> <hr /> <h3>评论 & 点赞</h3> <div class="input-group input-group-lg"> @{{parentAuthor}}/{{parentPermlink}} 此帖子的评论: </div> <ul class="list-group my-4" ng-init="loadComments()"> <li class="list-group-item" ng-repeat="comment in comments"> @{{comment.author}}: {{comment.body}} <button ng-show="isAuth()" class="ml-2 btn btn-secondary btn-sm" type="submit" ng-click="vote(comment.author, comment.permlink, 1000)"> <i class="icon iconfont icon-praise"></i> {{comment.net_votes}} </button> </li> </ul> <form ng-show="isAuth()" ng-submit="comment()"> <div class="input-group"> <input type="text" class="form-control" ng-disabled="loading" placeholder="Write a comment here" ng-model="message"> <span class="input-group-btn"> <button class="btn btn-primary" type="submit">Submit</button> </span> </div> </form> <hr /> <h3>用户信息</h3> <p>SteemConnect 允许开发者存储每一个用户的相关信息,例如用户偏好,但不会影响核心功能的使用。</p> <form ng-show="isAuth()" ng-submit="updateUserMetadata({ locale: locale })"> <div class="input-group"> <input type="text" class="form-control" placeholder="What is your locale?" ng-model="locale"> <span class="input-group-btn"> <button class="btn btn-primary" type="submit">Save</button> </span> </div> <pre class="mt-4">{{metadata}}</pre> </form> </div> </body> </html>
如果想要查看完整的代码,可以参考一下网站:
源码(从 URL 读取Access Token版本):https://github.com/cnsteem/sc2-angular
示例网站(从 URL 读取Access Token版本):https://cnsteem.github.io/sc2-angular
源码(从 Cookie 读取Access Token版本):https://github.com/RileyGe/sc2-cookie
示例网站(从 URL 读取Access Token版本):https://rileyge.github.io/sc2test
Upvoted ☝ Have a great day!
AngularJs 还是很牛的
恩,使用最后深有同感。
这个是用php语言吧?看起来似乎不错,可以试下
这个都是前端的语言,说白了都是js。
不过用了一个叫AngularJs的库,功能强大。
@rileyge, 不错!也教教我怎么能写出这样的好文!
@rileyge, 伦家就觉得你写得不错嘛~~~
顺祝狗年大吉,赚好多好多的STEEM/SBD哟!嘻嘻...
写的真不错,很详细,收藏了
谢谢支持。
说声抱歉,之前有两张图片没有加上,现在已经改正。
看了半天 steemjs 迷惑中, 看到这个 瞬间心情好多了
感谢你,麻烦 请收下我的膝盖吧
写的非常好,写完可以上传的steem指南的github哦,这样才可以让更多的人看见你的文章哦!
本文已收进steemh.org