一、什么是OAuth
OAuth: OAuth(开放授权)是一个开放标准,允许用户授权第三方网站访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方网站或分享他们数据的所有内容。
QQ登录OAuth2.0:对于用户相关的OpenAPI(例如获取用户信息,动态同步,照片,日志,分享等),为了保护用户数据的安全和隐私,第三方网站访问用户数据前都需要显式的向用户征求授权。
二、接入方式
网站可通过以下两种方式接入:
(1)使用QQ互联提供的SDK包,用户体验统一,只需要修改少量代码,不需要理解验证授权流程,需要快速接入QQ登录的网站和移动应用可选用此方法。
QQ互联提供JavaScript,PHP,Java等多个版本的SDK,详见:SDK下载
QQ登录JS SDK详见:JS SDK使用说明
(2)根据QQ登录OAuth2.0协议,自主开发,此方法自定义程度较高,需要与现有系统进行整合的网站和移动应用可选用此方法。(我们这里用第二种)
三、接入流程
QQ登录OAuth2.0总体处理流程如下:
Step1:申请接入,获取appid和apikey;
Step2:开发应用,并设置协作者帐号进行测试联调;
Step3:放置QQ登录按钮;
Step4:通过用户登录验证和授权,获取Access Token;
Step5:通过Access Token获取用户的OpenID;
Step6:调用OpenAPI,来请求访问或修改用户授权的资源。
四、核心代码
qqinfo.json中放的是app_id, app_secret和跳转地址,下面这段代码需要jackson和httpclient包,注意引入。
//页面返回信息 String nickname = "Unkown"; //json处理工具 ObjectMapper objectMapper = new ObjectMapper(); //请求执行工具 CloseableHttpClient httpclient = HttpClients.createDefault(); //请求发送信息 HttpPost httppost; HttpEntity reqEntity; CloseableHttpResponse responseEntity; //请求返回值处理需要的临时变量 BufferedReader reader; StringBuilder tempSB; String tempLine; //处理结果 Map<String, String> map; try { Map<String, String> param = objectMapper.readValue(new File(request.getServletContext().getRealPath("/") + "webfile/qqinfo.json"), Map.class); String codeStr = request.getParameter("code"); //如果code或json信息为空直接退出 if (codeStr != null && param != null) { //第一次请求 httppost = new HttpPost("https://graph.qq.com/oauth2.0/token"); reqEntity = MultipartEntityBuilder.create().addPart("code", new StringBody(codeStr, ContentType.TEXT_PLAIN)).addPart("client_id", new StringBody(param.get("qq_id"), ContentType.TEXT_PLAIN)).addPart("client_secret", new StringBody(param.get("qq_secret"), ContentType.TEXT_PLAIN)).addPart("redirect_uri", new StringBody(param.get("qq_url"), ContentType.TEXT_PLAIN)).addPart("grant_type", new StringBody("authorization_code", ContentType.TEXT_PLAIN)).build(); httppost.setEntity(reqEntity); responseEntity = httpclient.execute(httppost); //从第一次请求的返回值中拿到token String accessToken = null; tempSB = new StringBuilder(); reader = new BufferedReader(new InputStreamReader(responseEntity.getEntity().getContent())); while ((tempLine = reader.readLine()) != null) { tempSB.append(tempLine); } String[] results = tempSB.toString().split("&"); for (String result : results) { String[] tmp = result.split("="); if (tmp.length == 2 && "access_token".equals(tmp[0])) { accessToken = tmp[1]; } } //如果token拿不到直接退出 if (accessToken != null) { //第二次请求 httppost = new HttpPost("https://graph.qq.com/oauth2.0/me"); reqEntity = MultipartEntityBuilder.create().addPart("access_token", new StringBody(accessToken, ContentType.TEXT_PLAIN)).build(); httppost.setEntity(reqEntity); responseEntity = httpclient.execute(httppost); //从第二次请求的返回值中拿到openid tempSB = new StringBuilder(); reader = new BufferedReader(new InputStreamReader(responseEntity.getEntity().getContent())); while ((tempLine = reader.readLine()) != null) { tempSB.append(tempLine); } map = objectMapper.readValue(tempSB.toString().substring(tempSB.toString().indexOf("{"), tempSB.toString().indexOf("}") + 1), Map.class); //如果openid拿不到则直接退出 if (map.get("client_id") != null && map.get("openid") != null) { //第三次请求 httppost = new HttpPost("https://graph.qq.com/user/get_user_info"); reqEntity = MultipartEntityBuilder.create().addPart("access_token", new StringBody(accessToken, ContentType.TEXT_PLAIN)).addPart("openid", new StringBody(map.get("openid"), ContentType.TEXT_PLAIN)).addPart("oauth_consumer_key", new StringBody(map.get("client_id"), ContentType.TEXT_PLAIN)).build(); httppost.setEntity(reqEntity); responseEntity = httpclient.execute(httppost); //从第三次请求的返回值中拿到用户信息(nickname) tempSB = new StringBuilder(); reader = new BufferedReader(new InputStreamReader(responseEntity.getEntity().getContent())); while ((tempLine = reader.readLine()) != null) { tempSB.append(tempLine); } map = objectMapper.readValue(tempSB.toString(), Map.class); nickname = map.get("nickname"); } } } } catch (Exception e) { e.printStackTrace(); }