# 获取用户风险等级

为了更安全的网络访问，请务必使用TLS1.2协议及规定内的加密套件。若使用协议是TLS1.0、TLS1.1或规定外的加密套件，可能无法正常访问华为账号服务。

关于TLS1.2协议及规定内加密套件的信息，可以点击这里进行详细了解：TLS协议及加密套件。

#### 功能介绍

当应用服务端需要获取风险等级时，应用服务端向华为账号服务器调用该接口获取华为账号用户风险等级。

#### 场景描述

应用已经通过授权码（Authorization Code）获取到Access Token后，应用获取用户风险等级。

#### 使用约束

*   需确保调用端网络正常。
    
*   应用使用此功能之前，需要完成riskLevel（获取用户风险等级）的scope权限申请，在获取Authorization Code时携带riskLevel（获取用户风险等级）的scope，详见开发前提。
    

应用未申请riskLevel（获取用户风险等级）的scope权限，或获取Authorization Code时未携带riskLevel（获取用户风险等级）的scope，调用接口将返回错误，响应体中errCode=403。

#### 接口原型

*   **承载协议**：HTTPS POST
    
*   **接口方向**：开发者服务器->华为账号服务器
    
*   **接口URL**：
    
*   **数据格式**：
    
    请求消息：Content-Type: application/json;charset=utf-8
    
    响应消息：Content-Type: application/json;charset=utf-8
    

#### 请求参数

#### Request Header

| 参数 | 是否必选 | 参数类型 | 描述 |
| --- | --- | --- | --- |
| Content-Type | 是 | String | 取值为：application/json;charset=utf-8。 说明： Request Body传参，请务必遵循此请求头格式，否则可能导致请求失败，具体传参方式可参考示例代码。 |

#### Request Query Parameter

| 参数 | 是否必选 | 参数类型 | 描述 |
| --- | --- | --- | --- |
| clientID | 是 | String | 在创建应用后，由AppGallery Connect（简称AGC）为应用分配的唯一标识。参数取值详见查看应用基本信息中的OAuth 2.0客户端ID（凭据）-Client ID参数。 |
| transactionID | 是 | String | 交易流水号，每个消息都需要带该字段； 生成规则：YYYYMMDDhhmmssxxxxxxxxxx，例如： 202502121546232352164151 其中：YYYYMMDDhhmmss：请求产生时间，14位； xxxxxxxxxx：随机数，可变长，10至20位 |

#### Request Body

| 参数 | 是否必选 | 参数类型 | 描述 |
| --- | --- | --- | --- |
| accessToken | 是 | String | 通过获取用户级凭证获取的Access Token。获取凭证时Authorization Code需包含riskLevel scope的授权。 |
| scene | 是 | String | 获取用户风险等级的业务场景 - registration：注册 - marketing：营销活动 |

#### 请求示例

请通过POST方式调用，示例如下：

```
POST /user/getuserrisklevel?clientID=<clientID>&transactionID=<transactionID> HTTP/1.1
Host: account.cloud.huawei.com
Content-Type: application/json;charset=utf-8

{"accessToken":"<Access Token>", "scene":"<scene>"}
```

#### 响应参数

#### Response Header

| 参数 | 是否必选 | 参数类型 | 描述 |
| --- | --- | --- | --- |
| Content-Type | 是 | String | 取值为：application/json;charset=utf-8。 |

#### Response Body

| 参数 | 是否必选 | 参数类型 | 描述 |
| --- | --- | --- | --- |
| errCode | 是 | Int | 错误码，调用成功时返回0，调用失败时返回对应错误码。 |
| errMsg | 是 | String | 错误描述，调用成功时返回空字符串，调用失败时返回对应描述。 |
| riskLevel | 否 | Int | 请求成功时才会返回风险等级（详见后文附表①） 0：未发现显著风险 1：低风险 2：中风险 3：高风险 4：风险未知 |
| riskTag | 否 | String[] | 风险标签（详见后文附表②） |

附表①: 风险等级含义与建议处置方案

| 等级 | 含义 | 建议处置方案 |
| --- | --- | --- |
| 0 | 未发现显著风险项。例如通过合法途径注册，正常使用华为手机，无批量操作、使用自动机、养号等异常行为的场景。 | 建议确认无风险后放通。 |
| 1 | 低风险。发现风险因素，结合总体评分后风险较低。 | 建议进行简单验证（如验证码、短信等），或人工审核。 |
| 2 | 中风险。发现风险因素，结合总体评分后风险中等。 | 建议根据业务场景采取一定措施规避伤害。例如，营销活动可降低高等级奖励的概率、打榜类活动对此类投票降低权重、登录注册要求二次验证等。 |
| 3 | 高风险。发现风险因素，结合总体评分后风险较高。 | 建议业务逻辑直接拦截。例如，红包类活动返回不中奖或最小额红包、打榜类活动不计算票数、登录/注册操作要求二次验证、高危业务可选择限制本次操作。 |
| 4 | 风险未知。暂无明确风险等级。 | 建议结合账号历史行为及业务场景做出最终决策。 |

附表②: 风险标签及解释

| riskTag | 名称 | 解释 |
| --- | --- | --- |
| spamMailbox | 绑定垃圾邮箱 | 疑似绑定垃圾邮箱 |
| riskPhoneNumber | 绑定卡商手机号 | 疑似绑定卡商手机号 |
| riskDevice | 使用风险设备 | 疑似使用自动机、群控等异常设备注册或登录 |
| ipCluster | IP聚集 | 疑似半年内存在IP的聚集性异常 |
| deviceCluster | 设备聚集 | 疑似半年内存在设备的聚集性异常 |
| batchBehavior | 批量操作 | 疑似半年内与大量垃圾账号存在批量协同行为轨迹 |
| illegalLogin | 非法登录 | 当前华为账号疑似通过非法手段登录 |
| activityFraud | 恶意行为-薅羊毛 | 疑似半年内在营销活动中存在薅羊毛的行为 |

