• 开放平台-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访问及出错处理细节,可提高开发效率。
        
  • 相关阅读:
    单元测试
    软件工程
    使用工具进行单元测试
    关于软件工程的理解
    使用Junit等工具进行单元测试
    目前对软件工程所存在的问题
    二人组-----五子棋
    使用Junit等工具进行单元测试过程记录
    对软件工程的理解以及存在的问题
    软件设计文档及数据流向图
  • 原文地址:https://www.cnblogs.com/littleatp/p/4430297.html
Copyright © 2020-2023  润新知