第三方登录_新浪微博
1. 在微博开发平台,注册开发者用户并填写相关信息和创建应用信息,等待审核通过,然后进行下一步编码操作http://open.weibo.com/,具体操作可在微博开发平台观看相关操作文档(注意:创建的应用需要审核通过,回调地址才能生效)
2. 微博开发平台》文档》资源下载,选中自己需要的SDK,下载SDK
解压后
3. 其中weibo4j-oauth2 是一个Java工程项目,并没有像QQ那样整合的第三方jar包,所以需要自己手动集成,或者自己将该工程导入eclipse工具,然后打包成jar包(有些可能是自己现在用不到的,当时个人认为还是都打包上吧,说不定哪天就要用到了),也可以按照微博文档里说的方式手工集成(把依赖的源码,文件等都手工加入到工程里,手工的更改编译配置等),又或者可以使用 CocoaPods 工具自动集成。https://segmentfault.com/a/1190000004621065
将config.properties配置文件复制到自己工程对应的配置文件下,并将client_ID、client_SERCRET、redirect_URI 改成在新浪开发平台注册开发者信息是平台返回的对应值,redirect_URI为回调函数的路径,
但是有个地方需要注意的,SDK中的源码中该配置文件是默认在src下的,下图是SDK源码中解析改配置文件的工具类
如果使用该工具类来解析配置文件,那么路径改成在自己工程中对应的路径,如
4. weibo4j-oauth2目录,其中源码程序在src下,测试程序在example下
5. 集成项目依赖源码后,然后将测试程序需要的代码复制到自己工程controller层对应的xxxController.java 前端控制器中(如下图,前提:相应的配置文件相关信息需要改成自己注册时平台返回的对应值)
有个问题,就是如果直接使用BareBonesBrowserLaunch.openURL(oauth.authorize("code"));来打开链接url的话,会打开默认浏览器(如下图,为BareBonesBrowserLaunch类中运行浏览器并打开url的方法),可以根据自己项目情况自行修改
下图我采用了重定向的方式跳转到微博的授权页
下图是授权成功后的回调方法,因为我这边自己的应用还没有审核通过,所以回调方法的方法体暂时还没有做如何处理(另外新浪还有一个取消授权的回调路径,这个可以直接设置为工程的登录页就可以了,视个人情况而定)
第三方登录_QQ
1、在QQ互联平台https://connect.qq.com/ 注册开发者用户,等待审核通过并创建网站应用,http://blog.csdn.net/pan_y95/article/details/74797429这个链接是个人认为网上说的比较清楚的一个网友,不过有一个问题,在腾讯开放平台注册开发者用户通过后,但是在QQ互联平台上却没有显示审核通过,也创建不了应用,但是在腾讯开放平台上可以。
等创建应用也审核通过后,官方会返回相关信息,这些信息在你接入自己项目的时候配置文件中需要用到。
2、 创建开放者用户和创建应用等准备工作的通过后开始编码工作,下载SDK,QQ互联平台》文档资料》SDK及资源下载》SDK下载
解压后
开始编码工作,在sdk4j_demo中,找到SDK4J.jar包,并引入到自己项目工程中,还有qqconnectconfig.properties这个配置文件
将app_ID\app_KEY\redirect_URI三个参数的值改成自己的,也就是在注册开发者用户和创建应用通过后返回的参数值,其中redirect_URI是授权成功后的回调地址,这个在创建应用的时候配置的。
3、 在这里特意说一下SDK4J.jar这个jar包,因为里面封装了QQ自己需要用到的jar包,可能会跟你自己本地的jar包冲突,如果你项目工程用的maven工具来管理项目引入jar包的还好,我这边是jar包都是放在本的,就出现了冲突的情况,SDK4J.jar包中有commons-code 1.6 ,然后我本地也引入了一个,找了很长时间没有找到解决冲突的方法,因为我本地这边需要用到base64这个加密类,问题就是,这两个jar包的这个类的路径是一样的,而且QQ对该类进行了重写,所以两个base64这个类里的方法是不一样的,后面的解决方法:用好压打开QQ的jar包,然后把QQ里的commons-code1.6jar包给删了,然后项目就没有报错了(建议大家如果有其他的解决方法尽量不要用这种方法,因为我这边项目用不到我就给删除了,所以也没有报错)。
4、 另外该jar包还有一个地方需要注意的,就是关于qqconnectconfig.properties这个配置文件引入的位置,jar是默认放在src文件夹下的,不过一般的项目,配置文件都会统一管理放在conf 下
可以自己用反编译工具来打开jar看一下源码。
5、 下面就是如何跳转授权登录页和回调方法处理的问题了
大家也可以参考一下sdk4j_demo文件中的文件介绍。
获取用户信息在afterloginredirectservlet.java中有介绍,具体情况就看在自己本地工程中如何去处理数据了。
第三方登录_微信
准备工作
网站应用微信登录是基于OAuth2.0协议标准构建的微信OAuth2.0授权登录系统。
在进行微信OAuth2.在进行微信OAuth2.0授权登录接入之前,在微信开放平台注册开发者帐号,并拥有一个已审核通过的网站应用,并获得相应的AppID和AppSecret,申请微信登录且通过审核后,可开始接入流程。https://open.weixin.qq.com
授权流程说明
微信OAuth2.0授权登录让微信用户使用微信身份安全登录第三方应用或网站,在微信用户授权登录已接入微信OAuth2.0的第三方应用后,第三方可以获取到用户的接口调用凭证(access_token),通过access_token可以进行微信开放平台授权关系接口调用,从而可实现获取微信用户基本开放信息和帮助用户实现基础开放功能等。
微信OAuth2.0授权登录目前支持authorization_code模式,适用于拥有server端的应用授权。该模式整体流程为:
1.
第三方发起微信授权登录请求,微信用户允许授权第三方应用后,微信会拉起应用或重定向到第三方网站,并且带上授权临时票据code参数;
2.
通过code参数加上AppID和AppSecret等,通过API换取access_token;
3.
通过access_token进行接口调用,获取用户基本数据资源或帮助用户实现基本操作。
获取access_token时序图:
第一步:请求CODE
第三方使用网站应用授权登录前请注意已获取相应网页授权作用域(scope=snsapi_login),则可以通过在PC端打开以下链接:
https://open.weixin.qq.com/connect/qrconnect?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
若提示“该链接无法访问”,请检查参数是否填写错误,如redirect_uri的域名与审核时填写的授权域名不一致或scope不为snsapi_login。
参数说明
参数 | 是否必须 | 说明 | |
appid | 是 | 应用唯一标识 | |
redirect_uri | 是 | 请使用urlEncode对链接进行处理 | |
response_type | 是 | 填code | |
scope | 是 | 应用授权作用域,拥有多个作用域用逗号(,)分隔,网页应用目前仅填写snsapi_login即 | |
state | 否 | 用于保持请求和回调的状态,授权请求后原样带回给第三方。该参数可用于防止csrf攻击(跨站请求伪造攻击),建议第三方带上该参数,可设置为简单的随机数加session进行校验 |
返回说明
用户允许授权后,将会重定向到redirect_uri的网址上,并且带上code和state参数
redirect_uri?code=CODE&state=STATE
若用户禁止授权,则重定向后不会带上code参数,仅会带上state参数
redirect_uri?state=STATE
请求示例
登录一号店网站应用
https://passport.yhd.com/wechat/login.do
打开后,一号店会生成state参数,跳转到
https://open.weixin.qq.com/connect/qrconnect?appid=wxbdc5610cc59c1631&redirect_uri=https%3A%2F%2Fpassport.yhd.com%2Fwechat%2Fcallback.do&response_type=code&scope=snsapi_login&state=3d6be0a4035d839573b04816624a415e#wechat_redirect
微信用户使用微信扫描二维码并且确认登录后,PC端会跳转到
https://passport.yhd.com/wechat/callback.do?code=CODE&state=3d6be0a4035d839573b04816624a415e
为了满足网站更定制化的需求,我们还提供了第二种获取code的方式,支持网站将微信登录二维码内嵌到自己页面中,用户使用微信扫码授权后通过JS将code返回给网站。
JS微信登录主要用途:网站希望用户在网站内就能完成登录,无需跳转到微信域下登录后再返回,提升微信登录的流畅性与成功率。 网站内嵌二维码微信登录JS实现办法:
步骤1:在页面中先引入如下JS文件(支持https):
http://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js
步骤2:在需要使用微信登录的地方实例以下JS对象:
var obj = new WxLogin({
id:"login_container",
appid: "",
scope: "",
redirect_uri: "",
state: "",
style: "",
href: ""
});
参数说明
参数 | 是否必须 | 说明 |
id | 是 | 第三方页面显示二维码的容器id |
appid | 是 | 应用唯一标识,在微信开放平台提交应用审核通过后获得 |
scope | 是 | 应用授权作用域,拥有多个作用域用逗号(,)分隔,网页应用目前仅填写snsapi_login即可 |
redirect_uri | 是 | 重定向地址,需要进行UrlEncode |
state | 否 | 用于保持请求和回调的状态,授权请求后原样带回给第三方。该参数可用于防止csrf攻击(跨站请求伪造攻击),建议第三方带上该参数,可设置为简单的随机数加session进行校验 |
style | 否 | 提供"black"、"white"可选,默认为黑色文字描述。详见文档底部FAQ |
href | 否 | 自定义样式链接,第三方可根据实际需求覆盖默认样式。详见文档底部FAQ |
第二步:通过code获取access_token
通过code获取access_token
https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
参数说明
参数 | 是否必须 | 说明 |
appid | 是 | 应用唯一标识,在微信开放平台提交应用审核通过后获得 |
secret | 是 | 应用密钥AppSecret,在微信开放平台提交应用审核通过后获得 |
code | 是 | 填写第一步获取的code参数 |
grant_type | 是 | 填authorization_code |
返回说明
正确的返回:
{
"access_token":"ACCESS_TOKEN",
"expires_in":7200,
"refresh_token":"REFRESH_TOKEN",
"openid":"OPENID",
"scope":"SCOPE",
"unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
}
参数说明
参数 | 说明 |
access_token | 接口调用凭证 |
expires_in | access_token接口调用凭证超时时间,单位(秒) |
refresh_token | 用户刷新access_token |
openid | 授权用户唯一标识 |
scope | 用户授权的作用域,使用逗号(,)分隔 |
unionid | 当且仅当该网站应用已获得该用户的userinfo授权时,才会出现该字段。 |
错误返回样例:
{"errcode":40029,"errmsg":"invalid code"}
刷新access_token有效期
access_token是调用授权关系接口的调用凭证,由于access_token有效期(目前为2个小时)较短,当access_token超时后,可以使用refresh_token进行刷新,access_token刷新结果有两种:
1. 若access_token已超时,那么进行refresh_token会获取一个新的access_token,新的超时时间;
2.
若access_token未超时,那么进行refresh_token不会改变access_token,但超时时间会刷新,相当于续期access_token。
refresh_token拥有较长的有效期(30天),当refresh_token失效的后,需要用户重新授权。
请求方法
获取第一步的code后,请求以下链接进行refresh_token:
https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=APPID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN
参数说明
参数 | 是否必须 | 说明 |
appid | 是 | 应用唯一标识 |
grant_type | 是 | 填refresh_token |
refresh_token | 是 | 填写通过access_token获取到的refresh_token参数 |
返回说明
正确的返回:
{
"access_token":"ACCESS_TOKEN",
"expires_in":7200,
"refresh_token":"REFRESH_TOKEN",
"openid":"OPENID",
"scope":"SCOPE"
}
参数说明
参数 | 说明 |
access_token | 接口调用凭证 |
expires_in | access_token接口调用凭证超时时间,单位(秒) |
refresh_token | 用户刷新access_token |
openid | 授权用户唯一标识 |
scope | 用户授权的作用域,使用逗号(,)分隔 |
错误返回样例:
{"errcode":40030,"errmsg":"invalid refresh_token"}
注意:
1、Appsecret 是应用接口使用密钥,泄漏后将可能导致应用数据泄漏、应用的用户数据泄漏等高风险后果;存储在客户端,极有可能被恶意窃取(如反编译获取Appsecret);
2
、access_token 为用户授权第三方应用发起接口调用的凭证(相当于用户登录态),存储在客户端,可能出现恶意获取access_token 后导致的用户数据泄漏、用户微信相关接口功能被恶意发起等行为;
3
、refresh_token 为用户授权第三方应用的长效凭证,仅用于刷新access_token,但泄漏后相当于access_token 泄漏,风险同上。
建议将secret、用户数据(如access_token)放在App云端服务器,由云端中转接口调用请求。
第三步:通过access_token调用接口
获取access_token后,进行接口调用,有以下前提:
1. access_token有效且未超时;
2.
微信用户已授权给第三方应用帐号相应接口作用域(scope)。
对于接口作用域(scope),能调用的接口有以下:
授权作用域(scope) | 接口 | 接口说明 |
snsapi_base | /sns/oauth2/access_token | 通过code换取access_token、refresh_token和已授权scope |
snsapi_base | /sns/oauth2/refresh_token | 刷新或续期access_token使用 |
snsapi_base | /sns/auth | 检查access_token有效性 |
snsapi_userinfo | /sns/userinfo | 获取用户个人信息 |
其中snsapi_base属于基础接口,若应用已拥有其它scope权限,则默认拥有snsapi_base的权限。使用snsapi_base可以让移动端网页授权绕过跳转授权登录页请求用户授权的动作,直接跳转第三方网页带上授权临时票据(code),但会使得用户已授权作用域(scope)仅为snsapi_base,从而导致无法获取到需要用户授权才允许获得的数据和基础功能。
接口调用方法可查阅《微信授权关系接口调用指南》
F.A.Q
1. 什么是授权临时票据(code)?
答:第三方通过code进行获取access_token的时候需要用到,code的超时时间为10分钟,一个code只能成功换取一次access_token即失效。code的临时性和一次保障了微信授权登录的安全性。第三方可通过使用https和state参数,进一步加强自身授权登录的安全性。
2. 什么是授权作用域(scope)?
答:授权作用域(scope)代表用户授权给第三方的接口权限,第三方应用需要向微信开放平台申请使用相应scope的权限后,使用文档所述方式让用户进行授权,经过用户授权,获取到相应access_token后方可对接口进行调用。
3. 网站内嵌二维码微信登录JS代码中style字段作用?
答:第三方页面颜色风格可能为浅色调或者深色调,若第三方页面为浅色背景,style字段应提供"black"值(或者不提供,black为默认值),则对应的微信登录文字样式为黑色。
4.网站内嵌二维码微信登录JS代码中href字段作用?
答:如果第三方觉得微信团队提供的默认样式与自己的页面样式不匹配,可以自己提供样式文件来覆盖默认样式。举个例子,如第三方觉得默认二维码过大,可以提供相关css样式文件,并把链接地址填入href字段
.impowerBox .qrcode {width: 200px;}
.impowerBox .title {display: none;}
.impowerBox .info {width: 200px;}
.status_icon {display: none}
.impowerBox .status {text-align: center;}
编码
1、下面简单说明一下接入微信第三方登录的编码,微信不想微博,QQ那样有jar包或者示例工程demo,不过思路都是一样的,就是一步一步来去请求不同的接口获取需要的数据。这里我自己同样按照QQ微博的方式,将一些key一些数据配置到配置文件中。
2、编写读取配置文件的工具类,确实这个跟QQ微博的都一样,可以参考着来写,当然也可以把三种方式整合到一起
3、控制器 跳转授权页(也可以用js 的方式打开)及授权成功回调地址做本地数据处理
4、总得来说,就是按照微信官方的一个授权流程来走,代码实现自己来写
package com.deelon.thirdLogin.service;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.deelon.core.service.BaseService;
import com.deelon.thirdLogin.pojo.UserInfoData;
import com.deelon.thirdLogin.util.WeixinUtil;
import weibo4j.org.json.JSONException;
import weibo4j.org.json.JSONObject;
public class WeiXinAuthService {
private static final Logger logger = LoggerFactory
.getLogger(WeiXinAuthService.class);
public static UserInfoData checkLogin(String code) throws Exception {
// 获取授权 access_token
String loginUrl = WeixinUtil.getAccessTokenUrlByCode(code);
String loginRet = WeiXinAuthService.get(loginUrl);
JSONObject grantObj;
UserInfoData userInfo = new UserInfoData();
try {
grantObj = new JSONObject(loginRet);
String errcode = grantObj.optString("errcode");
if (!StringUtils.isEmpty(errcode)) {
logger.error("login weixin error" + loginRet);
return null;
}
String openId = grantObj.optString("openid");
if (StringUtils.isEmpty(openId)) {
logger.error("login weixin getOpenId error" + loginRet);
return null;
}
String accessToken = grantObj.optString("access_token");
String expiresIn = grantObj.optString("expires_in");
String refreshToken = grantObj.optString("refresh_token");
String scope = grantObj.optString("scope");
// 获取用户信息
String userUrl = WeixinUtil.getUserInfoUrl(accessToken, openId);
String userRet = WeiXinAuthService.get(userUrl);
JSONObject userObj;
userObj = new JSONObject(userRet);
userInfo.setOpenId(openId);
userInfo.setAuthToken(accessToken);
userInfo.setAuthRefreshToken(refreshToken);
userInfo.setScope(scope);
userInfo.setExpiresIn(Integer.valueOf(expiresIn));
String nickname = userObj.optString("nickname");
String sex = userObj.optString("sex");
String userImg = userObj.optString("headimgurl");
String unionid = userObj.optString("unionid");
userInfo.setNickName(nickname);
userInfo.setUserImg(userImg);
userInfo.setGender(sex);
userInfo.setUnionId(unionid);
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return userInfo;
}
public static String get(String url) {
String body = null;
try (CloseableHttpClient httpClient = HttpClientBuilder.create()
.build()) {
logger.info("create httppost:" + url);
HttpGet get = new HttpGet(url);
get.addHeader("Accept-Charset", "utf-8");
HttpResponse response = sendRequest(httpClient, get);
body = parseResponse(response);
} catch (IOException e) {
logger.error("send post request failed: {}", e.getMessage());
}
return body;
}
private static String paramsToString(Map<String, String> params) {
StringBuilder sb = new StringBuilder();
try {
for (String key : params.keySet()) {
sb.append(String.format("&%s=%s", key, URLEncoder.encode(
params.get(key), StandardCharsets.UTF_8.toString())));
}
} catch (UnsupportedEncodingException e) {
logger.warn("{}: encode url parameters failed", e.getMessage());
}
return sb.length() > 0 ? "?".concat(sb.substring(1)) : "";
}
private static HttpResponse sendRequest(CloseableHttpClient httpclient,
HttpUriRequest httpost) throws ClientProtocolException, IOException {
HttpResponse response = null;
response = httpclient.execute(httpost);
return response;
}
private static String parseResponse(HttpResponse response) {
logger.info("get response from http server..");
HttpEntity entity = response.getEntity();
logger.info("response status: " + response.getStatusLine());
Charset charset = ContentType.getOrDefault(entity).getCharset();
if (charset != null) {
logger.info(charset.name());
}
String body = null;
try {
body = EntityUtils.toString(entity, "utf-8");
logger.info("body " + body);
} catch (IOException e) {
logger.warn("{}: cannot parse the entity", e.getMessage());
}
return body;
}
}
第一次做第三方登录的功能,就写了一下文档,不好的地方还需要大家谅解,个人建议如果是第一次写,还是可以稍微花点时间看一下相关开发平台中的文档介绍,因为在论坛博客上看到的都是别人自己开发的心得,跟自己的情况并没有完全一样。