第一次提交

This commit is contained in:
wyw
2024-08-08 00:31:26 +08:00
commit c202e2b63d
1819 changed files with 221890 additions and 0 deletions

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.fastbee</groupId>
<artifactId>fastbee-server</artifactId>
<version>3.8.5</version>
</parent>
<artifactId>http-server</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.fastbee</groupId>
<artifactId>iot-server-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session</artifactId>
<version>1.3.5.RELEASE</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,55 @@
package com.fastbee.http.auth;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
@Slf4j
@Component
public class BasicAuth {
@Value("${server.http.auth.user.name)")
private String user;
@Value("${server.http.auth.user.password)")
private String password;
private static final String REALM = "FastbeeBasic";
public boolean auth(ChannelHandlerContext ctx, String authHeader) {
if (authHeader != null && authHeader.startsWith("Basic ")) {
String encodedCredentials = authHeader.substring("Basic ".length());
String credentials = new String(Base64.getDecoder().decode(encodedCredentials), StandardCharsets.UTF_8);
String[] parts = credentials.split(":", 2);
if (parts.length == 2 && validateUser(parts[0], parts[1])) {
// 用户名和密码验证通过,继续处理请求
return true;
} else {
// 发送401 Unauthorized响应
sendUnauthorizedResponse(ctx);
}
} else {
// 发送401 Unauthorized响应
sendUnauthorizedResponse(ctx);
}
return false;
}
private boolean validateUser(String username, String password) {
// 实现用户验证逻辑
return username.equals(user) && password.equals(this.password);
}
public void sendUnauthorizedResponse(ChannelHandlerContext ctx) {
FullHttpResponse response = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1, HttpResponseStatus.UNAUTHORIZED,
Unpooled.copiedBuffer("Unauthorized\r\n", CharsetUtil.UTF_8));
response.headers().set(HttpHeaderNames.WWW_AUTHENTICATE, "Basic realm=\"" + REALM + "\"");
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
}

View File

@ -0,0 +1,47 @@
package com.fastbee.http.auth;
import com.fastbee.http.utils.DigestAuthUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;
@Slf4j
@Component
public class DigestAuth {
private static final String REALM = "FastbeeDigest";
private static final String NONCE = UUID.randomUUID().toString();
@Value("${server.http.auth.user.name)")
private String user;
@Value("${server.http.auth.user.password)")
private String password;
public boolean auth(ChannelHandlerContext ctx, FullHttpRequest request) throws NoSuchAlgorithmException {
if (new DigestAuthUtil().doAuthenticatePlainTextPassword(request,
password)) {
return true;
} else {
// 发送401 Unauthorized响应
sendUnauthorizedResponse(ctx);
return false;
}
}
public void sendUnauthorizedResponse(ChannelHandlerContext ctx) {
FullHttpResponse response = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1, HttpResponseStatus.UNAUTHORIZED,
Unpooled.copiedBuffer("Unauthorized\r\n", CharsetUtil.UTF_8));
response.headers().set(HttpHeaderNames.WWW_AUTHENTICATE,
"Digest realm=\"" + REALM + "\", nonce=\"" + NONCE + "\"");
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
}

View File

@ -0,0 +1,9 @@
package com.fastbee.http.handler;
import io.netty.handler.codec.http.FullHttpRequest;
import javax.servlet.http.HttpSession;
public interface IHttpReqHandler {
public void processMsg(FullHttpRequest req, HttpSession session);
}

View File

@ -0,0 +1,9 @@
package com.fastbee.http.handler;
import io.netty.handler.codec.http.FullHttpResponse;
import java.text.ParseException;
public interface IHttpResHandler {
public void processMsg(FullHttpResponse req) throws ParseException;
}

View File

