海康NVR对接工具类(ISAPI)


import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.xsyt.nvr.model.CameraChannel;
import okhttp3.*;
import javax.xml.bind.DatatypeConverter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;

public class HikNvrUtil {

    private String ip;
    private int port;
    private String username;
    private String password;
    private OkHttpClient httpClient;
    private ObjectMapper jsonMapper;
    private XmlMapper xmlMapper;

    public HikNvrUtil(String ip, int port, String username, String password) {
        this.ip = ip;
        this.port = port;
        this.username = username;
        this.password = password;
        this.httpClient = new OkHttpClient.Builder()
                .addInterceptor(new DigestAuthenticator(username, password))
                .build();
        this.jsonMapper = new ObjectMapper();
        this.xmlMapper = new XmlMapper();
    }

    /**
     * 获取nvr的设备ID
     * @return
     * @throws IOException
     */
    public String getDeviceId() throws IOException {
        return JSONObject.parseObject(convertXmlToJson(getDeviceInfo())).getString("deviceID").replace("-","");
    }
    public String getDeviceInfo() throws IOException {
        String url = String.format("http://%s:%d/ISAPI/System/deviceInfo", ip, port);
        return executeGetRequest(url);
    }

    public String getDeviceStatus() throws IOException {
        String url = String.format("http://%s:%d/ISAPI/System/status", ip, port);
        return executeGetRequest(url);
    }

    public String getChannelInfo() throws IOException {
        String url = String.format("http://%s:%d/ISAPI/Streaming/channels", ip, port);
        return executeGetRequest(url);
    }

    /**
     * 获取通道ID
     * @return
     * @throws IOException
     */
    public List<String> getChannelIds() throws IOException {
        List<String> channelIds = new ArrayList<>();
        JSONObject.parseObject(convertXmlToJson(getChannelInfo())).getJSONArray("StreamingChannel").forEach(channel -> {
            channelIds.add(((JSONObject)channel).getString("id"));
        });
        return channelIds;
    }

    /**
     * 获取通道信息
     * @return
     * @throws IOException
     */
    public List<CameraChannel> getChannels() throws IOException {
        List<String> channelIds = getChannelIds();
        //通道ID改为四位数字
        channelIds.forEach(channelId -> {
            String channelIdStr = String.format("%04d", Integer.parseInt(channelId));
            channelIds.set(channelIds.indexOf(channelId), channelIdStr);
        });
        List<CameraChannel> cameraChannels = new ArrayList<>();
        //按照前两位数字一致的为同一个相机通道, 后两位是01的为主码通道,后两位是02的为子码通道,保存到对象CameraChannel中
        channelIds.forEach(channelId -> {
            if (channelId.endsWith("01")) {
                String channelNo = channelId.substring(0, 2);
                if (channelIds.contains(channelNo + "02")) {
                    cameraChannels.add(new CameraChannel(channelNo, channelId, channelNo + "02"));
                } else {
                    cameraChannels.add(new CameraChannel(channelNo, channelId, ""));
                }
            } else if (channelId.endsWith("02")) {
                String channelNo = channelId.substring(0, 2);
                if (!channelIds.contains(channelNo + "01")) {
                    cameraChannels.add(new CameraChannel(channelNo, "", channelId));
                }
                //包含则等循环到01时候添加
            } else {
                String channelNo = channelId.substring(0, 2);
                System.out.println("未知的通道规则:"+channelId);
                cameraChannels.add(new CameraChannel(channelNo, channelId, ""));
            }
        });

        return cameraChannels;

    }