#### 响应示例

#### 请求成功时

```
HTTP/1.1 200 OK
Content-Type: application/json;charset=utf-8

{
    "errCode": 0,
    "errMsg": "",
    "riskLevel": 2,
    "riskTag": [
        "spamMailbox"
    ]
}
```

#### 请求失败时

```
HTTP/1.1 200 OK
Content-Type: application/json;charset=utf-8

{
    "errCode": 403,
    "errMsg": "no permission"
}
```

#### 示例代码

Java示例代码如下，运行前需要进行示例代码环境配置（请将此示例代码与工具类CallUtils放于同一路径下，如不在同一路径，请手动添加import）

```
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * 获取用户风险等级
 */
public class GetUserRiskLevelDemo {
    public static void main(String[] args) throws IOException {
        // 获取用户风险等级的接口URL
        String url = "";
        // 替换为实际的Client ID
        String clientID = "<Client ID>";
        // 替换为实际的transactionID
        String transactionID = "<transactionID>";
        // 替换为实际获取到的用户级凭证Access Token
        String accessToken = "<Access Token>";
        // 替换为实际的scene
        String scene = "<scene>";
        JSONObject result = getUserRiskLevel(url, clientID, transactionID, accessToken, scene);
        // 解析获取errCode
        Integer errCode = result.getInteger("errCode");
        // 解析获取errMsg
        String errMsg = result.getString("errMsg");
        // 解析获取riskLevel
        Integer riskLevel = result.getInteger("riskLevel");
        // 解析获取riskTag
        JSONArray riskTag = result.getJSONArray("riskTag");
    }

    private static JSONObject getUserRiskLevel(String url, String clientID, String transactionID,
        String accessToken, String scene) throws IOException {
        HttpPost httpPost = new HttpPost(url + "?" + "clientID=" + clientID + "&transactionID=" + transactionID);
        Map<String, String> reqBody = new HashMap<>();
        reqBody.put("accessToken", accessToken);
        reqBody.put("scene", scene);
        httpPost.setHeader("Content-Type", "application/json;charset=utf-8");
        httpPost.setEntity(CallUtils.wrapJsonEntity(reqBody));
        return CallUtils.toJsonObject(CallUtils.remoteCall(httpPost, (CloseableHttpResponse response, String rawBody) -> {
            int statusCode = response.getStatusLine().getStatusCode();
            // http状态码不是200，请求失败
            if (statusCode != 200) {
                return new IOException("call failed! http status code: " + statusCode + ", response data: " + rawBody);
            }
            // http状态码为200，解析响应的body，判断业务错误码
            JSONObject errorResponseBody = CallUtils.toJsonObject(rawBody);
            // 错误码
            Integer errCode = errorResponseBody.getInteger("errCode");
            // errCode为0表示成功，非0表示失败
            if (Objects.nonNull(errCode) && errCode != 0) {
                return new IOException("call failed! http status code: " + statusCode + ", response data: " + rawBody);
            }
            return null;
        }));
    }
}
```

#### 错误码

| HTTP响应码 | 描述 | 解决方法 |
| --- | --- | --- |
| 200 | 仅表示本次接口调用成功（接口调用成功不等于业务处理成功，实际业务处理结果需要通过Response Body中的errCode进行判断）。 | - |
| 400 | 参数错误。 | 请根据文档排查请求参数是否符合规范。 |
| 403 | 无权限访问。 | 通常是调用方网络安全策略阻止了访问，请检查网络环境配置。若仍无法解决，请通过在线提单提交问题。 |
| 404 | 找不到服务。 | 请检查请求URI是否正确。 |
| 405 | 不支持的http请求method。 | 请检查http请求method是否与接口说明一致。 |
| 415 | 不支持的媒体类型 | 请检查http请求的contentType是否与接口说明一致。 |
| 500 | 服务内部错误。 | 请通过在线提单提交问题。 |
| 502 | 请求连接异常，常见于网络状况不稳定。 | 建议稍后重试，若仍无法解决，请通过在线提单提交问题。 |
| 504 | 请求连接超时，常见于网络状况不稳定。 | 建议稍后重试，若仍无法解决，请通过在线提单提交问题。 |
| 590 | 服务内部错误。 | 请通过在线提单提交问题。 |

| errCode | 描述 | 解决方法 |
| --- | --- | --- |
| 6 | 会话失效，session timeout。 可能原因: - access_token无效或已过期 - access_token格式不正确 - 其他内部原因 | - 请检查传参是否正确，如无问题请尝试重新获取。 - 本接口请求数据格式为 application/json;charset=utf-8，在构造请求体时，请确保不对access_token参数进行URLEncode处理，可参考示例代码组装参数。 - 根据返回的错误描述进行处理，若仍无法解决，请通过在线提单提交问题。 |
| 403 | 无权访问 | 请前往AppGallery Connect（简称AGC）为应用申请开放权限，详见申请账号权限。 |
| 503 | 触发系统流控。 | 请稍后重试。 |
| 70001201 | 请求参数错误 | 修改请求url或者请求体中的参数。 |
| 70001402 | 系统鉴权错误。 | 鉴权系统异常，若重试无法解决，请通过在线提单提交问题。 |
| 70020002 | 接口内部超时 | 稍后重试。 |
| 70001401 | 接口内部错误 | 根据返回的错误描述进行处理，若仍无法解决，请通过在线提单提交问题。 |