@ -0,0 +1,47 @@
package com.fastbee.http.handler.req;
import com.alibaba.fastjson2.JSON;
import com.fastbee.common.core.thingsModel.ThingsModelSimpleItem;
import com.fastbee.http.service.IHttpMqttService;
import com.fastbee.http.handler.IHttpReqHandler;
import com.fastbee.http.server.HttpListener;
import com.fastbee.iot.domain.Device;
import com.fastbee.iot.service.IDeviceService;
import io.netty.handler.codec.http.FullHttpRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpSession;
import java.nio.charset.StandardCharsets;
import java.util.List;
@Slf4j
@Component
public class EventHttpReqHandler implements InitializingBean, IHttpReqHandler {
@Autowired
private HttpListener httpListener;
@Autowired
private IHttpMqttService mqttService;
@Autowired
private IDeviceService deviceService;
@Override
public void afterPropertiesSet() throws Exception {
String uri = "/event/post";
httpListener.addRequestProcessor(uri, this);
}
@Override
public void processMsg(FullHttpRequest req, HttpSession session) {
String serialNumber = (String) session.getAttribute("SerialNumber");
Device device = deviceService.selectDeviceBySerialNumber(serialNumber);
if (device != null) {
List<ThingsModelSimpleItem> thingsModelSimpleItems = JSON.parseArray(req.content().toString(StandardCharsets.UTF_8), ThingsModelSimpleItem.class);
mqttService.publishEvent(device,thingsModelSimpleItems);
}
}
}

View File

@ -0,0 +1,55 @@
package com.fastbee.http.handler.req;
import com.alibaba.fastjson2.JSON;
import com.fastbee.common.enums.DeviceStatus;
import com.fastbee.common.utils.DateUtils;
import com.fastbee.http.service.IHttpMqttService;
import com.fastbee.http.handler.IHttpReqHandler;
import com.fastbee.http.manager.HttpSessionManager;
import com.fastbee.http.server.HttpListener;
import com.fastbee.iot.domain.Device;
import com.fastbee.iot.service.IDeviceService;
import io.netty.handler.codec.http.FullHttpRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpSession;
import java.nio.charset.StandardCharsets;
@Slf4j
@Component
public class InfoHttpReqHandler implements InitializingBean, IHttpReqHandler {
@Autowired
private HttpListener httpListener;
@Autowired
private HttpSessionManager sessionManager;
@Autowired
private IHttpMqttService mqttService;
@Autowired
private IDeviceService deviceService;
@Override
public void afterPropertiesSet() throws Exception {
String uri = "/info/post";
httpListener.addRequestProcessor(uri, this);
}
@Override
public void processMsg(FullHttpRequest req, HttpSession session) {
//设备上报基本信息后保存到会话中
Device device = JSON.parseObject(req.content().toString(StandardCharsets.UTF_8), Device.class);
session.setAttribute("SerialNumber", device.getSerialNumber());
session.setAttribute("productId", device.getProductId());
device.setActiveTime(DateUtils.getNowDate());
device.setStatus(DeviceStatus.ONLINE.getType());
sessionManager.saveSession(session.getId(), session);
//更新设备信息
deviceService.updateDevice(device);
//发布到mqtt
mqttService.publishInfo(device);
}
}

View File

@ -0,0 +1,40 @@
package com.fastbee.http.handler.req;
import com.fastbee.common.utils.DateUtils;
import com.fastbee.http.handler.IHttpReqHandler;
import com.fastbee.http.server.HttpListener;
import com.fastbee.iot.domain.Device;
import com.fastbee.iot.service.IDeviceService;
import io.netty.handler.codec.http.FullHttpRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpSession;
@Slf4j
@Component
public class KeepaliveHttpReqHandler implements InitializingBean, IHttpReqHandler {
@Autowired
private HttpListener httpListener;
@Autowired
private IDeviceService deviceService;
@Override
public void processMsg(FullHttpRequest req, HttpSession session) {
String serialNumber = (String) session.getAttribute("SerialNumber");
Device device = deviceService.selectDeviceBySerialNumber(serialNumber);
if (device != null) {
device.setActiveTime(DateUtils.getNowDate());
deviceService.updateDevice(device);
}
}
@Override
public void afterPropertiesSet() throws Exception {
String uri = "/keepalive";
httpListener.addRequestProcessor(uri, this);
}
}

View File

@ -0,0 +1,46 @@
package com.fastbee.http.handler.req;
import com.alibaba.fastjson2.JSON;
import com.fastbee.common.core.thingsModel.ThingsModelSimpleItem;
import com.fastbee.http.service.IHttpMqttService;
import com.fastbee.http.handler.IHttpReqHandler;
import com.fastbee.http.server.HttpListener;
import com.fastbee.iot.domain.Device;
import com.fastbee.iot.service.IDeviceService;
import io.netty.handler.codec.http.FullHttpRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpSession;
import java.nio.charset.StandardCharsets;
import java.util.List;
@Slf4j
@Component
public class MonitorHttpReqHandler implements InitializingBean, IHttpReqHandler {
@Autowired
private HttpListener httpListener;
@Autowired
private IHttpMqttService mqttService;
@Autowired
private IDeviceService deviceService;
@Override
public void afterPropertiesSet() throws Exception {
String uri = "/monitor/post";
httpListener.addRequestProcessor(uri, this);
}
@Override
public void processMsg(FullHttpRequest req, HttpSession session) {
String serialNumber = (String) session.getAttribute("SerialNumber");
Device device = deviceService.selectDeviceBySerialNumber(serialNumber);
if (device != null) {
List<ThingsModelSimpleItem> thingsModelSimpleItems = JSON.parseArray(req.content().toString(StandardCharsets.UTF_8), ThingsModelSimpleItem.class);
mqttService.publishMonitor(device,thingsModelSimpleItems);
}
}
}

View File

@ -0,0 +1,47 @@
package com.fastbee.http.handler.req;
import com.alibaba.fastjson2.JSON;
import com.fastbee.common.core.thingsModel.ThingsModelSimpleItem;
import com.fastbee.http.service.IHttpMqttService;
import com.fastbee.http.handler.IHttpReqHandler;
import com.fastbee.http.server.HttpListener;
import com.fastbee.iot.domain.Device;
import com.fastbee.iot.service.IDeviceService;
import io.netty.handler.codec.http.FullHttpRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpSession;
import java.nio.charset.StandardCharsets;
import java.util.List;
@Slf4j
@Component
public class PropertyHttpReqHandler implements InitializingBean, IHttpReqHandler {
@Autowired
private HttpListener httpListener;
@Autowired
private IHttpMqttService mqttService;
@Autowired
private IDeviceService deviceService;
@Override
public void afterPropertiesSet() throws Exception {
String uri = "/property/post";
httpListener.addRequestProcessor(uri, this);
}
@Override
public void processMsg(FullHttpRequest req, HttpSession session) {
String serialNumber = (String) session.getAttribute("SerialNumber");
Device device = deviceService.selectDeviceBySerialNumber(serialNumber);
if (device != null) {
List<ThingsModelSimpleItem> thingsModelSimpleItems = JSON.parseArray(req.content().toString(StandardCharsets.UTF_8), ThingsModelSimpleItem.class);
mqttService.publishProperty(device, thingsModelSimpleItems, 0);
}
}
}

View File

