您的位置:首页 > 其它

开放平台-web实现人人网第三方登录

2015-04-15 21:37 826 查看

应用场景

web应用通过人人网登录授权实现第三方登录。

操作步骤

1 注册成为人人网开放平台开发者
http://app.renren.com/developers/devinfo

2 准备一个可访问的域名,如dev.foo.com
3 创建网页应用,配置必要信息,其中包括根域名、图标信息

4 获取应用appID、appKey进行开发

登录流程

采用server-side方式实现登录授权,流程如下





流程描述
1 server端页面跳转到登录授权页面(Authorization code方式)
2 回调获得code
3 置换accessToken,同时也得到uid、用户资料信息
4 同步用户信息并登录

OAuth2.0 采用Authorization code方式将更为可靠、安全。
更多信息可参考人人网开放平台wiki:
http://wiki.dev.renren.com/wiki/Authentication#.E6.9C.8D.E5.8A.A1.E7.AB.AF.E6.8E.88.E6.9D.83

案例实战

本地开发环境准备

修改hosts文件将dev.foo.com映射到127.0.0.1;
本地服务器以80端口启动, windows下可能会出现80端口被系统进程占用的情况,解决方法可参考 /article/5287127.html
本地服务器启动后,以dev.foo.com的域名进行访问,在登录授权时可通过域名验证这一步

前端登录跳转页面

<html>
<head>
<title>人人网登录跳转</title>
<script src="http://lib.sinaapp.com/js/jquery/1.7.2/jquery.min.js" type="text/javascript"></script>

<script type="text/javascript">
//应用的APIKEY
var apiKey = "3ce9cb1e264f4e93b1f38807be66e629";
//成功授权后的回调地址
var redirectUrl = "@@{openapi.Renrens.callback()}";

var authorizeUrl = "https://graph.renren.com/oauth/authorize?"
var queryParams = ['client_id=' + apiKey,'redirect_uri=' + redirectUrl,'response_type=code'];
var url = authorizeUrl + queryParams.join('&');

//打开授权登录页面
window.location.href= url;
</script>
</head>

<body>
</body>
</html>


功能描述
页面在打开时直接跳转到人人网登录授权页面,此后授权成功后将回调至redirect_uri

server端处理回调,同步信息

授权回调的页面处理

/**
* 授权回调
*
* @param code
* @param error
*/
public static void callback(String code) {
if (!StringUtils.isEmpty(code)) {
error("授权失败");
}

// 根据code换取accesstoken,包括用户信息
// ...

String callbackUrl = RouteContext.getUrl("openapi.Renrens.callback", Collections.EMPTY_MAP,
true);
RenrenToken token = RenApi.getTokenInfo(code, callbackUrl);
if (token == null) {
error("授权失败:无法获取连接系统");
}
render(code, token);
}


数据对象

RenrenToken类

/**
* 返回token数据对象
*
* <pre>
*      * {
*   "token_type":"bearer",
*   "expires_in":2595096,
*   "refresh_token":"127021|0.KAS3b8doSitHk6RLDtitb2VY8PjktTRA.229819774.1376381303243",
*   "user":{
*     "id":229819700,
*     "name":"二小姐",
*     "avatar":[
*         {   "type":"avatar",
*         "url":"http://hdn.xnimg.cn/photos/hdn121/20130805/2055/h_head_KFTQ_d536000000d0111b.jpg"
*         },
*         {   "type":"tiny",
*         "url":"http://hdn.xnimg.cn/photos/hdn221/20130805/2055/tiny_jYQe_ec4300051e7a113f.jpg"
*         },
*         {   "type":"main",
*         "url":"http://hdn.xnimg.cn/photos/hdn121/20130805/2055/h_main_ksPJ_d536000000d0111b.jpg"},
*         {   "type":"large",
*         "url":"http://hdn.xnimg.cn/photos/hdn121/20130805/2055/h_large_yxZz_d536000000d0111b.jpg"
*         }
*     ]
*   },
*   "access_token":"127066|6.08718aa138db0578dda3250f33bads6e.2592000.1378976400-229819774"
*   "scope":"read_user_feed read_user_album",
* </pre>
*
* @author littleatp
* @createDate 2015年4月14日
*
*/
public class RenrenToken {

public String token_type;
public int expires_in;
public String refresh_token;
public String access_token;
public String scope;

public RenrenUser user;
}


RenrenUser类

/**
* 人人网用户信息
*
* <pre>
* "user":{
*     "id":229819700,
*     "name":"二小姐",
*     "avatar":[
*         {   "type":"avatar",
*         "url":"http://hdn.xnimg.cn/photos/hdn121/20130805/2055/h_head_KFTQ_d536000000d0111b.jpg"
*         },
*         {   "type":"tiny",
*         "url":"http://hdn.xnimg.cn/photos/hdn221/20130805/2055/tiny_jYQe_ec4300051e7a113f.jpg"
*         },
*         {   "type":"main",
*         "url":"http://hdn.xnimg.cn/photos/hdn121/20130805/2055/h_main_ksPJ_d536000000d0111b.jpg"},
*         {   "type":"large",
*         "url":"http://hdn.xnimg.cn/photos/hdn121/20130805/2055/h_large_yxZz_d536000000d0111b.jpg"
*         }
*     ]
*   },
* </pre>
*
* @author littleatp
* @createDate 2015年4月14日
*
*/
public class RenrenUser {

public long id;
public String name;
public List<RenrenAvatar> avatar;

public String getAvatarUrl() {
if (avatar == null || avatar.isEmpty()) {
return "";
}
return avatar.get(0).url;
}

public static class RenrenAvatar {
public String type;
public String url;
}
}


