主动发起注册 未完

This commit is contained in:
shikong 2023-09-12 16:30:32 +08:00
parent 6d280daed8
commit 68f5964a42
10 changed files with 274 additions and 1 deletions

View File

@ -0,0 +1,31 @@
package cn.skcks.docking.gb28181.mocking.api.gb28181;
import cn.skcks.docking.gb28181.annotation.web.methods.GetJson;
import cn.skcks.docking.gb28181.common.json.JsonResponse;
import cn.skcks.docking.gb28181.mocking.config.SwaggerConfig;
import cn.skcks.docking.gb28181.mocking.service.gb28181.register.RegisterService;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springdoc.core.models.GroupedOpenApi;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@Tag(name = "设备信息")
@RestController
@RequestMapping("/gb28181")
@RequiredArgsConstructor
public class Gb28181Controller {
private final RegisterService registerService;
@Bean
public GroupedOpenApi gb28181Api() {
return SwaggerConfig.api("GB28181 Api", "/gb28181");
}
@GetJson("/register")
public JsonResponse<Boolean> register(){
return JsonResponse.success(registerService.register());
}
}

View File

@ -5,6 +5,8 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.sip.ListeningPoint;
@Component
@ConfigurationProperties(prefix = "gb28181.server", ignoreInvalidFields = true)
@Order(0)
@ -15,4 +17,5 @@ public class ServerConfig {
private String ip;
private int port;
private String password;
private String transport = ListeningPoint.UDP;
}

View File

