邊緣應用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
},
}