@ -0,0 +1,54 @@
package com.fastbee.http.manager;
import com.fastbee.common.core.redis.RedisCache;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.Map;
@Slf4j
@Component
public class HttpSessionManager {
@Autowired
private RedisCache sessionStore;
private final ObjectMapper objectMapper = new ObjectMapper();
public HttpSessionManager() {
}
public String createSession() {
String sessionId = generateSessionId();
saveSession(sessionId, new NettyHttpSession(sessionId));
return sessionId;
}
public HttpSession getSession(String sessionId) {
String sessionData = sessionStore.getCacheObject("session:" + sessionId);
if (sessionData != null) {
try {
return new NettyHttpSession(sessionId, objectMapper.readValue(sessionData, Map.class));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return null;
}
public void saveSession(String sessionId, HttpSession session) {
try {
String sessionData = objectMapper.writeValueAsString(((NettyHttpSession) session).getAttributeMap());
sessionStore.setCacheObject("session:" + sessionId, sessionData);
sessionStore.expire("session:" + sessionId, 30 * 60);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private String generateSessionId() {
return Long.toHexString(Double.doubleToLongBits(Math.random()));
}
}

View File

@ -0,0 +1,117 @@
package com.fastbee.http.manager;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionContext;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
public class NettyHttpSession implements HttpSession {
private final String id;
private final Map<String, Object> attributes;
public NettyHttpSession(String id) {
this(id, new HashMap<>());
}
public NettyHttpSession(String id, Map<String, Object> attributes) {
this.id = id;
this.attributes = attributes;
}
@Override
public long getCreationTime() {
// Implement as needed
return 0;
}
@Override
public String getId() {
return id;
}
@Override
public long getLastAccessedTime() {
// Implement as needed
return 0;
}
@Override
public javax.servlet.ServletContext getServletContext() {
// Implement as needed
return null;
}
@Override
public void setMaxInactiveInterval(int interval) {
// Implement as needed
}
@Override
public int getMaxInactiveInterval() {
// Implement as needed
return 0;
}
@Override
public HttpSessionContext getSessionContext() {
return null;
}
@Override
public void invalidate() {
// Implement as needed
}
@Override
public boolean isNew() {
// Implement as needed
return false;
}
@Override
public Object getAttribute(String name) {
return attributes.get(name);
}
@Override
public Object getValue(String name) {
return attributes.get(name);
}
@Override
public Enumeration<String> getAttributeNames() {
// Implement as needed
return null;
}
@Override
public String[] getValueNames() {
// Implement as needed
return new String[0];
}
@Override
public void setAttribute(String name, Object value) {
attributes.put(name, value);
}
@Override
public void putValue(String name, Object value) {
attributes.put(name, value);
}
@Override
public void removeAttribute(String name) {
attributes.remove(name);
}
@Override
public void removeValue(String name) {
attributes.remove(name);
}
public Map<String, Object> getAttributeMap() {
return attributes;
}
}

View File

@ -0,0 +1,55 @@
package com.fastbee.http.server;
import com.fastbee.http.handler.IHttpReqHandler;
import com.fastbee.http.handler.IHttpResHandler;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpSession;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
@Component
public class HttpListener {
private static final Map<String, IHttpReqHandler> requestProcessorMap = new ConcurrentHashMap<>();
private static final Map<String, IHttpResHandler> responseProcessorMap = new ConcurrentHashMap<>();
public void addRequestProcessor(String method, IHttpReqHandler processor) {
requestProcessorMap.put(method, processor);
}
public void addResponseProcessor(String method, IHttpResHandler processor) {
responseProcessorMap.put(method, processor);
}
@Async("taskExecutor")
public void processRequest(FullHttpRequest req, HttpSession session) {
String uri = req.uri();
IHttpReqHandler sipRequestProcessor = requestProcessorMap.get(uri);
if (sipRequestProcessor == null) {
log.warn("不支持的uri:{}", uri);
return;
}
requestProcessorMap.get(uri).processMsg(req, session);
}
@Async("taskExecutor")
public void processResponse(FullHttpResponse response) {
HttpResponseStatus status = response.status();
// 响应成功
if ((status.code() >= HttpResponseStatus.OK.code()) && (status.code() < HttpResponseStatus.MULTIPLE_CHOICES.code())) {
log.info("response{},", response.content());
log.info("接收response响应status{}", status);
} else if ((status.code() >= HttpResponseStatus.CONTINUE.code()) && (status.code() < HttpResponseStatus.OK.code())) {
log.info("接收response响应status{}", status);
} else {
log.warn("接收到失败的response响应status{}", status);
}
}
}

View File

@ -0,0 +1,54 @@
package com.fastbee.http.server;
import com.fastbee.server.Server;
import io.netty.bootstrap.AbstractBootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioChannelOption;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.util.concurrent.DefaultThreadFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@Slf4j
@Component
public class HttpServer extends Server {
@Autowired
private HttpServerHandler httpServerHandler;
@Override
protected AbstractBootstrap initialize() {
bossGroup = new NioEventLoopGroup(1, new DefaultThreadFactory(config.name, Thread.MAX_PRIORITY));
workerGroup = new NioEventLoopGroup(config.workerCore, new DefaultThreadFactory(config.name, Thread.MAX_PRIORITY));
if (config.businessCore > 0) {
businessService = new ThreadPoolExecutor(config.businessCore, config.businessCore, 1L,
TimeUnit.SECONDS, new LinkedBlockingQueue<>(), new DefaultThreadFactory(config.name + "-B", true, Thread.NORM_PRIORITY));
}
return new ServerBootstrap()
.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(NioChannelOption.SO_REUSEADDR, true)
.option(NioChannelOption.SO_BACKLOG, 1024)
.childOption(NioChannelOption.SO_KEEPALIVE, true)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast(new HttpServerCodec());
p.addLast(new HttpObjectAggregator(65536));
p.addLast(httpServerHandler);
}
});
}
}

View File

@ -0,0 +1,98 @@
package com.fastbee.http.server;
import com.fastbee.http.auth.BasicAuth;
import com.fastbee.http.auth.DigestAuth;
import com.fastbee.http.manager.NettyHttpSession;
import com.fastbee.http.manager.HttpSessionManager;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.cookie.Cookie;
import io.netty.handler.codec.http.cookie.DefaultCookie;
import io.netty.handler.codec.http.cookie.ServerCookieEncoder;
import io.netty.handler.codec.http.cookie.ServerCookieDecoder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpSession;
import java.util.Set;
@Slf4j
@Component
public class HttpServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
@Autowired
private HttpSessionManager sessionManager;
@Autowired
private HttpListener httpListener;
@Autowired
private BasicAuth basicAuth;
@Autowired
private DigestAuth digestAuth;
@Value("${server.http.auth.type)")
private String authtype;
@Override
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception {
String sessionId = null;
HttpSession session;
String cookieHeader = req.headers().get(HttpHeaderNames.COOKIE);
// 使用ServerCookieDecoder解码Cookie字符串
Set<Cookie> cookies = ServerCookieDecoder.STRICT.decode(cookieHeader);
// 遍历Cookies
for (Cookie cookie : cookies) {
if ("JSESSIONID".equals(cookie.name())) {
sessionId = cookie.value();
}
}
// 未认证
if (sessionId == null) {
String authHeader = req.headers().get(HttpHeaderNames.AUTHORIZATION);
boolean check = false;
if (authHeader != null && authHeader.startsWith("Basic ")) {
if (basicAuth.auth(ctx, authHeader)) {
check = true;
}
} else if (authHeader != null && authHeader.startsWith("Digest ")) {
if (digestAuth.auth(ctx, req)) {
check = true;
}
}
if (check) {
sessionId = sessionManager.createSession();
session = sessionManager.getSession(sessionId);
((NettyHttpSession) session).setAttribute("user", "John Doe");
// 创建一个Cookie
Cookie sessionCookie = new DefaultCookie("JSESSIONID", sessionId);
// 设置一些属性,比如路径和最大年龄
sessionCookie.setPath("/");
sessionCookie.setMaxAge(30 * 60); // 30分钟
// 编码Cookie并添加到响应头中
FullHttpResponse res = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
res.headers().add(HttpHeaderNames.SET_COOKIE, ServerCookieEncoder.STRICT.encode(sessionCookie));
ctx.writeAndFlush(res);
} else {
if ("Basic".equals(authtype)) {
basicAuth.sendUnauthorizedResponse(ctx);
} else {
digestAuth.sendUnauthorizedResponse(ctx);
}
}
} else {
// 已经认证
session = sessionManager.getSession(sessionId);
// http 路由处理函数
httpListener.processRequest(req, session);
}
}
}

