開放平台-web實現人人網第三方登錄


應用場景

    web應用通過人人網登錄授權實現第三方登錄。
 

操作步驟

    1  注冊成為人人網開放平台開發者
 
    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:
 

案例實戰

    本地開發環境准備

        修改hosts文件將dev.foo.com映射到127.0.0.1;
        本地服務器以80端口啟動, windows下可能會出現80端口被系統進程占用的情況,解決方法可參考 http://www.cnblogs.com/littleatp/p/4414578.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
 
    場景
    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代碼
 
SDK的使用
        對於強依賴於人人網平台的應用,建議使用下平台的SDK,如JavaSDK;其封裝了大量api訪問及出錯處理細節,可提高開發效率。
    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM