IM 安全通(内容安全审核)
更新时间: 2024/08/14 11:41:36
网易云信 IM 安全通(内容安全审核)服务,融合了网易易盾的内容审核能力,支持对文本消息、图片消息、自定义消息、用户头像和用户资料等类型的内容安全检测,实现全流程的审-查-禁功能,降低人力投入。
功能介绍
开通安全通功能并配置安全通检测规则后,指定类型的消息经过相关的接口调用和配置后,都会先经由安全通进行内容安全检测,之后才会转发给接收端的用户。安全通支持如下几种消息类型的内容审核。
API 关键字 | 说明 |
---|---|
text | 文本消息 |
image | 图片消息 |
audio | 音频消息 |
video | 视频消息 |
avchat | 音视频通话事件消息 |
notification | 通知消息 |
tip | 提醒消息 |
custom | 自定义消息 |
技术原理
针对不同的内容类型,安全通提供两套内容审核方案:
审核方案 |
说明 |
---|---|
实时(同步)内容审核 | 主要针对文本内容。由云信服务端进行内容提交、过滤和投递,客户端会收到内容审核结果,且云信服务器会同时抄送审核结果给应用服务器。客户端上需根据内容审核结果自行实现相关业务处理。 |
异步内容审核 | 主要针对音频、视频。由云信服务端先行投递消息,违规内容由云信服务端删除。内容审核结果抄送至应用服务器,后者根据审核结果对消息进行处理(如撤回)。客户端不会收到内容审核结果。 |
若您需要云信服务器将审核结果抄送到您的应用服务器,即上图中的流程 6,请先开通易盾异步反垃圾抄送。
实现流程
graph LR
%% 定义样式类
classDef default fill:#337EFF,stroke:#337EFF,stroke-width:1px,color:#FFFFFF;
开通安全通 --> 配置安全通检测规则 --> 实现安全通审核 --> 获取审核结果 --> 处理安全审核结果
步骤1:开通安全通并配置安全通检测规则
- 开通安全通,具体步骤请参见开通能力。
- 在控制台首页选择对应的应用,在左侧导航栏选择 产品功能 > IM 即时通讯,顶部选择安全通页签,单击子功能配置。
-
配置内容安全抄送地址。
接收 IM 内容审核结果的抄送地址与 IM、音视频通话 2.0 的消息抄送地址相同,若您此前从未开通抄送功能、未配置抄送地址,请单击前往配置,配置抄送地址。
如果您已经开通了抄送功能,此处默认使用同一个抄送地址。
-
配置内容安全范围。
单击具体审核类型的配置选择需要的模块。
-
配置内容安全疑似处理规则。
单击具体业务的配置进行过审内容和疑似处理配置。
步骤2:配置关键词名单
您可以配置关键词名单,对文本消息中的敏感词进行检测。
-
在网易云信控制台的IM 安全通配置 页面底部,单击更多安全配置。
-
在易盾智能审核平台左侧导航栏选择策略配置 > 关键词名单,将需要检测的敏感词添加到关键词清单中。
步骤3:实现安全通审核
开通安全通服务后,调用发送消息方法时传入相关参数(NIMAntiSpamOption
)可实现消息内容过安全通审核。指定类型的消息会根据检测规则进行内容安全检测。
json{
"type": 1, //1:文本,2:图片
"data": "" //文本内容or图片地址
}
示例代码
java// 该帐号为示例,请先注册
String account = "testAccount";
// 以单聊类型为例
SessionTypeEnum sessionType = SessionTypeEnum.P2P;
String text = "这条不需要过安全通";
// 创建一个文本消息
IMMessage textMessage = MessageBuilder.createTextMessage(account, sessionType, text);
// 构造反垃圾对象
NIMAntiSpamOption antiSpamOption = new NIMAntiSpamOption();
antiSpamOption.enable = false;
textMessage.setNIMAntiSpamOption(antiSpamOption);
// 发送给对方
NIMClient.getService(MsgService.class).sendMessage(textMessage, false);
步骤4:获取审核结果
匹配消息体命中敏感词后,可通过 IMMessage#getYidunAntiSpamRes
字段通知客户端结果,该字段需要通过 observeMsgStatus
回调获取。
只有被安全通拦截的消息才会有yidunAntiSpamRes
返回。对于疑似消息根据云信控制台设置的策略来判断,如果疑似消息被拦截会有yidunAntiSpamRes
返回,如果疑似消息放行则没有yidunAntiSpamRes
返回。
返回的审核结果 yidunAntiSpamRes
为 JSON 字符串格式,请自行解析或者反转成 JSON 对象使用。
yidunAntiSpamRes
字段定义如下:
名称 | 类型 | 说明 |
---|---|---|
code | Integer | 状态码:
|
type | String | 内容审核类型
|
version | String | 易盾内容审核的接口版本 |
taskId | String | 审核任务的 ID |
suggestion | Integer | 建议处理方式
|
status | Integer | 内容审核请求结果
|
ext | String | 内容审核结果,对应易盾的 result 字段,result 字段详情参见易盾文档(注:本链接仅以“单次同步文本检测的 result 字段说明”为例) |
安全通重要参数
参数 | 类型 | 说明 |
---|---|---|
yidunAntiSpamExt | 易盾反垃圾扩展字段 | 透传给易盾的反垃圾增强版的检测参数,具体请参见易盾的反垃圾增强版用户可扩展参数,格式为json,长度限制1024 |
yidunAntiSpamRes | 易盾反垃圾结果 | 易盾反垃圾触发时返回的结果字段,格式为json |
yidunAntiCheating | 易盾反作弊字段 | 透传给易盾的反作弊检测参数,格式为json,长度限制1024 ,具体请参见文本防刷版开发文档 |
示例代码
Observer<IMMessage> statusMessage =
(Observer<IMMessage>)
message -> {
if (!TextUtils.isEmpty(message.getYidunAntiSpamRes())) {
JSONObject jsonObject = null;
try {
jsonObject = new JSONObject(message.getYidunAntiSpamRes());
String ext = jsonObject.optString("ext");
// 安全违规消息
} catch (JSONException e) {
e.printStackTrace();
}
}
};
NIMClient.getService(MsgServiceObserve.class).observeMsgStatus(statusMessage, true);
步骤5:处理安全审核结果
当消息体命中敏感词后,在客户端页面展示类似下图的消息通知。
您可以通过 IM 自定义消息 实现该功能,实现方法如下:
// 注册自定义消息解析器
ChatKitClient.addCustomAttach(
OneOnOneChatCustomMessageType.PRIVACY_RISK_MESSAGE_TYPE, PrivacyRiskAttachment.class);
ChatKitClient.addCustomAttach(
OneOnOneChatCustomMessageType.COMMON_RISK_MESSAGE_TYPE, CommonRiskAttachment.class);
// 注册自定义消息视图
ChatKitClient.addCustomViewHolder(
OneOnOneChatCustomMessageType.PRIVACY_RISK_MESSAGE_TYPE,
PrivacyRiskMessageViewHolder.class);
ChatKitClient.addCustomViewHolder(
OneOnOneChatCustomMessageType.COMMON_RISK_MESSAGE_TYPE, CommonRiskMessageViewHolder.class);
// 监听消息
private final EventObserver<IMMessageInfo> msgObserver =
new EventObserver<IMMessageInfo>() {
@Override
public void onEvent(@Nullable IMMessageInfo event) {
handleRiskMessage(event);
}
};
private void handleRiskMessage(IMMessageInfo event) {
if (event != null && !TextUtils.isEmpty(event.getMessage().getYidunAntiSpamRes())) {
boolean isHitCustomWords = false;
if (event.getMessage().getMsgType() == MsgTypeEnum.text) {
String yidunAntiSpamRes = event.getMessage().getYidunAntiSpamRes();
try {
JSONObject jsonObject = new JSONObject(yidunAntiSpamRes);
String ext = jsonObject.optString("ext");
ALog.i(TAG, "ext:" + ext);
YidunAntiSpamResModel.ExtBean extBean =
GsonUtils.fromJson(ext, YidunAntiSpamResModel.ExtBean.class);
if (extBean != null
&& extBean.getAntispam() != null
&& extBean.getAntispam().getLabels() != null
&& !extBean.getAntispam().getLabels().isEmpty()) {
for (YidunAntiSpamResModel.ExtBean.AntispamBean.LabelsBean label :
extBean.getAntispam().getLabels()) {
List<YidunAntiSpamResModel.ExtBean.AntispamBean.LabelsBean.SubLabelsBean> subLabels =
label.getSubLabels();
if (subLabels != null && !subLabels.isEmpty()) {
for (YidunAntiSpamResModel.ExtBean.AntispamBean.LabelsBean.SubLabelsBean subLabel :
subLabels) {
if (subLabel.getDetails() != null
&& subLabel.getDetails().getKeywords() != null
&& !subLabel.getDetails().getKeywords().isEmpty()) {
List<
YidunAntiSpamResModel.ExtBean.AntispamBean.LabelsBean.SubLabelsBean
.DetailsBean.KeywordsBean>
keywords = subLabel.getDetails().getKeywords();
for (YidunAntiSpamResModel.ExtBean.AntispamBean.LabelsBean.SubLabelsBean
.DetailsBean.KeywordsBean
keyword : keywords) {
if (keyword != null && !TextUtils.isEmpty(keyword.getWord())) {
isHitCustomWords = true;
break;
}
}
}
}
}
}
}
} catch (JSONException e) {
e.printStackTrace();
ALog.e(TAG, "e:" + e);
}
if (isHitCustomWords) {
// 插入隐私提醒本地消息
mainHandler.postDelayed(() -> ChatUtil.insertPrivacyRiskMessage(sessionId), DELAY_TIME);
} else {
// 插入安全提醒本地消息
mainHandler.postDelayed(() -> ChatUtil.insertCommonRiskMessage(sessionId), DELAY_TIME);
}
} else {
// 插入安全提醒本地消息
mainHandler.postDelayed(() -> ChatUtil.insertCommonRiskMessage(sessionId), DELAY_TIME);
}
}
}