View File

@ -0,0 +1,18 @@
package com.fastbee.http.service;
import com.fastbee.common.core.thingsModel.ThingsModelSimpleItem;
import com.fastbee.iot.domain.Device;
import java.util.List;
public interface IHttpMqttService {
void publishInfo(Device device);
void publishStatus(Device device, int deviceStatus);
void publishEvent(Device device, List<ThingsModelSimpleItem> thingsList);
void publishProperty(Device device, List<ThingsModelSimpleItem> thingsList, int delay);
void publishMonitor(Device device, List<ThingsModelSimpleItem> thingsList);
}

View File

@ -0,0 +1,73 @@
package com.fastbee.http.service.impl;
import com.alibaba.fastjson2.JSON;
import com.fastbee.common.core.thingsModel.ThingsModelSimpleItem;
import com.fastbee.common.enums.TopicType;
import com.fastbee.common.utils.gateway.mq.TopicsUtils;
import com.fastbee.http.service.IHttpMqttService;
import com.fastbee.iot.domain.Device;
import com.fastbee.mqttclient.PubMqttClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.List;
@Slf4j
@Service
public class HttpMqttServiceImpl implements IHttpMqttService {
@Resource
private PubMqttClient mqttClient;
@Resource
private TopicsUtils topicsUtils;
@Override
public void publishInfo(Device device) {
device.setRssi(0);
device.setStatus(3);
device.setFirmwareVersion(BigDecimal.valueOf(1.0));
String topic = topicsUtils.buildTopic(device.getProductId(), device.getSerialNumber(), TopicType.DEV_INFO_POST);
mqttClient.publish(1, false, topic, JSON.toJSONString(device));
}
@Override
public void publishStatus(Device device, int deviceStatus) {
}
@Override
public void publishEvent(Device device, List<ThingsModelSimpleItem> thingsList) {
String topic = topicsUtils.buildTopic(device.getProductId(), device.getSerialNumber(), TopicType.DEV_EVENT_POST);
if (thingsList == null) {
mqttClient.publish(1, false, topic, "");
} else {
mqttClient.publish(1, false, topic, JSON.toJSONString(thingsList));
}
}
@Override
public void publishProperty(Device device, List<ThingsModelSimpleItem> thingsList, int delay) {
String pre = "";
if (delay > 0) {
pre = "$delayed/" + String.valueOf(delay) + "/";
}
String topic = topicsUtils.buildTopic(device.getProductId(), device.getSerialNumber(), TopicType.DEV_PROPERTY_POST);
if (thingsList == null) {
mqttClient.publish(1, false, topic, "");
} else {
mqttClient.publish(1, false, topic, JSON.toJSONString(thingsList));
}
}
@Override
public void publishMonitor(Device device, List<ThingsModelSimpleItem> thingsList) {
String topic = topicsUtils.buildTopic(device.getProductId(), device.getSerialNumber(), TopicType.DEV_PROPERTY_POST);
if (thingsList == null) {
mqttClient.publish(1, false, topic, "");
} else {
mqttClient.publish(1, false, topic, JSON.toJSONString(thingsList));
}
}
}

View File

@ -0,0 +1,197 @@
package com.fastbee.http.utils;
import gov.nist.core.InternalErrorHandler;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.DecimalFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Implements the HTTP digest authentication method server side functionality.
*/
@Slf4j
public class DigestAuthUtil {
private final MessageDigest messageDigest;
public static final String DEFAULT_ALGORITHM = "MD5";
public static final String DEFAULT_SCHEME = "Digest";
/** to hex converter */
private static final char[] toHex = { '0', '1', '2', '3', '4', '5', '6',
'7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
/**
* Default constructor.
* @throws NoSuchAlgorithmException
*/
public DigestAuthUtil()
throws NoSuchAlgorithmException {
messageDigest = MessageDigest.getInstance(DEFAULT_ALGORITHM);
}
public static String toHexString(byte b[]) {
int pos = 0;
char[] c = new char[b.length * 2];
for (byte value : b) {
c[pos++] = toHex[(value >> 4) & 0x0F];
c[pos++] = toHex[value & 0x0f];
}
return new String(c);
}
/**
* Generate the challenge string.
*
* @return a generated nonce.
*/
private String generateNonce() {
// Get the time of day and run MD5 over it.
Date date = new Date();
long time = date.getTime();
Random rand = new Random();
long pad = rand.nextLong();
String nonceString = (new Long(time)).toString()
+ (new Long(pad)).toString();
byte[] mdbytes = messageDigest.digest(nonceString.getBytes());
// Convert the mdbytes array into a hex string.
return toHexString(mdbytes);
}
public FullHttpResponse generateChallenge(String realm) {
try {
FullHttpResponse response = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1, HttpResponseStatus.UNAUTHORIZED,
Unpooled.copiedBuffer("Unauthorized\r\n", CharsetUtil.UTF_8));
response.headers().set(HttpHeaderNames.WWW_AUTHENTICATE,
"Digest realm=\"" + realm + "\", nonce=\"" + generateNonce()
+ "\", opaque=\"\", stale=\"FALSE\", algorithm=\"" + DEFAULT_ALGORITHM + "\"");
return response;
} catch (Exception ex) {
InternalErrorHandler.handleException(ex);
}
return null;
}
/**
* Authenticate the inbound request.
*
* @param request - the request to authenticate.
* @param hashedPassword -- the MD5 hashed string of username:realm:plaintext password.
*
* @return true if authentication succeded and false otherwise.
*/
public boolean doAuthenticateHashedPassword(FullHttpRequest request, String hashedPassword) {
String authHeader = request.headers().get(HttpHeaderNames.AUTHORIZATION);
if ( authHeader == null ) return false;
Map<String, String> params = parseDigestParameters(authHeader);
String realm = params.get("realm");
String username = params.get("username");
if ( username == null || realm == null ) {
return false;
}
String nonce = params.get("nonce");
String uri = params.get("uri");
if (uri == null) {
return false;
}
String A2 = request.method() + ":" + uri;
byte[] mdbytes = messageDigest.digest(A2.getBytes());
String HA2 = toHexString(mdbytes);
String cnonce = params.get("cnonce");
String KD = hashedPassword + ":" + nonce;
if (cnonce != null) {
KD += ":" + cnonce;
}
KD += ":" + HA2;
mdbytes = messageDigest.digest(KD.getBytes());
String mdString = toHexString(mdbytes);
String response = params.get("response");
return mdString.equals(response);
}
/**
* Authenticate the inbound request given plain text password.
*
* @param request - the request to authenticate.
* @param pass -- the plain text password.
*
* @return true if authentication succeded and false otherwise.
*/
public boolean doAuthenticatePlainTextPassword(FullHttpRequest request, String pass) {
String authHeader = request.headers().get(HttpHeaderNames.AUTHORIZATION);
if ( authHeader == null ) return false;
Map<String, String> params = parseDigestParameters(authHeader);
String realm = params.get("realm");
String username = params.get("username");
String nonce = params.get("nonce");
String uri = params.get("uri");
if (uri == null) {
return false;
}
// qop 保护质量 包含auth默认的和auth-int增加了报文完整性检测两种策略
String qop = params.get("qop");
// nonce计数器是一个16进制的数值表示同一nonce下客户端发送出请求的数量
int nc = Integer.parseInt(params.get("nc"));
String ncStr = new DecimalFormat("00000000").format(nc);
String A1 = username + ":" + realm + ":" + pass;
String A2 = request.method() + ":" + uri;
byte[] mdbytes = messageDigest.digest(A1.getBytes());
String HA1 = toHexString(mdbytes);
mdbytes = messageDigest.digest(A2.getBytes());
String HA2 = toHexString(mdbytes);
String cnonce = params.get("cnonce");
String KD = HA1 + ":" + nonce;
if (qop != null && qop.equals("auth") ) {
if (nc != -1) {
KD += ":" + ncStr;
}
if (cnonce != null) {
KD += ":" + cnonce;
}
KD += ":" + qop;
}
KD += ":" + HA2;
mdbytes = messageDigest.digest(KD.getBytes());
String mdString = toHexString(mdbytes);
String response = params.get("response");
return mdString.equals(response);
}
private Map<String, String> parseDigestParameters(String authHeader) {
Map<String, String> params = new HashMap<>();
Pattern pattern = Pattern.compile("(\\w+)=(?:\"([^\"]*)\"|([^,]+))");
Matcher matcher = pattern.matcher(authHeader);
while (matcher.find()) {
String key = matcher.group(1);
String value = matcher.group(2) != null ? matcher.group(2) : matcher.group(3);
params.put(key, value);
}
return params;
}
}