RenApi功能实现

/**
* 人人网API
*
* <pre>
* 登录流程:
*
* 1 前端跳转人人网授权(code方式)
* 2 回调获得authorize code
* 3 通过code换取access_token
* 4 获得token及用户信息
*
* 参考文档:
* http://wiki.dev.renren.com/wiki/Authentication * </pre>
*
*
* @author littleatp
* @createDate 2015年4月10日
*
*/
public class RenApi {

public static String apiKey = "xxx";
public static String secretKey = "xxx";

public static String baseUrl = "https://graph.renren.com/oauth";

protected static final String URL_GET_TOKEN = baseUrl + "/token?grant_type=authorization_code"
+ "&client_id=%s&client_secret=%s&code=%s&redirect_uri=%s";

protected static final long ACCESS_TIMEOUT = 15;

protected static final String DEF_APP_TOKEN_EXPIRE = "3h";/**
* 获取token信息
*
* <pre>
* http://wiki.dev.renren.com/wiki/Authentication#.E5.AE.A2.E6.88.B7.E7.AB.AF.E6.8E.88.E6.9D.83 * 返回token的同时也附带了用户信息
*
* 调用地址:
* https://graph.renren.com/oauth/token *
* 参数
* grant_type:使用Authorization Code 作为Access Grant时,此值固定为“authorization_code”;
* client_id:在开发者中心注册应用时获得的API Key;
* client_secret:在开发者中心注册应用时获得的Secret Key。Secret Key是应用的保密信息,请不要将其嵌入到服务端以外的代码里;
* redirect_uri:必须与获取Authorization Code时传递的“redirect_uri”保持一致;
* code:上述过程中获得的Authorization Code。
*
* 返回结果如下:
* {
*   "token_type":"bearer",
*   "expires_in":2595096,
*   "refresh_token":"127021|0.KAS3b8doSitHk6RLDtitb2VY8PjktTRA.229819774.1376381303243",
*   "user":{
*     "id":229819700,
*     "name":"二小姐",
*     "avatar":[
*         {   "type":"avatar",
*         "url":"http://hdn.xnimg.cn/photos/hdn121/20130805/2055/h_head_KFTQ_d536000000d0111b.jpg"
*         },
*         {   "type":"tiny",
*         "url":"http://hdn.xnimg.cn/photos/hdn221/20130805/2055/tiny_jYQe_ec4300051e7a113f.jpg"
*         },
*         {   "type":"main",
*         "url":"http://hdn.xnimg.cn/photos/hdn121/20130805/2055/h_main_ksPJ_d536000000d0111b.jpg"},
*         {   "type":"large",
*         "url":"http://hdn.xnimg.cn/photos/hdn121/20130805/2055/h_large_yxZz_d536000000d0111b.jpg"
*         }
*     ]
*   },
*   "access_token":"127066|6.08718aa138db0578dda3250f33bads6e.2592000.1378976400-229819774"
*   "scope":"read_user_feed read_user_album",
* }
*
* 错误返回:
* {
*   "error": "invalid_grant",
*   "error_code": 20204
*   "error_description": "Invalid authorization code: 9OCQp3IzRcwtSRPKOEUKiRRsz9SUNgdE"
* }
* http://wiki.dev.renren.com/wiki/%E9%94%99%E8%AF%AF%E5%93%8D%E5%BA%94 * </pre>
*
* @param accessToken
* @return
*/
public static RenrenToken getTokenInfo(String code, String callbackUrl) {
if (StringUtils.isEmpty(code)) {
return null;
}

String url = String.format(URL_GET_TOKEN, apiKey, secretKey, code, callbackUrl);

String resultString = DefaultHttp.get(url, ACCESS_TIMEOUT, GlobalConstants.UTF_8);

Logger.debug("[sso-renren]get token. use url '%s'", url);

RenrenToken token = JsonUtil.fromJson(resultString, RenrenToken.class);
if (token == null || StringUtils.isEmpty(token.access_token)) {
Logger.debug("[sso-renren]get token failed, with result of '%s'", resultString);
return null;
}

Logger.debug("[sso-renren]get token success, with result of '%s'", resultString);
return token;
}
}


关于CSRF

跨站攻击问题CSRF
什么是CSRF? 可参考下/article/4901241.html

场景
A网站接入了人人网开放平台,但apikey和secretkey通过页面泄露了出去;
B网站根据同样的apikey和secretkey仿造authorize请求,获得authorization code;
B网站直接跳转到A网站的callback页面;
A网站按授权流程获得用户信息并登录;

这样B网站便成功实现了仿造请求登录A网站的功能;

解决方法
在向平台请求授权(authorize)时可带上一个state参数,建议该参数由A网站动态生成。
平台调用callback时会回传该state参数,此时A网站需要在callback处理时对该参数进行验证
于是B网站无法伪造state参数,也就无法伪造登录场景了。

常见问题

网页跳转提示
redirect_uri_mismatch
通常是应用配置中的根域名与当前开发服务器访问地址不一致导致

授权返回错误

检查返回的error代码
http://wiki.dev.renren.com/wiki/%E9%94%99%E8%AF%AF%E5%93%8D%E5%BA%94

SDK的使用
对于强依赖于人人网平台的应用,建议使用下平台的SDK,如JavaSDK;其封装了大量api访问及出错处理细节,可提高开发效率。
参考:http://wiki.dev.renren.com/wiki/V2/sdk/java_sdk
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: