JustAuth发布1.10.0版本,集成华为和企业微信登录,更加灵活的state缓存
Log
工具类注册并登录华为开发者联盟官网:http://developer.huawei.com/consumer/cn
点击右上角“管理中心”,如果你是第一次注册登录华为开发者平台,那么需要先进行账号认证
请自行操作。
注:如果你是新用户,那么可能注册完成后就会要求你认证,页面如下:
我选择的“支付宝扫码认证”,这种方式确实十分快,几分钟就完事了(PS:“身份证人工审核认证 ”这种认证方式,本人没用,所以不知道具体的认证时间)。
认证完成后,重新进入管理中心:https://developer.huawei.com/consumer/cn/console#/serviceCards/
这个“开发者联盟使用向导”可以视个人情况选择关闭,接下来就是重点了,如果不想走弯路,一定要仔细按照教程步骤来做,当时我就折腾了老大会才搞明白。
首先,我一开始创建应用的时候是按照官网教程:https://developer.huawei.com/consumer/cn/devservice/doc/31107操作的,官网教程中说的是创建“免安装/H5应用”,
但是我在实际操作的时候,并没有看到这个选项
当时我还以为是华为的网站更新而文档没有更新,所以专门去提了工单
后来才得知是“开通了白名单才能申请“免安装/H5应用”,个人开发者一般是无法申请的”。
那么,难道个人开发者就没法用了吗?当然不是,要不也不会有这篇教程了…
当时我根据网站上列出的这几种应用类型,挨个点了一遍,大致浏览了以下各类型应用需要的参数,也不知道是因为心有灵犀还是因为别的原因,当时自己选来选去,最终选了一个“服务器应用”,奔着试试看的想法,大不了换种类型重新试。
选定应用类型后,根据提示,需要先创建产品
点击“创建产品”按钮后会弹出窗口,按照提示填入相应内容即可:
创建完产品后,会弹出提示框告知产品的appid和secret,记得先保存起来。
最终创建完成后的应用内容如下
参考开放平台鉴权:https://developer.huawei.com/consumer/cn/devservice/doc/30101文档说明,获取授权链接,然后拿授权code换取accessToken。
根据文档说明,我们拼装一下授权地址:
https://oauth-login.cloud.huawei.com/oauth2/v2/authorize?response_type=code&client_id=[创建产品时给的appid]&redirect_uri=[上面填的回调地址]&access_type=offline&scope=https%3A%2F%2Fwww.huawei.com%2Fauth%2Faccount%2Fbase.profile
浏览器直接访问这个地址,就会跳转到华为的授权登录页面
点击“授权并登录”后,就会跳转到我们配置的“回调地址”中,并附带一个authorization_code
参数(注,因为我们生成授权链接的时候没有带state参数,所以回调中也就没有这个参数)。
根据文档说明,我们编写以下代码获取token:
String code = "上一步获取的authorization_code";
HttpResponse response = HttpRequest.post("https://oauth-login.cloud.huawei.com/oauth2/v2/token")
.form("grant_type", "authorization_code")
.form("code", code)
.form("client_id", "[创建产品时给的appid]")
.form("client_secret", "[创建产品时给的secret]")
.form("redirect_uri", "[上面填的回调地址]")
.execute();
System.out.println(response.body());
请求成功后会返回以下内容
{
"access_token": "accessToken",
"expires_in": 3600,
"refresh_token": "refreshToken",
"scope": "https:\/\/www.huawei.com\/auth\/account\/base.profile",
"token_type": "Bearer"
}
到这一步又是一个麻烦,因为开放平台鉴权:https://developer.huawei.com/consumer/cn/devservice/doc/30101文档中只有获取授权地址和获取accessToken以及刷新token的api,但是并没有获取用户信息的接口说明,我翻遍了华为的api文档,也没有找到相关的信息(可能是我比较菜,没找到藏哪儿了)。当然, 这是难不倒我们的。
有朋友会问,官网都没提供文档说明,你怎么能搞出来? (蜜汁微笑~~( ̄▽ ̄)")
虽然官网没有这方便的api,但是我在他官网发现了另一份SDK:华为开放平台JavaScript SDK:https://developer.huawei.com/consumer/cn/devservice/doc/30104,既然js能实现,那么我们可以从这儿入手,把sdk下载下来,研究一下他的jssdk怎么实现的,然后想办法翻译成Java语言。
SDK下载来后直接解压,是一个web工程,目录结构如下(已隐去非必要的文件):
.
|-- WebContent
| |-- WEB-INF
| | | `-- lib
| | | `-- fastjson-1.1.32.jar
| | `-- web.xml
| |-- handleTransit.html
| |-- index.html
| `-- js
| |-- NspClient.js
| `-- jquery-1.10.2.js
`-- src
`-- com
`-- huawei
`-- demo
|-- GetAccessToken.java
`-- HttpUtil.java
其中的java代码,我们可以直接忽略,没什么用。我们主要关注这三个文件:
authorization_code
获取token的,并把token存到cookie中,然后跳转刷新页面。在源码中,我注意到在index.html
中有一段代码:
var option = {
respontype : "code",
appid:"",
handle_login_uri:"http://ip:port/NSPClient/handleTransit.html"
}
var nspp = new NspClient(option);
nspp.checkLogin(function() {
// 调用接口
nspp.api('OpenUP.User.getInfo', {
callback : function(data) {
if (!!data) {
alert("userName:" + data.userName + ",userID:" + data.userID)
}
},
onError : function(err) {
if (!!err) {
alert("error");
}
}
});
});
根据代码字面意思,可以猜到,这个就是获取用户信息的接口,不过这儿有一点疑问:正常的oauth授权流程中,获取用户信息,必然会用到accesstoken,但是这里面却没有显示引用token的地方,考虑handleTransit.html
文件中有把token存cookie的操作,所以在这儿看来引用token的操作应该是被封装到NspClient.js
中。下一步我们就以nspp.api('OpenUP.User.getInfo', {})
为入口,阅读NspClient.js
的源码实现。
nspp.api()
源码如下(by zhyd xx
开头的是我添加的注释,方便各位理解):
/**
* api请求(向后台发请求的公共方法)
*
* @param name =
* api名称
* @param Settings={
* param:{json格式参数}, dataType:
* ’POST/GET/JSONP/XPOST(同POST)’,callback:回调方法 }
*
* @desc JSONP方式采用JS方式调用网关,其它方式通过JSON方式
*/
NspClient.prototype.api = function (name, settings, apiUrl) {
var self = this;
// by zhyd 通过阅读下面的代码,发现这才是真正的请求api的地址,源码分析见下面【关于`url`】
var url = this.url;
var API = {}, paramData;
paramData = settings.param;
// 调用测试桩时使用
// by zhyd 在`index.html`页面中,调用`nspp.api()`时,只传入了name和settings,并没有传入apiUrl,所以这个if代码块并不会执行,里面的内容我们直接忽略。
if (apiUrl) {
if (window.console) {
window.console.warn('正在使用测试桩,接口名为:' + name);
}
url = apiUrl;
if (settings.dataType === 'XPOST') {
settings.dataType = 'JSONP';
}
}
// by zhyd 入参settings中并没有传入param,所以这个for循环代码块也不会执行,里面的内容我们直接忽略。
for (var i in paramData) {
if (typeof paramData[i] === 'object') {
paramData[i] = NSPHelper.jsonToString(paramData[i]);
}
API[i] = paramData[i];
}
// if (!apiUrl && name && name.indexOf("OpenOther.System") < 0) {
// name = "OpenOther.Delegate." + name.replace(new RegExp("\\.", "g"), "_");
// }
// by zhyd 关键!指定调用的api名称
API.nsp_svc = name;
var type = 'JSONP';
// by zhyd 入参未传入dataType,所以type一定等于'JSONP',下面的switch代码块,我们直接看case 'JSONP'下的即可。
if (!!settings.dataType) {
type = settings.dataType;
}
switch (type) {
case 'JSONP':
// 通过JSONP方式请求api【关于`getJSONP`】
this.getJSONP(url, API, settings.callback, settings.onError);
break;
case 'XPOST':
NspClient.xdr.send(url, "POST", API, settings.callback,
settings.error);
break;
case 'POST':
case 'GET':
case 'post':
case 'get':
NspClient.xdr.send(url, settings.dataType, API,
settings.callback, settings.error);
break;
default:
this.getJSONP(url, API, settings.callback, settings.onError);
}
};
ok,上面api的代码已经大致理解了,关键部分就那几处,分开解释。
url
这个url是在创建NspClient
时指定的,源码如下:
/**
* NspClient Class
*
* @param {obj}
* option为类似如下配置参数: { appid: 开发平台在OpenGw的对应应用id ,
* access_token:xxx, debug:xxx }
*/
var NspClient = function (option) {
this.handleLoginUri = option && option.handle_login_uri ? option.handle_login_uri
: nspClientCfg.handle_login_uri;
// 登录成功后,保存登录状态页面
// by zhyd 这儿指定
this.url = nspClientCfg.opengw_api_url;// OpenGw API接口地址
this.loginUrl = nspClientCfg.login_url;// 登录页面地址
this.urlGetToken = nspClientCfg.get_token_url;// 应用级API前获取token的接口地址
this.authApi = 'open.auth.check';
this.appid = option && option.appid ? option.appid
: nspClientCfg.app_id;// 开发者联盟的appid
this.secret = option && option.secret ? option.secret
: nspClientCfg.serect;
this.access_token = option && option.access_token ? option.access_token
: '';
this.respontype = option && option.respontype ? option.respontype
: nspClientCfg.respontype;
};
我们再看nspClientCfg.opengw_api_url
的内容
var gwAccess = {
opengw_api_url: "https://api.vmall.com/rest.php",// OpenGw API接口地址
opengw_cross_url: "https://api.vmall.com/nspcross.html",
login_url: "https://login.vmall.com/oauth2/authorize",// 登录页面地址
get_token_url: "https://login.vmall.com/oauth2/token",// 应用级API前获取token的接口地址
xdr_proxy_url: "/",
respontype: "token"
};
到这儿我们就明白了,实际的获取用户的api接口是https://api.vmall.com/rest.php
。
getJSONP
源码如下
/**
* 组装数据,发送jsonp请求
*
* @param {string}
* url 请求地址
* @param {object}
* data 请求数据
* @param {function}
* callback 成功回调函数
* @return undefined
*/
NspClient.prototype.getJSONP = function (url, data, callback, onError) {
var self = this;
// by zhyd api请求参数,表示当前时间戳
data.nsp_ts = new Date().getTime();
// by zhyd 在这儿传入的token
if (!data.access_token) {
data.access_token = encodeURIComponent(this.access_token ? this.access_token
: NSPHelper.getCookie('access_token'));
}
// by zhyd 下面这两个也是api请求参数,fmt表示是js请求,cb代表JSONP回调方法的名称
data.nsp_fmt = 'JS';
data.nsp_cb = '_jqjsp';
/*
* 使用jquery的jsonp可以简化代码,减少代码量,但无法获得nsp_status,暂时搁浅 $.ajax({ url :
* url, data : data, dataType : "jsonp", jsonp : 'nsp_cb', success :
* function(data, textStatus, jqXHR) { }, error : function(e) { if
* (typeof onError == 'function') { onError(e); } else { if
* (window.console) { console.warn("request failed!"); } } } });
*/
ajax({
url: url,
data: data,
dataType: "jsonp",
// jsonpCallback:"_jqjsp",
timeout: 30000,
callbackParameter: "nsp_cb",
success: function (data, nsp_status) {
// by zhyd 这儿的回调处理我们直接忽略
},
error: function (e) {
// by zhyd 这儿的异常处理我们直接忽略
}
});
};
ok,到这一步,我们算是全部理解了华为javascript sdk的获取用户信息的api操作流程,也正如上面猜的, accessToken就是在js内部做的处理。
那么接下来,我们就需要将这些代码整理翻译成java语言
HttpResponse response = HttpRequest.post("https://api.vmall.com/rest.php")
.form("nsp_ts", System.currentTimeMillis())
.form("access_token", ["4.2 使用code获取accessToken"中获取的token])
.form("nsp_fmt", "JS")
.form("nsp_cb", "_jqjsp")
.form("nsp_svc", "OpenUP.User.getInfo")
.execute();
System.out.println(response.body());
打印结果为:
_jqjsp({"gender":1,"headPictureURL":"xxx","languageCode":"zh-CN","userID":"xxx","userName":"xxx","userState":1,"userValidStatus":1}, 0)
典型的JSONP格式,那么有没有办法去掉这个_jqjsp()
?如果不去掉,我们还需要手动处理这些东西,才能得到一个真正的json内容,那么我们就尝试以下,还记得上面说的("nsp_cb", "_jqjsp")
这个就是指定的callback的方法,那么我们直接去掉这个参数,试试行不行
果然! 不得不说:WHNB!
参考JustAuth官方文档:https://docs.justauth.whnb.wang,根据快速开始的说明,我们需要引入依赖:
me.zhyd.oauth
JustAuth
1.10.0
然后通过以下方式调用api进行授权
// 创建授权request
AuthRequest authRequest = new AuthGiteeRequest(AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("redirectUri")
.build());
// 生成授权页面
authRequest.authorize();
// 授权登录后会返回code(auth_code(仅限支付宝))、state,1.8.0版本后,可以用AuthCallback类作为回调接口的参数
// 注:JustAuth默认保存state的时效为3分钟,3分钟内未使用则会自动清除过期的state
authRequest.login(callback);
当然,项目是有配套demo的,可以直接使用https://gitee.com/yadong.zhang/JustAuth-demo这个demo项目进行测试。
clone下来项目后,在RestAuthController#getAuthRequest(String)
方法中找到case "huawei":
,然后将上面我们申请应用时的那三个属性(appid
,secret
,redirectUri
)配置到代码中:
case "huawei":
authRequest = new AuthHuaweiRequest(AuthConfig.builder()
.clientId("[创建产品时提供的appid]")
.clientSecret("[创建产品时提供的secret]")
.redirectUri("[上面配置的回调地址]")
.build());
break;
注,JustAuth-demo
项目中的controller方法比较规范,是根据source
(平台名)区别的具体执行request
,所以,如果直接使用本例的话,需要在创建华为应用时指定redirectUri
为http://127.0.0.1:8443/oauth/callback/huawei
,当然,你也可以直接对JustAuth-demo项目进行改造。
然后启动项目,访问http://localhost:8443
,点击最后的华为
登录即可
点击“授权并登录”即可看到登录后的用户信息。
参考:https://xkcoding.com/2019/08/06/use-justauth-integration-wechat-enterprise.html
JustAuth,史上最全的整合第三方登录的开源库。目前已支持Github、Gitee、微博、钉钉、百度、Coding、腾讯云开发者平台、OSChina、支付宝、QQ、微信、淘宝、Google、Facebook、抖音、领英、小米、微软、今日头条、Teambition、StackOverflow、Pinterest、人人、华为和企业微信等第三方平台的授权登录。 Login, so easy!
JustAuth,如你所见,它仅仅是一个第三方授权登录的工具类库,它可以让我们脱离繁琐的第三方登录SDK,让登录变得So easy!
项目开源地址:gitee | github
废话不多说,就俩字:
快速开始
),尽量让您用起来没有障碍感!