From ab4b621ad7c1def27c64d1a9871df4b94943ca41 Mon Sep 17 00:00:00 2001 From: lmc Date: Mon, 28 Oct 2024 09:26:34 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AE=BE=E5=A4=87=E5=AF=B9=E6=8E=A5=EF=BC=8C?= =?UTF-8?q?=E5=8F=8A=20=E5=8D=95=E7=82=B9=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- RuoYi-Vue-Oracle/pom.xml | 15 + .../main/java/com/ruoyi/RuoYiApplication.java | 6 + .../com/ruoyi/common/OAuth/OauthDemo.java | 209 ++++++++++++++ .../java/com/ruoyi/common/OAuth/RestDemo.java | 152 ++++++++++ .../com/ruoyi/common/sms/forest/MyClient.java | 11 + .../main/java/com/ruoyi/common/sms/sms.java | 19 ++ .../java/com/ruoyi/common/task/EquTask.java | 43 +++ .../common/task/forest/MyTaskClient.java | 19 ++ ...rTcpNettyChannelInboundHandlerAdapter.java | 129 +++++++++ .../tcp/CarTcpNettyChannelInitializer.java | 25 ++ .../ruoyi/common/tcp/CarTcpNettyServer.java | 48 ++++ .../outside/controller/OutSideController.java | 1 + .../system/controller/BbcController.java | 268 ++++++++++++++++++ .../system/lib/banboocloud_Codec-0.0.jar | Bin 0 -> 51242 bytes .../system/utils/BamboocloudUtils.java | 84 ++++++ .../com/business/message/forest/MyClient.java | 13 + .../message/tio/utils/SpringUtil.java | 55 ++++ 17 files changed, 1097 insertions(+) create mode 100644 RuoYi-Vue-Oracle/src/main/java/com/ruoyi/common/OAuth/OauthDemo.java create mode 100644 RuoYi-Vue-Oracle/src/main/java/com/ruoyi/common/OAuth/RestDemo.java create mode 100644 RuoYi-Vue-Oracle/src/main/java/com/ruoyi/common/sms/forest/MyClient.java create mode 100644 RuoYi-Vue-Oracle/src/main/java/com/ruoyi/common/sms/sms.java create mode 100644 RuoYi-Vue-Oracle/src/main/java/com/ruoyi/common/task/EquTask.java create mode 100644 RuoYi-Vue-Oracle/src/main/java/com/ruoyi/common/task/forest/MyTaskClient.java create mode 100644 RuoYi-Vue-Oracle/src/main/java/com/ruoyi/common/tcp/CarTcpNettyChannelInboundHandlerAdapter.java create mode 100644 RuoYi-Vue-Oracle/src/main/java/com/ruoyi/common/tcp/CarTcpNettyChannelInitializer.java create mode 100644 RuoYi-Vue-Oracle/src/main/java/com/ruoyi/common/tcp/CarTcpNettyServer.java create mode 100644 RuoYi-Vue-Oracle/src/main/java/com/ruoyi/project/system/controller/BbcController.java create mode 100644 RuoYi-Vue-Oracle/src/main/java/com/ruoyi/project/system/lib/banboocloud_Codec-0.0.jar create mode 100644 RuoYi-Vue-Oracle/src/main/java/com/ruoyi/project/system/utils/BamboocloudUtils.java create mode 100644 message/message/src/main/java/com/business/message/forest/MyClient.java create mode 100644 message/message/src/main/java/com/business/message/tio/utils/SpringUtil.java diff --git a/RuoYi-Vue-Oracle/pom.xml b/RuoYi-Vue-Oracle/pom.xml index 0cddcaf..19f49d6 100644 --- a/RuoYi-Vue-Oracle/pom.xml +++ b/RuoYi-Vue-Oracle/pom.xml @@ -48,6 +48,13 @@ spring-boot-starter + + + io.netty + netty-all + 4.1.63.Final + + org.springframework.boot @@ -80,6 +87,14 @@ spring-boot-starter-security + + + + com.dtflys.forest + forest-spring-boot-starter + 1.5.36 + + org.springframework.boot diff --git a/RuoYi-Vue-Oracle/src/main/java/com/ruoyi/RuoYiApplication.java b/RuoYi-Vue-Oracle/src/main/java/com/ruoyi/RuoYiApplication.java index 32eb6f1..7ac0620 100644 --- a/RuoYi-Vue-Oracle/src/main/java/com/ruoyi/RuoYiApplication.java +++ b/RuoYi-Vue-Oracle/src/main/java/com/ruoyi/RuoYiApplication.java @@ -1,5 +1,6 @@ package com.ruoyi; +import com.ruoyi.common.tcp.CarTcpNettyServer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; @@ -16,6 +17,11 @@ public class RuoYiApplication { // System.setProperty("spring.devtools.restart.enabled", "false"); SpringApplication.run(RuoYiApplication.class, args); + try { + new CarTcpNettyServer().bind(8001); + } catch (Exception e) { + e.printStackTrace(); + } System.out.println("(♥◠‿◠)ノ゙ 若依启动成功 ლ(´ڡ`ლ)゙ \n" + " .-------. ____ __ \n" + " | _ _ \\ \\ \\ / / \n" + diff --git a/RuoYi-Vue-Oracle/src/main/java/com/ruoyi/common/OAuth/OauthDemo.java b/RuoYi-Vue-Oracle/src/main/java/com/ruoyi/common/OAuth/OauthDemo.java new file mode 100644 index 0000000..1eb540c --- /dev/null +++ b/RuoYi-Vue-Oracle/src/main/java/com/ruoyi/common/OAuth/OauthDemo.java @@ -0,0 +1,209 @@ +package com.ruoyi.common.OAuth; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.*; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLEncoder; + +/** + * Author:Zhenggang + * CreateTime:2018/10/30 18:22 + */ +@Controller +public class OauthDemo { + + /** + * 认证地址、应用注册id、应用注册key 三个参数因环境不同而不同,建议改为从配置文件中读取 + */ + //认证地址 + public static final String BASE_URL = "http://utuum.sd-gold.com:7021/idp/oauth2"; + //应用注册id + public static final String CLIENT_ID = "ERM"; + //应用注册key + public static final String CLIENT_SECRET = "ermsecret"; + + //获取access_token的url + public static final String GET_ACCESS_TOKEN_URL = BASE_URL + "/getToken"; + //获取用户信息的url + public static final String GET_USERINFO_URL = BASE_URL + "/getUserInfo?client_id=" + CLIENT_ID + "&access_token="; + + + /** + * 访问ip:port/root/redirectToAuth时,拼接并且重定向到 + * http://utuum.sd-gold.com:7021/idp/oauth2/authorize?redirect_uri=ip:port/root/getAccountName&state=sso&client_id=ECD&response_type=code + */ + @RequestMapping("/redirectToAuth") + public void reToAuth(HttpServletRequest request, HttpServletResponse response) { + String url = request.getRequestURL().toString().replaceAll("/redirectToAuth", "/getAccountName"); + String re_url = BASE_URL + "/authorize?redirect_uri=" + url + "&state=sso&client_id=" + CLIENT_ID + "&response_type=code"; + try { + response.sendRedirect(re_url); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * 此方法最后取到账号acc的值后,需要各系统进行登录逻辑处理 + * @param code 用户名和密码认证通过后返回的code,access_token,从而获取到用户或账号信息 + * @return + */ + @ResponseBody + @RequestMapping(value = "/getAccountName", method = RequestMethod.GET) + public String getAccountName(@RequestParam(name = "code") String code) { + String accessTokenParam = null; + System.out.println("1).authorize code is:" + code); + try { + accessTokenParam = "client_id=" + URLEncoder.encode(this.CLIENT_ID, "UTF-8"); + accessTokenParam += "&client_secret=" + URLEncoder.encode(this.CLIENT_SECRET, "UTF-8"); + accessTokenParam += "&grant_type=" + URLEncoder.encode("authorization_code", "UTF-8"); + accessTokenParam += "&code=" + URLEncoder.encode(code, "UTF-8"); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + String TokenString = getStringPost(this.GET_ACCESS_TOKEN_URL, accessTokenParam); + + if (TokenString == null || TokenString.equals("")) { + System.out.println("cannot get tokenInfo"); + return null; + } + System.out.println("2).tokenInfo is: " + TokenString); + String accessToken = getValueFromJson(TokenString, "access_token"); + String addressParam = this.GET_USERINFO_URL + accessToken; + String userInfo = getStringGet(addressParam); + if (userInfo == null || userInfo.equals("")) { + System.out.println("cannot get userInfo"); + return null; + } + System.out.println("3).userInfo is :" + userInfo); + String acc = getValueFromJson(userInfo, "spRoleList"); + if (acc == null || acc.equals("")) { + System.out.println("cannot get acc"); + return null; + } + System.out.println("the acc is :" + acc); + return "the acc is : " + acc; + } + + + public String getStringPost(String address, String content) { + StringBuffer buffer = new StringBuffer(); + DataOutputStream out = null; + BufferedReader reader = null; + try { + URL url = new URL(address); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setConnectTimeout(20000);//设置超时时间 + conn.setDoOutput(true);//设置连接是否可输出数据 + conn.setRequestMethod("POST"); + conn.setUseCaches(false); + conn.setInstanceFollowRedirects(true); + + conn.connect(); + out = new DataOutputStream(conn.getOutputStream()); + + out.writeBytes(content); + out.flush(); + out.close(); + System.out.println("返回代码:" + conn.getResponseCode()); + if (conn.getResponseCode() == 200) { + reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8")); + String line = ""; + while ((line = reader.readLine()) != null) { + buffer.append(line); + } + reader.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + + if (out != null) { + try { + out.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return buffer.toString(); + } + + public String getStringGet(String address) { + StringBuffer buffer = new StringBuffer(); + BufferedReader reader = null; + try { + URL url = new URL(address); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.connect(); + reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8")); + String line = ""; + while ((line = reader.readLine()) != null) { + buffer.append(line); + } + reader.close(); + } catch (MalformedURLException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return buffer.toString(); + } + + + /** + * + * @param jsonString json字符串 + * @param key 键值 + * 此处解析json依赖jackson,可改为fastjson、gosn等 + * @return + */ + public String getValueFromJson(String jsonString, String key) { + ObjectMapper mapper = new ObjectMapper(); + String value = ""; + try { + JsonNode rootNode = mapper.readTree(jsonString); + JsonNode tempValue = rootNode.get(key); + if (tempValue == null) { + return value; + } + if (tempValue.isArray()) { + if (tempValue.size() > 0) { + value = tempValue.get(0).asText(); + } + } else { + value = tempValue.asText(); + } + } catch (IOException e) { + e.printStackTrace(); + } + return value; + } + +} diff --git a/RuoYi-Vue-Oracle/src/main/java/com/ruoyi/common/OAuth/RestDemo.java b/RuoYi-Vue-Oracle/src/main/java/com/ruoyi/common/OAuth/RestDemo.java new file mode 100644 index 0000000..772caed --- /dev/null +++ b/RuoYi-Vue-Oracle/src/main/java/com/ruoyi/common/OAuth/RestDemo.java @@ -0,0 +1,152 @@ +package com.ruoyi.common.OAuth; + + +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.servlet.http.HttpServletRequest; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Author:Zhenggang + * CreateTime:2018/11/21 13:49 + */ +@Controller +public class RestDemo { + + /** + * REST_BASE_URL、APP_ID参数建议改为可配置项 + */ + public static final String REST_BASE_URL = "http://utuum.sd-gold.com:7021/idp/restful"; + public static final String APP_ID = "COP"; + + /** + * 方法得到accountName后,需要各个系统处理登录逻辑 + * @param username + * @param password + * @param httpRequest + * @return + */ + @ResponseBody + @RequestMapping(value = "/userLogin", method = RequestMethod.POST) + public Map login(@RequestParam(name = "username") String username, + @RequestParam(name = "password") String password, + HttpServletRequest httpRequest) { + String authorizeParam = null; + Map resultMap = new HashMap<>(); + resultMap.put("success", false); + String remoteIp = ""; + remoteIp = getRemotIpAddr(httpRequest); + // remoteIp = "10.8.41.156"; + System.out.println("remoteIp is:" + remoteIp); + + String error = null; + //认证用户,通过后得到tokenId + authorizeParam = "appId=" + APP_ID + "&userName=" + username + "&password=" + password + "&remoteIp=" + remoteIp; + String tokenJson = getStringGet(REST_BASE_URL + "/IDPAuthenticate?" + authorizeParam); + + JSONObject tokenJsonObject = JSONObject.parseObject(tokenJson).getJSONObject("data"); + if (tokenJsonObject == null) { + error = JSONObject.parseObject(tokenJson).getJSONObject("message").toString(); + System.out.println(error); + resultMap.put("error", error); + return resultMap; + } + String tokenId = ""; + if (tokenJsonObject.containsKey("tokenId")) { + tokenId = tokenJsonObject.getString("tokenId"); + } + System.out.println("1.----tokenid is-----:" + tokenId); + //利用tokenId获取用户信息 + String userInfoParam = "appId=" + APP_ID + "&tokenId=" + tokenId + "&remoteIp=" + remoteIp; + String userInfoString = getStringGet(REST_BASE_URL + "/getIDPUserAttributes?" + userInfoParam); + JSONObject userInfoError = JSONObject.parseObject(userInfoString).getJSONObject("message"); + if (userInfoError != null && userInfoError.size() > 0) { + error = userInfoError.toString(); + resultMap.put("error", error); + return resultMap; + } + JSONObject userInfoJson = JSONObject.parseObject(userInfoString).getJSONObject("data"); + System.out.println("2.----userinfo is------:" + userInfoJson); + JSONArray accountList = userInfoJson.getJSONObject("attributes").getJSONArray("spRoleList"); + if (accountList.size() == 0) { + error = "account not exist"; + resultMap.put("error", error); + return resultMap; + } +// String accountName = accountList.get(0); + System.out.println("3.----username is-------- : " + "accountName"); + + resultMap.put("success", true); + resultMap.put("accessToken", tokenId); + resultMap.put("accountList", accountList); + resultMap.put("remoteIp", remoteIp); + return resultMap; + } + + + public String getStringGet(String address) { + StringBuffer buffer = new StringBuffer(); + BufferedReader reader = null; + try { + URL url = new URL(address); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.connect(); + reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8")); + String line = ""; + while ((line = reader.readLine()) != null) { + buffer.append(line); + } + reader.close(); + } catch (MalformedURLException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return buffer.toString(); + } + + + public static String getRemotIpAddr(HttpServletRequest request) { + String ip = request.getHeader("x-forwarded-for"); + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("HTTP_CLIENT_IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("HTTP_X_FORWARDED_FOR"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + return ip; + } + + +} diff --git a/RuoYi-Vue-Oracle/src/main/java/com/ruoyi/common/sms/forest/MyClient.java b/RuoYi-Vue-Oracle/src/main/java/com/ruoyi/common/sms/forest/MyClient.java new file mode 100644 index 0000000..375e957 --- /dev/null +++ b/RuoYi-Vue-Oracle/src/main/java/com/ruoyi/common/sms/forest/MyClient.java @@ -0,0 +1,11 @@ +package com.ruoyi.common.sms.forest; + +import com.dtflys.forest.annotation.Get; +import com.dtflys.forest.annotation.Request; + +public interface MyClient { + + @Get("http://localhost:8080/hello") + String simpleRequest(); + +} diff --git a/RuoYi-Vue-Oracle/src/main/java/com/ruoyi/common/sms/sms.java b/RuoYi-Vue-Oracle/src/main/java/com/ruoyi/common/sms/sms.java new file mode 100644 index 0000000..9c93220 --- /dev/null +++ b/RuoYi-Vue-Oracle/src/main/java/com/ruoyi/common/sms/sms.java @@ -0,0 +1,19 @@ +package com.ruoyi.common.sms; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.webservices.client.WebServiceTemplateBuilder; +import org.springframework.stereotype.Component; + +import java.net.HttpURLConnection; + +@Component +public class sms { + + +// @Autowired +// private WebServiceTemplateBuilder webServiceTemplateBuilder; + public void sms(String phones,String msg){ +// HttpURLConnection + } + +} diff --git a/RuoYi-Vue-Oracle/src/main/java/com/ruoyi/common/task/EquTask.java b/RuoYi-Vue-Oracle/src/main/java/com/ruoyi/common/task/EquTask.java new file mode 100644 index 0000000..ba557fe --- /dev/null +++ b/RuoYi-Vue-Oracle/src/main/java/com/ruoyi/common/task/EquTask.java @@ -0,0 +1,43 @@ +package com.ruoyi.common.task; + +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.common.task.forest.MyTaskClient; +import com.ruoyi.common.utils.sign.Md5Utils; +import com.ruoyi.project.oil.domain.monitor.ThDevice; +import com.ruoyi.project.outside.service.OutSideService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; + +@Component +public class EquTask { + + + + @Autowired + private MyTaskClient myClient; + @Autowired + private OutSideService outSideService; + /** + * 港口原有设备对接 + */ + public void equ(){ + String tokenByStr = myClient.loginRequest("admin", Md5Utils.hash("123456")); + JSONObject tokenByJson = JSONObject.parseObject(tokenByStr); + JSONObject dataByJson = tokenByJson.getJSONObject("data"); + String token = dataByJson.getString("token"); + ArrayList equLists = new ArrayList<>(); //查询 数据库 获取 港口以前的 设备信息 + equLists.forEach(e->{ + String vocByStr = myClient.selectRequest(token, e.getSn(), e.getName()); + JSONObject vocByJson = JSONObject.parseObject(vocByStr); + JSONObject vocDataByJson = vocByJson.getJSONObject("data"); + JSONArray vocByArray = vocDataByJson.getJSONArray("array"); + + }); + } + public void dateByDay(){ + outSideService.monthTask(""); + } +} diff --git a/RuoYi-Vue-Oracle/src/main/java/com/ruoyi/common/task/forest/MyTaskClient.java b/RuoYi-Vue-Oracle/src/main/java/com/ruoyi/common/task/forest/MyTaskClient.java new file mode 100644 index 0000000..3e7d0d4 --- /dev/null +++ b/RuoYi-Vue-Oracle/src/main/java/com/ruoyi/common/task/forest/MyTaskClient.java @@ -0,0 +1,19 @@ +package com.ruoyi.common.task.forest; + +import com.dtflys.forest.annotation.*; +import org.springframework.stereotype.Component; + +@Component +public interface MyTaskClient { + + @Post(url = "http://10.201.35.30:8090/user/login", + headers = { + "Content-Type: application/json" + } + ) + String loginRequest(@Body String userName,@Body String password); + @Get(url = "http://10.201.35.30:8090/enviSensor/select?page=1&pageSize=20&valid=0&type=5&sensorUId={sensorUId}&label={label}" + ) + String selectRequest(@Body String token,@Var("sensorUId") String sensorUId,@Var("label")String label); + +} diff --git a/RuoYi-Vue-Oracle/src/main/java/com/ruoyi/common/tcp/CarTcpNettyChannelInboundHandlerAdapter.java b/RuoYi-Vue-Oracle/src/main/java/com/ruoyi/common/tcp/CarTcpNettyChannelInboundHandlerAdapter.java new file mode 100644 index 0000000..c56eaf1 --- /dev/null +++ b/RuoYi-Vue-Oracle/src/main/java/com/ruoyi/common/tcp/CarTcpNettyChannelInboundHandlerAdapter.java @@ -0,0 +1,129 @@ +package com.ruoyi.common.tcp; + +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.project.outside.service.OutSideService; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.util.CharsetUtil; +import java.io.IOException; +import java.net.InetSocketAddress; +/** + * I/O数据读写处理类 + * + * @author xiaobo + */ + +public class CarTcpNettyChannelInboundHandlerAdapter extends ChannelInboundHandlerAdapter { + /** + * 从客户端收到新的数据时,这个方法会在收到消息时被调用 + * + * @param ctx + * @param msg + */ + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception, IOException { + // 这里是在前面的DelimiterBasedFrameDecoder转为了ByteBuf,验证是否是ByteBuf + if (msg instanceof ByteBuf) { + ByteBuf byteBuf = (ByteBuf) msg; + try { + String receivedData = byteBuf.toString(CharsetUtil.UTF_8); + // 接收完整数据 + handleReceivedData(receivedData); + } finally { + // 释放 ByteBuf 占用的资源 + byteBuf.release(); + // 回复消息 + ctx.writeAndFlush(Unpooled.copiedBuffer("收到over", CharsetUtil.UTF_8)); + } + } + } + private void handleReceivedData(String receivedData) { + // 数据处理 tudo 数据 存入 + OutSideService outSideService = SpringUtils.getBean(OutSideService.class); + outSideService.insertDevice(receivedData); + // 这里如果想实现spring中bean的注入,可以用geBean的方式获取 + System.out.println(receivedData); + } + /** + * 从客户端收到新的数据、读取完成时调用 + * + * @param ctx + */ + @Override + public void channelReadComplete(ChannelHandlerContext ctx) throws IOException { +// log.info("channelReadComplete"); + ctx.flush(); + } + /** + * 当出现 Throwable 对象才会被调用,即当 Netty 由于 IO 错误或者处理器在处理事件时抛出的异常时 + * + * @param ctx + * @param cause + */ + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws IOException { + cause.printStackTrace(); + ctx.close();// 抛出异常,断开与客户端的连接 + } + /** + * 客户端与服务端第一次建立连接时 执行 + * + * @param ctx + * @throws Exception + */ + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception, IOException { + super.channelActive(ctx); + ctx.channel().read(); + InetSocketAddress socket = (InetSocketAddress) ctx.channel().remoteAddress(); + String clientIp = socket.getAddress().getHostAddress(); + // 此处不能使用ctx.close(),否则客户端始终无法与服务端建立连接 + System.out.println("channelActive:" + clientIp + ctx.name()); + // 这里是向客户端发送回应 + ctx.writeAndFlush(Unpooled.copiedBuffer("收到over", CharsetUtil.UTF_8)); + } + /** + * 客户端与服务端 断连时 执行 + * + * @param ctx + * @throws Exception + */ + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception, IOException { + super.channelInactive(ctx); + InetSocketAddress socket = (InetSocketAddress) ctx.channel().remoteAddress(); + String clientIp = socket.getAddress().getHostAddress(); + // 断开连接时,必须关闭,否则造成资源浪费,并发量很大情况下可能造成宕机 + ctx.close(); +// log.info("channelInactive:{}", clientIp); + } + /** + * 服务端当read超时, 会调用这个方法 + * + * @param ctx + * @param evt + * @throws Exception + */ + @Override + public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception, IOException { + super.userEventTriggered(ctx, evt); + InetSocketAddress socket = (InetSocketAddress) ctx.channel().remoteAddress(); + String clientIp = socket.getAddress().getHostAddress(); + ctx.close();// 超时时断开连接 +// log.info("userEventTriggered:" + clientIp); + } + @Override + public void channelRegistered(ChannelHandlerContext ctx) throws Exception { +// log.info("channelRegistered"); + } + @Override + public void channelUnregistered(ChannelHandlerContext ctx) throws Exception { +// log.info("channelUnregistered"); + } + @Override + public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception { +// log.info("channelWritabilityChanged"); + } +} \ No newline at end of file diff --git a/RuoYi-Vue-Oracle/src/main/java/com/ruoyi/common/tcp/CarTcpNettyChannelInitializer.java b/RuoYi-Vue-Oracle/src/main/java/com/ruoyi/common/tcp/CarTcpNettyChannelInitializer.java new file mode 100644 index 0000000..9f75e3f --- /dev/null +++ b/RuoYi-Vue-Oracle/src/main/java/com/ruoyi/common/tcp/CarTcpNettyChannelInitializer.java @@ -0,0 +1,25 @@ +package com.ruoyi.common.tcp; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import io.netty.channel.ChannelInitializer; +import io.netty.handler.codec.DelimiterBasedFrameDecoder; +/** + * description:

通道初始化

+ * + * @author bo + * @version 1.0 + * @date 2024/2/27 16:13 + */ +public class CarTcpNettyChannelInitializer extends ChannelInitializer { + @Override + protected void initChannel(Channel ch) throws Exception { + ByteBuf delemiter = Unpooled.buffer(); + delemiter.writeBytes("$".getBytes()); + // 这里就是解决数据过长问题,而且数据是以$结尾的 + ch.pipeline().addLast(new DelimiterBasedFrameDecoder(907200, true, true, delemiter)); + // 自定义ChannelInboundHandlerAdapter + ch.pipeline().addLast(new CarTcpNettyChannelInboundHandlerAdapter()); + } +} \ No newline at end of file diff --git a/RuoYi-Vue-Oracle/src/main/java/com/ruoyi/common/tcp/CarTcpNettyServer.java b/RuoYi-Vue-Oracle/src/main/java/com/ruoyi/common/tcp/CarTcpNettyServer.java new file mode 100644 index 0000000..1b9baa0 --- /dev/null +++ b/RuoYi-Vue-Oracle/src/main/java/com/ruoyi/common/tcp/CarTcpNettyServer.java @@ -0,0 +1,48 @@ +package com.ruoyi.common.tcp; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.AdaptiveRecvByteBufAllocator; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelOption; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; +/** + * description:

netty创建的TCP

+ * + * @author bo + * @version 1.0 + * @date 2024/2/27 16:25 + */ +public class CarTcpNettyServer { + public void bind(int port) throws Exception { + // 配置服务端的NIO线程组 + // NioEventLoopGroup 是用来处理I/O操作的Reactor线程组 + // bossGroup:用来接收进来的连接,workerGroup:用来处理已经被接收的连接,进行socketChannel的网络读写, + // bossGroup接收到连接后就会把连接信息注册到workerGroup + // workerGroup的EventLoopGroup默认的线程数是CPU核数的二倍 + EventLoopGroup bossGroup = new NioEventLoopGroup(1); + EventLoopGroup workerGroup = new NioEventLoopGroup(); + try { + ServerBootstrap serverBootstrap = new ServerBootstrap(); + serverBootstrap.group(bossGroup, workerGroup) + .channel(NioServerSocketChannel.class) + // netty 默认数据包传输大小为1024字节, 设置它可以自动调整下一次缓冲区建立时分配的空间大小,避免内存的浪费 最小 初始化 最大 (根据生产环境实际情况来定) + // 使用对象池,重用缓冲区 + .option(ChannelOption.RCVBUF_ALLOCATOR, new AdaptiveRecvByteBufAllocator(64, 10496, 1048576)) + .childOption(ChannelOption.RCVBUF_ALLOCATOR, new AdaptiveRecvByteBufAllocator(64, 10496, 1048576)) + // 设置 I/O处理类,主要用于网络I/O事件,记录日志,编码、解码消息 + .childHandler(new CarTcpNettyChannelInitializer()); +// log.info("<===========netty server start success!==============>"); + // 绑定端口,同步等待成功 + ChannelFuture f = serverBootstrap.bind(port).sync(); + // 等待服务器监听端口关闭 + f.channel().closeFuture().sync(); + } finally { + // 退出,释放线程池资源 + bossGroup.shutdownGracefully(); + workerGroup.shutdownGracefully(); + } + } +} \ No newline at end of file diff --git a/RuoYi-Vue-Oracle/src/main/java/com/ruoyi/project/outside/controller/OutSideController.java b/RuoYi-Vue-Oracle/src/main/java/com/ruoyi/project/outside/controller/OutSideController.java index ad8eac1..07e152e 100644 --- a/RuoYi-Vue-Oracle/src/main/java/com/ruoyi/project/outside/controller/OutSideController.java +++ b/RuoYi-Vue-Oracle/src/main/java/com/ruoyi/project/outside/controller/OutSideController.java @@ -39,5 +39,6 @@ public class OutSideController extends BaseController { // // } + } diff --git a/RuoYi-Vue-Oracle/src/main/java/com/ruoyi/project/system/controller/BbcController.java b/RuoYi-Vue-Oracle/src/main/java/com/ruoyi/project/system/controller/BbcController.java new file mode 100644 index 0000000..e09be40 --- /dev/null +++ b/RuoYi-Vue-Oracle/src/main/java/com/ruoyi/project/system/controller/BbcController.java @@ -0,0 +1,268 @@ +package com.ruoyi.project.system.controller; + + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.banboocloud.Codec.BamboocloudFacade; +import com.ruoyi.common.utils.reflect.ReflectUtils; +import com.ruoyi.framework.web.controller.BaseController; +import com.ruoyi.project.system.domain.SysUser; +import com.ruoyi.project.system.service.ISysDeptService; +import com.ruoyi.project.system.service.ISysPostService; +import com.ruoyi.project.system.service.ISysRoleService; +import com.ruoyi.project.system.service.ISysUserService; +import com.ruoyi.project.system.utils.BamboocloudUtils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.*; + +/** + * swagger 用户测试方法 + * 加密认证 + * @author ruoyi + */ + +@RestController +@RequestMapping("/bbc/user") +public class BbcController extends BaseController { + + private String bimRequestId; + + private static Logger logger = LoggerFactory.getLogger(ReflectUtils.class); + + + @Autowired + private ISysUserService userService; + + @Autowired + private ISysRoleService roleService; + + @Autowired + private ISysDeptService deptService; + + @Autowired + private ISysPostService postService; + +// @ApiOperation("测试") + @RequestMapping() + public JSONObject bbcUser() { + + JSONObject jsonObject = new JSONObject(); + jsonObject.put("resultCode", "0"); + jsonObject.put("message", "success"); + return jsonObject; + + } + +// @ApiOperation("对象属性字段查询") + @PostMapping("/schemaService") + public String SchemaService(HttpServletRequest req, HttpServletResponse resp) { + + JSONObject jsonObject = new JSONObject(); + + // StringBuilder sb = stringBuilder(req); + String bodyparam= BamboocloudUtils.getRequestBody(req); + logger.info("json--param-->" + bodyparam); + // String bodyparam = sb.toString(); + //解密json字符传 + bodyparam = BamboocloudUtils.getPlaintext(bodyparam, "123456", "AES"); + logger.info("json--param-->" + bodyparam); + Map reqmap = (Map) JSON.parse(bodyparam); + //验证签名 + if (BamboocloudUtils.verify(reqmap, "MD5").booleanValue()) { + String username = (String) reqmap.get("bimRemoteUser"); + String password = (String) reqmap.get("bimRemotePwd"); + //判断接口中的调用名及调用密码是否正确 + if (BamboocloudUtils.checkUsernamePassword(username, password)) { +// //添加返回的对象及属性字段名 +// MapJson mapJson = new MapJson(); +// Map>> map = new HashMap<>(); +// jsonObject.put("account", mapJson.accountList()); +// jsonObject.put("organization", mapJson.organizationList()); +// jsonObject.put("role", mapJson.roleList()); +// jsonObject.put("post", mapJson.postList()); +// jsonObject.put("dict", mapJson.dictDataList()); +// jsonObject.put("bimRequestId", reqmap.get("bimRequestId")); + String mapJs = JSON.toJSONString(jsonObject); + logger.info(jsonObject.toJSONString()); + //返回加密的json字符串 + mapJs = BamboocloudFacade.encrypt(mapJs, "123456", "AES"); + //jsonObject.put(map); + // values = jsonObject.values(); + return mapJs; + } + } + jsonObject.put("bimRequestId", bimRequestId); + jsonObject.put("resultCode", "505"); + jsonObject.put("message", "连接失败,请检查连接器配置的参数"); + logger.info(jsonObject.toJSONString()); + String mapJs = JSON.toJSONString(jsonObject); + //返回加密的json字符串 + mapJs = BamboocloudFacade.encrypt(mapJs, "123456", "AES"); + return mapJs; + } + +// @ApiOperation("新增用户") +// @ApiImplicitParam(name = "user", value = "新增用户信息", dataType = "User") + @PostMapping("/UserCreateService") + @ResponseBody + public String userCreateService(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + + Map schema = new HashMap(); + + JSONObject jsonObject = new JSONObject(); + + StringBuilder sb = stringBuilder(req); + //修改多值的属性格式方便转换 + String bodyparam = sb.toString(); + bodyparam = BamboocloudUtils.getPlaintext(bodyparam, "123456", "AES"); + String p = bodyparam; + String z = "[\""; + String y = "\"]"; + bodyparam.indexOf(z); + bodyparam.indexOf(y); + while (bodyparam.contains(z)) { + p = bodyparam.substring(bodyparam.indexOf(z), bodyparam.indexOf(y) + 2).replace(z, "\"").replace(y, "\"").replace("\",\"", ","); + bodyparam = bodyparam.substring(0, bodyparam.indexOf(z)) + p + bodyparam.substring(bodyparam.indexOf(y) + 2, bodyparam.length()); + } + logger.info("json--param-->" + bodyparam); + Map reqmap = (Map) JSON.parse(bodyparam); + String username = (String) reqmap.get("bimRemoteUser"); + String password = (String) reqmap.get("bimRemotePwd"); + + if (BamboocloudUtils.checkUsernamePassword(username, password)) { + /** + * 新增用户 + */ + //获取用户创建所需的参数 +// User user = createUpdateUser(reqmap); + SysUser user = new SysUser(); + //用户创建 + userService.insertUser(user); + //获取返回给IAM连接器的唯一标识,用于后续该条数据的更新修改删除 + String uid = user.getUserId() + ""; + if (uid != null) { + schema.put("uid", uid); + schema.put("bimRequestId", reqmap.get("bimRequestId")); + schema.put("resultCode", "0"); + schema.put("message", "success"); + } else { + schema.put("bimRequestId", reqmap.get("bimRequestId")); + schema.put("resultCode", "500"); + schema.put("message", "账号创建失败"); + } + String mapJson = JSON.toJSONString(schema); + mapJson = BamboocloudFacade.encrypt(mapJson, "123456", "AES"); + return mapJson; + } + schema.put("bimRequestId", reqmap.get("bimRequestId")); + schema.put("resultCode", "500"); + schema.put("message", "账号创建失败,请检查连接器配置的参数"); + String mapJson = JSON.toJSONString(schema); + //返回加密的json字符串 + mapJson = BamboocloudFacade.encrypt(mapJson, "123456", "AES"); + return mapJson; + } + + +// @ApiOperation("修改用户") +// @ApiImplicitParam(name = "user", value = "修改用户信息", dataType = "User") + @PostMapping("/UserUpdateService") + @ResponseBody + public String userUpdateService(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + + Map schema = new HashMap(); + + JSONObject jsonObject = new JSONObject(); + + StringBuilder sb = stringBuilder(req); + + String bodyparam = sb.toString(); + + bodyparam = BamboocloudUtils.getPlaintext(bodyparam, "123456", "AES"); + //修改多值的属性格式方便转换 + String p = bodyparam; + String bdp = bodyparam; + String z = "[\""; + String y = "\"]"; + bodyparam.indexOf(z); + bodyparam.indexOf(y); + while (bodyparam.contains(z)) { + p = bodyparam.substring(bodyparam.indexOf(z), bodyparam.indexOf(y) + 2).replace(z, "\"").replace(y, "\"").replace("\",\"", ","); + bodyparam = bodyparam.substring(0, bodyparam.indexOf(z)) + p + bodyparam.substring(bodyparam.indexOf(y) + 2, bodyparam.length()); + } + logger.info("json--param-->" + bodyparam); + Map reqmap = (Map) JSON.parse(bodyparam); + String username = (String) reqmap.get("bimRemoteUser"); + String password = (String) reqmap.get("bimRemotePwd"); + if (BamboocloudUtils.checkUsernamePassword(username, password)) { + SysUser user = new SysUser(); + //获取用于更新的参数 +// user = createUpdateUser(reqmap); + //获取用于更新的唯一标识 + user.setUserId(Long.valueOf(String.valueOf(reqmap.get("bimUid")))); + //更新用户 + userService.updateUser(user); + schema.put("bimRequestId", reqmap.get("bimRequestId")); + schema.put("resultCode", "0"); + schema.put("message", "success"); + String mapJson = JSON.toJSONString(schema); + mapJson = BamboocloudFacade.encrypt(mapJson, "123456", "AES"); + logger.info("response----->" + schema); + return mapJson; + } + schema.put("bimRequestId", reqmap.get("bimRequestId")); + schema.put("resultCode", "500"); + schema.put("message", "账号更新失败,请检查连接器配置的参数"); + String mapJson = JSON.toJSONString(schema); + mapJson = BamboocloudFacade.encrypt(mapJson, "123456", "AES"); + logger.info("response----->" + schema); + return mapJson; + } + + + public StringBuilder stringBuilder(HttpServletRequest req) { + BufferedReader br = null; + StringBuilder sb = new StringBuilder(); + String str = ""; + try { + br = req.getReader(); + while ((str = br.readLine()) != null) { + sb.append(str); + } + br.close(); + } catch (IOException e) { + e.printStackTrace(); + if (br != null) + try { + br.close(); + } catch (IOException eo) { + eo.printStackTrace(); + } + } finally { + if (br != null) { + try { + br.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return sb; + } + +} + diff --git a/RuoYi-Vue-Oracle/src/main/java/com/ruoyi/project/system/lib/banboocloud_Codec-0.0.jar b/RuoYi-Vue-Oracle/src/main/java/com/ruoyi/project/system/lib/banboocloud_Codec-0.0.jar new file mode 100644 index 0000000000000000000000000000000000000000..79476d9f33100a3f679e98b63c42ab94536175af GIT binary patch literal 51242 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" + username + " password --- >" + password + " ----ok"); + if("bbcadmin".equals(username)&&"P@ssw0rd".equals(password)) { + return true; + } + else { + return false; + } + } + + public static String getPlaintext(String ciphertext, String key, String type) { + return BamboocloudFacade.decrypt(ciphertext, key, type); + } + + public static Boolean verify(Map reqmap, String type) { + Map verifymap = new TreeMap(); + StringBuffer sb = new StringBuffer(); + Iterator it = reqmap.keySet().iterator(); + while (it.hasNext()) { + String key = (String) it.next(); + verifymap.put(key, reqmap.get(key)); + } + Iterator ittree = verifymap.keySet().iterator(); + while (ittree.hasNext()) { + String key = (String) ittree.next(); + if (!"signature".equals(key)) { + sb.append(key).append("=").append(verifymap.get(key)).append("&"); + } + } + sb.deleteCharAt(sb.length() - 1); + System.out.println(reqmap.get("signature") + " now " + sb.toString()); + return BamboocloudFacade.verify(reqmap.get("signature").toString(), sb.toString(), type); + } + + public static String getRequestBody(HttpServletRequest request) { + BufferedReader br = null; + StringBuilder sb = new StringBuilder(); + String str = ""; + try { + br = request.getReader(); + while ((str = br.readLine()) != null) { + sb.append(str); + } + br.close(); + } catch (IOException e) { + e.printStackTrace(); + if (br != null) + try { + br.close(); + } catch (IOException eo) { + eo.printStackTrace(); + } + } finally { + if (br != null) { + try { + br.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return sb.toString(); + } + +} \ No newline at end of file diff --git a/message/message/src/main/java/com/business/message/forest/MyClient.java b/message/message/src/main/java/com/business/message/forest/MyClient.java new file mode 100644 index 0000000..3f79253 --- /dev/null +++ b/message/message/src/main/java/com/business/message/forest/MyClient.java @@ -0,0 +1,13 @@ +package com.business.message.forest; + +import com.dtflys.forest.annotation.Body; +import com.dtflys.forest.annotation.Post; +import org.springframework.stereotype.Component; + +@Component +public interface MyClient { + + @Post("http://218.58.79.146:13080/prod-api/outside/sensorData") + String receiveSensorData(@Body("data") String data); + +} diff --git a/message/message/src/main/java/com/business/message/tio/utils/SpringUtil.java b/message/message/src/main/java/com/business/message/tio/utils/SpringUtil.java new file mode 100644 index 0000000..9ed398d --- /dev/null +++ b/message/message/src/main/java/com/business/message/tio/utils/SpringUtil.java @@ -0,0 +1,55 @@ +package com.business.message.tio.utils; + +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; + +/** + * @author LemonZuo + * @create 2020-04-28 22:17 + */ +@Component +public class SpringUtil implements ApplicationContextAware { + private static ApplicationContext context; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + context = applicationContext; + } + + public static void set(ApplicationContext applicationContext) { + context = applicationContext; + } + + /** + * 通过字节码获取 + * @param beanClass + * @param + * @return + */ + public static T getBean(Class beanClass) { + return context.getBean(beanClass); + } + + /** + * 通过BeanName获取 + * @param beanName + * @param + * @return + */ + public static T getBean(String beanName) { + return (T) context.getBean(beanName); + } + + /** + * 通过beanName和字节码获取 + * @param name + * @param beanClass + * @param + * @return + */ + public static T getBean(String name, Class beanClass) { + return context.getBean(name, beanClass); + } +} \ No newline at end of file