    private String executeGetRequest(String url) throws IOException {
        Request request = new Request.Builder()
                .url(url)
                .get()
                .build();

        try (Response response = httpClient.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                throw new IOException("Unexpected code " + response);
            }
            return response.body().string();
        }
    }


    private String convertXmlToJson(String xml) throws IOException {
        JsonNode node = xmlMapper.readTree(xml.getBytes(StandardCharsets.UTF_8));
        return jsonMapper.writerWithDefaultPrettyPrinter().writeValueAsString(node);
    }

    public static void main(String[] args) {
        HikNvrUtil nvrUtils = new HikNvrUtil("192.168.0.4", 80, "admin", "1qaz2wsx");
        try {
            System.out.println("Device ID: " + nvrUtils.getDeviceId());
//            System.out.println("Device Info: " + nvrUtils.convertXmlToJson(nvrUtils.getDeviceInfo()));
//            System.out.println("Device Status: " + nvrUtils.convertXmlToJson(nvrUtils.getDeviceStatus()));
//            System.out.println("Channel Info: " + nvrUtils.convertXmlToJson(nvrUtils.getChannelInfo()));
//            System.out.println("Channel IDs: " + nvrUtils.getChannelIds());
            System.out.println("Channels: " + nvrUtils.getChannels());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

class DigestAuthenticator implements Interceptor {

    private final String username;
    private final String password;
    private String realm;
    private String nonce;
    private String qop;
    private String opaque;
    private String algorithm;

    public DigestAuthenticator(String username, String password) {
        this.username = username;
        this.password = password;
    }

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        Response response = chain.proceed(request);

        if (response.code() == 401) {
            Headers headers = response.headers();
            String authHeader = headers.get("WWW-Authenticate");

            if (authHeader != null && authHeader.startsWith("Digest ")) {
                parseAuthHeader(authHeader);
                String digestHeader = createDigestHeader(request);
                Request newRequest = request.newBuilder()
                        .header("Authorization", digestHeader)
                        .build();

                response.close();
                return chain.proceed(newRequest);
            }
        }

        return response;
    }

    private void parseAuthHeader(String authHeader) {
        String[] parts = authHeader.substring(7).split(", ");
        for (String part : parts) {
            String[] keyValue = part.split("=", 2);
            String key = keyValue[0];
            String value = keyValue[1].replaceAll("\"", "");

            switch (key) {
                case "realm":
                    this.realm = value;
                    break;
                case "nonce":
                    this.nonce = value;
                    break;
                case "qop":
                    this.qop = value;
                    break;
                case "opaque":
                    this.opaque = value;
                    break;
                case "algorithm":
                    this.algorithm = value;
                    break;
            }
        }
    }

    private String createDigestHeader(Request request) {
        String method = request.method();
        String uri = request.url().encodedPath();
        String ha1 = md5(username + ":" + realm + ":" + password);
        String ha2 = md5(method + ":" + uri);
        String nc = "00000001";
        String cnonce = UUID.randomUUID().toString().replace("-", "");
        String response = md5(ha1 + ":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + ha2);

        return "Digest username=\"" + username + "\", realm=\"" + realm + "\", nonce=\"" + nonce + "\", uri=\"" + uri
                + "\", qop=" + qop + ", nc=" + nc + ", cnonce=\"" + cnonce + "\", response=\"" + response
                + "\", opaque=\"" + opaque + "\", algorithm=" + algorithm;
    }

    private String md5(String input) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] messageDigest = md.digest(input.getBytes(StandardCharsets.UTF_8));
            return DatatypeConverter.printHexBinary(messageDigest).toLowerCase();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }
}

对应在Postman中的调用方式:

 

相关推荐

  1. 45 对接视频九宫格的实现

    2024-07-10 17:20:03       22 阅读
  2. 115.工业相机SDK开发指南(阅读)

    2024-07-10 17:20:03       26 阅读
  3. 对接明眸门禁设备-周计划模板

    2024-07-10 17:20:03       43 阅读
  4. 对接明眸门禁设备-查询人员信息

    2024-07-10 17:20:03       58 阅读
  5. Qt C++SDK中注册回调代码

    2024-07-10 17:20:03       19 阅读
  6. C# MG.CamCtrl 工业相机库(开源) 大恒

    2024-07-10 17:20:03       32 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-10 17:20:03       5 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-10 17:20:03       5 阅读
  3. 在Django里面运行非项目文件

    2024-07-10 17:20:03       4 阅读
  4. Python语言-面向对象

    2024-07-10 17:20:03       6 阅读

热门阅读

  1. 图形渲染基础-GPU驱动的渲染管线

    2024-07-10 17:20:03       12 阅读
  2. 数据库的基本概念

    2024-07-10 17:20:03       11 阅读
  3. 图形渲染基础-Unity渲染管线介绍

    2024-07-10 17:20:03       12 阅读
  4. spring xml实现bean对象(仅供自己参考)

    2024-07-10 17:20:03       11 阅读
  5. Tomcat异常处理【Spring源码学习】

    2024-07-10 17:20:03       14 阅读
  6. Leetcode101 判断二叉树是否对称

    2024-07-10 17:20:03       7 阅读
  7. 【深入剖析】Kylin架构全景及其组件详解

    2024-07-10 17:20:03       9 阅读