边缘应用OAuth免登对接
1.边缘应用OAuth免登介绍
本章主要介绍部署在边缘集群托管的应用,实现免登及用户信息获取相关流程。
1.1 OAuth免登调用流程
系统应用环境变量中获取访问域名。System.getenv(“iot.hosting.api.domain”)。
应用跳转IoT oauth验证地址,同时携带认证后跳转的callback地址。
IoT认证后,携带授权码oauthcode跳转第2步callback的地址。
应用获取到oauthcode后可以换取accesscode,并通过accesscode获取到用户的完整信息。
2.实操效果展示
2.1 部署组件
单击组件管理>访问入口 复制IP+端口号,打开新的浏览器页面进行访问,如下图所示:
2.2 登录边缘控制台
使用“超级账户iotedgeadmin”登录“集群控制台”,用户名与密码一致,首次登录强制修改密码,此连接为内网地址,请保证电脑可以正常连接至内网,如下图所示:
2.3 查看部署应用
登录成功后,可单击部署应用的应用卡片,进入应用详情,如下图所示:
2.4 点击应用进行免登
点击部署的应用,直接跳转到应用内部,验证免登,如下图所示:
3.开发API
3.1 获取应用入口地址
接口描述:
授权类型 | APPSIGN |
协议 | HTTP |
请求方法 | get |
域名(环境变量中获取) | System.getenv(“iot.hosting.api.domain”) |
路径 | /api/console/app/get |
入参说明
入参名称 | 数据类型 | 是否必须 | 入参示例 | 入参描述 |
appKey | 字符串 | 是 | 21312**** | appkey |
port | int | 是 | 80443 | 请求的服务端口 |
出参列表
出参名称 | 数据类型 | 出参描述 |
code | 整型 | 响应码, 200: 成功 |
message | 字符串 | 错误消息 |
localizedMsg | 字符串 | 本地语言错误消息 |
data | 长整型 | 响应结果返回为app的相关应用URL |
请求拼接示例
http://30.42.XX.XX:32187/api/console/app/get?appKey=2813****
请求示例
/**
* 获取环境变量和回调的url
* @return
*/
//iot.hosting.api.schema----是请求协议格式http
private static final String API_GATEWAY_SCHEMA = System.getenv("iot.hosting.api.schema");
//iot.hosting.api.domain--是跳转路径的回调ip地址
private static final String API_GATEWAY_DOMAIN = System.getenv("iot.hosting.api.domain");
//iot.hosting.api.port----是请求端口
private static final String API_GATEWAY_SCHEMA = System.getenv("iot.hosting.api.port");
// iot.hosting.appKey----是请求的appkey
private static final String API_GATEWAY_SCHEMA = System.getenv(" iot.hosting.appKey");
//PATH_APP_GET是请求应用app的响应路径
private static final String PATH_APP_GET = "/api/console/app/get";
@Override
public String getAppIndex() {
RequestBuilder builder = RequestBuilder.create(METHOD_GET);
Map<String, String> queryParams = Maps.newHashMap();
queryParams.put(PARAM_APP_KEY, appKey);
builder.setUri(HttpUtils.buildUrl(schema, apiGatewayDomain, apiGatewayPort, PATH_APP_GET, queryParams));
IoTxResult<AppDTO> result = httpProxy.invoke(
(HttpRequestBase) builder.build(),
new TypeReference<IoTxResult<AppDTO>>() {
}
);
logger.info("path={}; params={}; result={}", PATH_APP_GET, JSON.toJSONString(queryParams), JSON.toJSONString(result));
Assert.assertSuccess(result);
AppDTO app = result.getData();
return app.getLoginUrl();
}
返回结果示例 JSON
{
{
"code": 200,
"data": {
"aliyunPk": "101248722030****",
"appKey": "2813****",
"appMeta": {
"logoUrl": "http://192.168.11.130:32628/index/28135***",
"name": "oauth2边缘托管",
"subVersionId": "1.0",
"uuid": "937aaa0284864396b54ecadb0d16****",
"versionUuid": "9ff097f16d8347e6abfcaec0cd14****"
},
"appSecret": "NGNiOWQyYzI4MGVhOWNlYmNhOTdmMDIyNTg2MzlhY2Q=",
"clusterId": "bbeba04d880d49dc80bf83632619341c",
"configName": "oauth2边缘托管",
"configUuid": "a7b996cd6ab04b7fb3a54abbc81b****",
"configVersionUuid": "8e43f4d96bef4a289ad9f9af079d****",
"loginUrl": "http://192.168.11.130:30313/index",//-----应用登录url
"name": "Oauth2演示",
"oauth": {
"path": "/index",
"port": 8080,
"protocol": "",
"serviceName": "edge",
"serviceUuid": ""
},
"type": "GENERAL_APP",
"uuid": "731082ac0c444053bad624ec915b****"
},
"message": "success"
}
}
3.2 获取API网关地址
接口描述
授权类型 | APPSIGN |
协议 | HTTP |
请求方法 | get |
域名(环境变量中获取) | System.getenv(“iot.hosting.api.domain”) |
路径 | /api/console/host/account |
入参说明
入参名称 | 数据类型 | 是否必须 | 入参示例 | 入参描述 |
chema | 字符串 | 是 | HTTP | 请求协议 |
ports | int | 是 | 80443 | 请求的服务端口 |
url | 字符型 | 是 | /api/console/host/account | 请求URL |
host | 字符型 | 是 | 30.42.XX.XX:32187 | 请求host |
出参列表
出参名称 | 数据类型 | 出参描述 |
code | 整型 | 响应码, 200: 成功 |
message | 字符串 | 错误消息 |
localizedMsg | 字符串 | 本地语言错误消息 |
data | 长整型 | 响应结果 |
请求拼接示例
http://30.42.XX.XX:32187/api/console/host/account
请求示例
/**
*
* /api/console/host/account
* @param path
* @return
*/
@Override
public URI getURI(String path) {
RequestBuilder builder = RequestBuilder.create(METHOD_GET);
Map<String, String> queryParams = Maps.newHashMap();
builder.setUri(HttpUtils.buildUrl(schema, apiGatewayDomain, apiGatewayPort, path, queryParams));
logger.info("RequestBuilder请求url");
IoTxResult<String> result = httpProxy.invoke(
(HttpRequestBase) builder.build(),
new TypeReference<IoTxResult<String>>() {
}
);
logger.info("path={}; params={}; result={}", path, JSON.toJSONString(queryParams), JSON.toJSONString(result));
Assert.assertSuccess(result);
try {
return new URI(result.getData());
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
返回结果示例 JSON
{
"code": 200,
"data": {
"oauth": {
"path": "/index",
"port": 32187,
"host":30.42.XX.XX
"protocol": "",
"serviceName": "edge",
"serviceUuid": ""
},
"type": "GENERAL_APP",
"uuid": "731082ac0c444053bad624ec915b****"
},
"message": "success"
}
3.3 发起免登验证
接口描述
授权类型 | APPSIGN |
协议 | HTTP |
请求方法 | get |
域名(环境变量中获取) | System.getenv(“iot.hosting.api.domain”) |
路径 | /oauth2/auth |
入参说明
入参名称 | 数据类型 | 是否必须 | 入参示例 | 入参描述 |
client_id | String | 是 | 2813**** | 应用的appkey |
redirect_uri | String | 是 | http://30.42.XX.XX:32187/index | OAuth认证通过后的重定向应用的URI,包含完整的域名 |
response_type | String | 是 | code | 返回类型。根据OAuth 2.0标准,目前支持设置此参数的取值为 |
state | String | 否 | 2813**** | 应用的appkey携带项 |
scope | String | 否 | 空格分隔的OAuth范围列表。如不指定此参数取值,则默认为应用注册的全部OAuth范围,加上scopid |
出参列表
出参名称 | 数据类型 | 出参描述 |
code | 整型 | 响应码, 200: 成功 |
message | 字符串 | 错误消息 |
localizedMsg | 字符串 | 本地语言错误消息 |
data | 长整型 | 响应结果 |
请求拼接示例
http://30.42.XX.XX:32187/oauth2/auth?
redirect_uri=http://30.42.XX.XX:32187/index&
client_id=28135051&state=28135051&
response_type=code
请求示例
/**
*获取免登url和code
* @param appKey
* @param redirectUri
* @return
*/
public String getLoginRedirectUrl(String appKey, String redirectUri) {
Map<String, String> queryParams = Maps.newHashMap();
queryParams.put(PARAM_CLIENT_ID, appKey);
queryParams.put(PARAM_REDIRECT_URI, redirectUri);
queryParams.put(PARAM_RESPONSE_TYPE, "code");
URI accountUri = getAccountUri();
return HttpUtils.buildUrl(
schema,
accountUri.getHost(),
accountUri.getPort(),
PATH_LOGIN,
queryParams).toASCIIString();
}
返回结果示例 JSON
http://30.42.XX.XX:32187/index?code=64a67ee15534defea7ad0d0535189b24&state=28135051
3.4 换取accessToken
接口描述
授权类型 | ANONYMOUS |
协议 | HTTP |
请求方法 | post |
域名(环境变量中获取) | System.getenv(“iot.hosting.api.domain”) |
路径 | /user/oauth2/token/get |
入参说明
入参名称 | 数据类型 | 是否必须 | 入参描述 |
code | 字符串 | 是 | 初始请求中获取的授权码 |
grant_type | 字符串 | 是 | 根据OAuth 2.0标准, 取值为authorization_code |
redirect_uri | 字符型 | 是 | 初始authorization请求中设置的redirect_uri参数 |
client_id | 字符型 | 是 | 应用的appkey |
出参列表
出参名称 | 数据类型 | 出参描述 |
code | 整型 | 响应码, 200: 成功 |
message | 字符串 | 错误消息 |
localizedMsg | 字符串 | 本地语言错误消息 |
data | 长整型 | 响应结果 |
请求示例
/**
* 根据code获取到token
* @param appKey
* @param oauthCode
* @return
*/
public String getAccessTokenByOauthCode(String appKey, String oauthCode) {
RequestBuilder builder = RequestBuilder.create(METHOD_GET);
Map<String, String> queryParams = Maps.newHashMap();
queryParams.put(PARAM_CODE, oauthCode);
queryParams.put(PARAM_GRANT_TYPE, "authorization_code");
queryParams.put(PARAM_CLIENT_ID, appKey);
URI accountUri = getAccountUri();
builder.setUri(HttpUtils.buildUrl(accountUri.getScheme(), accountUri.getHost(), accountUri.getPort(), PATH_GET_ACCESS_TOKEN_BY_OAUTH_CODE, queryParams));
IoTxResult<AccessTokenDTO> result = httpProxy.invoke(
(HttpRequestBase) builder.build(),
new TypeReference<IoTxResult<AccessTokenDTO>>() {
}
);
PROXY_LOGGER.info("path={}; params={}; result={}", PATH_GET_ACCESS_TOKEN_BY_OAUTH_CODE, JSON.toJSONString(queryParams), JSON.toJSONString(result));
Assert.assertSuccess(result);
AccessTokenDTO accessTokenDTO = result.getData();
Assert.assertNotNull(accessTokenDTO, "get accessToken failed");
return accessTokenDTO.getAccessToken();
}
返回结果示例 JSON
{
"code": 200,
"data": {
"access_token": "agasdfagdsafasdf",
"expired_time": 21232,
"refresh_token": "asdgadgasfadsaf",
"token_type": "Bearer",
"open_id": "gasdfasdfasdf"
},
"id": "246da69e-40ad-4f44-955e-ac880f9867d7"
}
3.5 获取用户信息
接口描述
授权类型 | ANONYMOUS |
协议 | HTTP |
请求方法 | post |
域名(环境变量中获取) | System.getenv(“iot.hosting.api.domain”) |
路径 | /user/oauth2/userinfo/get |
入参说明
入参名称 | 数据类型 | 是否必须 | 入参示例 | 入参描述 |
access_token | String | 是 | 3123qwqrqwq | 访问令牌 |
出参列表
出参名称 | 数据类型 | 出参描述 |
code | 整型 | 响应码, 200: 成功 |
message | 字符串 | 错误消息 |
localizedMsg | 字符串 | 本地语言错误消息 |
data | 长整型 | 响应结果 |
请求示例
/**
* 根据token获取用户信息
* @param accessToken
* @return
*/
public UserInfoDTO getUserInfoByAccessToken(String accessToken) {
RequestBuilder builder = RequestBuilder.create(METHOD_GET);
Map<String, String> queryParams = Maps.newHashMap();
queryParams.put(PARAM_ACCESS_TOKEN, accessToken);
URI accountUri = getAccountUri();
builder.setUri(HttpUtils.buildUrl(accountUri.getScheme(), accountUri.getHost(), accountUri.getPort(), PATH_GET_USER_INFO_BY_ACCESS_TOKEN, queryParams));
IoTxResult<UserInfoDTO> result = httpProxy.invoke(
(HttpRequestBase) builder.build(),
new TypeReference<IoTxResult<UserInfoDTO>>() {
}
);
PROXY_LOGGER.info("path={}; params={}; result={}", PATH_GET_USER_INFO_BY_ACCESS_TOKEN, JSON.toJSONString(queryParams), JSON.toJSONString(result));
Assert.assertSuccess(result);
UserInfoDTO userInfoDTO = result.getData();
Assert.assertNotNull(userInfoDTO, "get userInfo failed");
return userInfoDTO;
}
返回结果示例 JSON
"id": "246da69e-40ad-4f44-955e-ac880f9867d7",
"code": 200,
"message": null,
"localizedMsg": null,
"data": {
"open_id": "eaef01f7bfaf0b7a95366720d58d1fd5",
"name": "test",
"phone": "1381234****",
"tenant_open_id": "1c8c23f0adef8bd04fa166372b781f0d",
"tenant_name": "test"
}
3.6 Token有效性判断
接口描述
授权类型 | ANONYMOUS |
协议 | HTTP |
请求方法 | post |
域名(环境变量中获取) | System.getenv(“iot.hosting.api.domain”) |
路径 | /user/oauth2/accesstoken/check |
入参说明
入参名称 | 数据类型 | 是否必须 | 入参示例 | 入参描述 |
access_token | String | 是 | 3123qwqrqwq | 访问令牌 |
出参列表
出参名称 | 数据类型 | 出参描述 |
code | 整型 | 响应码, 200: 成功 |
message | 字符串 | 错误消息 |
localizedMsg | 字符串 | 本地语言错误消息 |
data | 长整型 | 响应结果 |
请求示例
/**
* token登录有效期检查
* @param token
* @return
*/
public boolean checkLogin(String token) {
RequestBuilder builder = RequestBuilder.create(METHOD_GET);
Map<String, String> queryParams = Maps.newHashMap();
queryParams.put(PARAM_ACCESS_TOKEN, token);
URI accountUri = getAccountUri();
builder.setUri(HttpUtils.buildUrl(accountUri.getScheme(), accountUri.getHost(), accountUri.getPort(), PATH_LOGINCHECK, queryParams));
IoTxResult<Boolean> result = httpProxy.invoke(
(HttpRequestBase) builder.build(),
new TypeReference<IoTxResult<Boolean>>() {
}
);
PROXY_LOGGER.info("path={}; params={}; result={}", PATH_LOGINCHECK, JSON.toJSONString(queryParams), JSON.toJSONString(result));
Assert.assertSuccess(result);
return result.getData();
}
返回结果示例 JSON
{
"code": 200,
"data": {
"result":true
},
}