@ -26,6 +26,8 @@ public class SipConfig {
private String password;
private int expire = 3600;
Integer ptzSpeed = 50;
Integer registerTimeInterval = 120;

View File

@ -0,0 +1,5 @@
package cn.skcks.docking.gb28181.mocking.core.sip.message.register.request;
public class RegisterRequest {
}

View File

@ -0,0 +1,176 @@
package cn.skcks.docking.gb28181.mocking.core.sip.request;
import cn.skcks.docking.gb28181.core.sip.message.MessageHelper;
import cn.skcks.docking.gb28181.core.sip.utils.SipUtil;
import cn.skcks.docking.gb28181.mocking.config.sip.ServerConfig;
import cn.skcks.docking.gb28181.mocking.config.sip.SipConfig;
import cn.skcks.docking.gb28181.mocking.orm.mybatis.dynamic.model.MockingDevice;
import cn.skcks.docking.gb28181.orm.mybatis.dynamic.model.DockingDevice;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;
import javax.sip.SipFactory;
import javax.sip.address.Address;
import javax.sip.address.SipURI;
import javax.sip.header.*;
import javax.sip.message.Request;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
@DependsOn("serverConfig")
@Component
public class SipRequestBuilder implements ApplicationContextAware {
private static ServerConfig serverConfig;
private static SipConfig sipConfig;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
serverConfig = applicationContext.getBean(ServerConfig.class);
sipConfig = applicationContext.getBean(SipConfig.class);
}
private static SipFactory getSipFactory(){
return SipFactory.getInstance();
}
@SneakyThrows
private static SipURI getSipURI(String id, String address){
return MessageHelper.createSipURI(id, address);
}
@SneakyThrows
private static Address getAddress(SipURI uri){
return MessageHelper.createAddress(uri);
}
@SneakyThrows
private static FromHeader getFromHeader(Address fromAddress, String fromTag){
return MessageHelper.createFromHeader(fromAddress, fromTag);
}
@SneakyThrows
private static ToHeader getToHeader(Address toAddress, String toTag){
return MessageHelper.createToHeader(toAddress, toTag);
}
@SneakyThrows
private static MaxForwardsHeader getMaxForwardsHeader(int maxForwards){
return getSipFactory().createHeaderFactory().createMaxForwardsHeader(maxForwards);
}
@SneakyThrows
private static List<ViaHeader> getDeviceViaHeaders(DockingDevice device, String viaTag){
ViaHeader viaHeader = getSipFactory().createHeaderFactory().createViaHeader(device.getLocalIp(), serverConfig.getPort(), device.getTransport(), viaTag);
viaHeader.setRPort();
return Collections.singletonList(viaHeader);
}
@SneakyThrows
private static List<ViaHeader> getViaHeaders(String ip,int port, String transport, String viaTag){
ViaHeader viaHeader = getSipFactory().createHeaderFactory().createViaHeader(ip, port, transport, viaTag);
viaHeader.setRPort();
return Collections.singletonList(viaHeader);
}
@SneakyThrows
private static CSeqHeader getCSeqHeader(long cSeq, String method){
return getSipFactory().createHeaderFactory().createCSeqHeader(cSeq, method);
}
@SneakyThrows
public static Request createRegisterRequest(MockingDevice device, String ip, int port, long cSeq, String fromTag, String viaTag, CallIdHeader callIdHeader) {
String target = StringUtils.joinWith(":", serverConfig.getIp(), serverConfig.getPort());
SipURI requestURI = getSipURI(serverConfig.getId(), target);
// via
List<ViaHeader> viaHeaders = getViaHeaders(serverConfig.getIp(), serverConfig.getPort(), serverConfig.getTransport(), viaTag);
// from
String from = StringUtils.joinWith(":", ip, port);
SipURI fromSipURI = getSipURI(device.getGbDeviceId(), from);
Address fromAddress = getAddress(fromSipURI);
FromHeader fromHeader = getFromHeader(fromAddress, fromTag);
// to
ToHeader toHeader = getToHeader(fromAddress, null);
// forwards
MaxForwardsHeader maxForwardsHeader = getMaxForwardsHeader(70);
// ceq
CSeqHeader cSeqHeader = getCSeqHeader(cSeq, Request.REGISTER);
SipFactory sipFactory = getSipFactory();
Request request = sipFactory.createMessageFactory().createRequest(requestURI, Request.REGISTER, callIdHeader,
cSeqHeader, fromHeader, toHeader, viaHeaders, maxForwardsHeader);
Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory()
.createSipURI(device.getGbDeviceId(), from));
request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
ExpiresHeader expires = sipFactory.createHeaderFactory().createExpiresHeader(sipConfig.getExpire());
request.addHeader(expires);
UserAgentHeader userAgentHeader = SipUtil.createUserAgentHeader();
request.addHeader(userAgentHeader);
return request;
}
@SneakyThrows
public static Request createRegisterRequestWithAuthorization(MockingDevice device, String ip, int port, long cSeq, String fromTag, String viaTag, CallIdHeader callIdHeader, String callId, WWWAuthenticateHeader www) {
Request request = createRegisterRequest(device, ip, port, cSeq, fromTag, viaTag, callIdHeader);
String realm = www.getRealm();
String nonce = www.getNonce();
String scheme = www.getScheme();
String qop = www.getQop();
String target = StringUtils.joinWith(":", serverConfig.getIp(), serverConfig.getPort());
SipURI requestURI = getSipURI(serverConfig.getId(), target);
String cNonce = null;
String nc = "00000001";
if (qop != null) {
if ("auth".equals(qop)) {
// 客户端随机数这是一个不透明的字符串值由客户端提供并且客户端和服务器都会使用以避免用明文文本
// 这使得双方都可以查验对方的身份并对消息的完整性提供一些保护
cNonce = UUID.randomUUID().toString();
} else if ("auth-int".equals(qop)) {
// TODO
}
}
String HA1 = DigestUtils.md5DigestAsHex((device.getGbDeviceId() + ":" + realm + ":" + serverConfig.getPassword()).getBytes());
String HA2 = DigestUtils.md5DigestAsHex((Request.REGISTER + ":" + requestURI.toString()).getBytes());
StringBuffer reStr = new StringBuffer(200);
reStr.append(HA1);
reStr.append(":");
reStr.append(nonce);
reStr.append(":");
if (qop != null) {
reStr.append(nc);
reStr.append(":");
reStr.append(cNonce);
reStr.append(":");
reStr.append(qop);
reStr.append(":");
}
reStr.append(HA2);
String response = DigestUtils.md5DigestAsHex(reStr.toString().getBytes());
AuthorizationHeader authorizationHeader = getSipFactory().createHeaderFactory().createAuthorizationHeader(scheme);
authorizationHeader.setUsername(device.getGbDeviceId());
authorizationHeader.setRealm(realm);
authorizationHeader.setNonce(nonce);
authorizationHeader.setURI(requestURI);
authorizationHeader.setResponse(response);
authorizationHeader.setAlgorithm("MD5");
if (qop != null) {
authorizationHeader.setQop(qop);
authorizationHeader.setCNonce(cNonce);
authorizationHeader.setNonceCount(1);
}
request.addHeader(authorizationHeader);
return request;
}
}

View File

@ -119,6 +119,10 @@ public class DeviceService {
return deviceMapper.updateByPrimaryKey(device) > 0;
}
public List<MockingDevice> getAllDevice(){
return deviceMapper.select(u -> u.orderBy(MockingDeviceDynamicSqlSupport.id.descending()));
}
/**
* 分页查询设备
* @param page 页数

View File

@ -0,0 +1,47 @@
package cn.skcks.docking.gb28181.mocking.service.gb28181.register;
import cn.skcks.docking.gb28181.core.sip.listener.SipListener;
import cn.skcks.docking.gb28181.core.sip.service.SipService;
import cn.skcks.docking.gb28181.core.sip.utils.SipUtil;
import cn.skcks.docking.gb28181.mocking.config.sip.SipConfig;
import cn.skcks.docking.gb28181.mocking.core.sip.request.SipRequestBuilder;
import cn.skcks.docking.gb28181.mocking.service.device.DeviceService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.sip.ListeningPoint;
import javax.sip.SipException;
import javax.sip.SipProvider;
import javax.sip.message.Request;
@Slf4j
@RequiredArgsConstructor
@Service
public class RegisterService {
private final SipConfig sipConfig;
private final SipListener sipListener;
private final SipService sipService;
private final DeviceService deviceService;
public boolean register(){
deviceService.getAllDevice().parallelStream().forEach(device -> {
sipConfig.getIp().parallelStream().forEach(ip->{
SipProvider provider = sipService.getProvider(ListeningPoint.UDP, ip);
if(provider == null){
return;
}
Request request = SipRequestBuilder.createRegisterRequest(device,ip, sipConfig.getPort(),1, SipUtil.generateFromTag(),null, provider.getNewCallId());
try {
provider.sendRequest(request);
} catch (SipException e) {
throw new RuntimeException(e);
}
});
});
return true;
}
}

View File

@ -6,6 +6,7 @@ import cn.skcks.docking.gb28181.mocking.config.sip.SipConfig;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.SmartLifecycle;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.DependsOn;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@ -16,6 +17,7 @@ import org.springframework.util.CollectionUtils;
@RequiredArgsConstructor
@Component
@DependsOn("mockingOrmInitService")
@ComponentScan("cn.skcks.docking.gb28181.core.sip.executor")
public class SipStarter implements SmartLifecycle {
private final SipService sipService;
private final SipConfig sipConfig;

View File

@ -11,6 +11,7 @@ import org.springframework.context.annotation.ComponentScan;
"cn.skcks.docking.gb28181.annotation",
"cn.skcks.docking.gb28181.common",
"cn.skcks.docking.gb28181.mocking",
"cn.skcks.docking.gb28181.core.sip.utils",
})
public class Gb28181MockingStarter {
public static void main(String[] args) {

View File

@ -48,8 +48,10 @@ gb28181:
id: 44050100002000000002
# [可选] 默认设备认证密码,后续扩展使用设备单独密码, 移除密码将不进行校验
password: 123456
expire: 3600
server:
ip: 192.168.10.241
ip: 192.168.10.32
# ip: 192.168.3.12
port: 5060
password: 123456
domain: 4405010000