当前位置:首页 > TAG信息列表 > 潺潺水叮咛什么意思

潺潺水叮咛什么意思

叮咛的意思是什么 汉字意思

前言

最近工作中需要开发前端操作远程虚拟机的功能,简称webshell。基于当前的技术栈为react+django,调研了一会发现大部分的后端实现都是django+channels来实现websocket服务。

大致看了下觉得这不够有趣,翻了翻django的官方文档发现django原生是不支持websocket的,但django3之后支持了asgi协议可以自己实现websocket服务。

于是选定gunicorn+uvicorn+asgi+websocket+django3.2+paramiko来实现webshell。

实现websocket服务

使用django自带的脚手架生成的项目会自动生成asgi.py和wsgi.py两个文件,普通应用大部分用的都是wsgi.py配合nginx部署线上服务。

webshell交易原理(webshell扫描工具)

这次主要使用asgi.py实现websocket服务的思路大致网上搜一下就能找到,主要就是实现
connect/send/receive/disconnect这个几个动作的处理方法。

这里howtoaddwebsocketstoadjangoappwithoutextradependencies(
https://jaydenwindle.com/writing/django-websockets-zero-dependencies/)就是一个很好的实例,但过于简单……

思路


#asgi.py
importos

fromdjango.core.asgiimportget_asgi_application
fromwebsocket_app.websocketimportwebsocket_application

os.environ.setdefault(\'django_settings_module\',\'websocket_app.settings\')

django_application=get_asgi_application()

asyncdefapplication(scope,receive,send):
ifscope[\'type\']==\'http\':
awaitdjango_application(scope,receive,send)
elifscope[\'type\']==\'websocket\':
awaitwebsocket_application(scope,receive,send)
else:
raisenotimplementederror(f\"unknownscopetype{scope[\'type\']}\")

#websocket.py
asyncdefwebsocket_application(scope,receive,send):
pass


#websocket.py
asyncdefwebsocket_application(scope,receive,send):
whiletrue:
event=awaitreceive()

ifevent[\'type\']==\'websocket.connect\':
awaitsend({
\'type\':\'websocket.accept\'
})

ifevent[\'type\']==\'websocket.disconnect\':
break

ifevent[\'type\']==\'websocket.receive\':
ifevent[\'text\']==\'ping\':
awaitsend({
\'type\':\'websocket.send\',
\'text\':\'pong!\'
})实现

上面的代码提供了思路,比较完整的可以参考这里websockets-in-django-3-1(
https://aliashkevich.com/websockets-in-django-3-1/)基本可以复用了。

其中最核心的实现部分我放下面:


classwebsocket:
def__init__(self,scope,receive,send):
self._scope=scope
self._receive=receive
self._send=send
self._client_state=state.connecting
self._app_state=state.connecting

@property
defheaders(self):
returnheaders(self._scope)

@property
defscheme(self):
returnself._scope[\"scheme\"]

@property
defpath(self):
returnself._scope[\"path\"]

@property
defquery_params(self):
returnqueryparams(self._scope[\"query_string\"].decode())

@property
defquery_string(self)->str:
returnself._scope[\"query_string\"]

@property
defscope(self):
returnself._scope

asyncdefaccept(self,subprotocol:str=none):
\"\"\"acceptconnection.
:paramsubprotocol:thesubprotocoltheserverwishestoaccept.
:typesubprotocol:str,optional
\"\"\"
ifself._client_state==state.connecting:
awaitself.receive()
awaitself.send({\"type\":sendevent.accept,\"subprotocol\":subprotocol})

asyncdefclose(self,code:int=1000):
awaitself.send({\"type\":sendevent.close,\"code\":code})

asyncdefsend(self,message:t.mapping):
ifself._app_state==state.disconnected:
raiseruntimeerror(\"websocketisdisconnected.\")

ifself._app_state==state.connecting:
assertmessage[\"type\"]in{sendevent.accept,sendevent.close},(
\'couldnotwriteevent\"%s\"intosocketinconnectingstate.\'
%message[\"type\"]
)
ifmessage[\"type\"]==sendevent.close:
self._app_state=state.disconnected
else:
self._app_state=state.connected

elifself._app_state==state.connected:
assertmessage[\"type\"]in{sendevent.send,sendevent.close},(
\'connectedsocketcansend\"%s\"and\"%s\"events,not\"%s\"\'
%(sendevent.send,sendevent.close,message[\"type\"])
)
ifmessage[\"type\"]==sendevent.close:
self._app_state=state.disconnected

awaitself._send(message)

asyncdefreceive(self):
ifself._client_state==state.disconnected:
raiseruntimeerror(\"websocketisdisconnected.\")

message=awaitself._receive()

ifself._client_state==state.connecting:
assertmessage[\"type\"]==receiveevent.connect,(
\'websocketisinconnectingstatebutreceived\"%s\"event\'
%message[\"type\"]
)
self._client_state=state.connected

elifself._client_state==state.connected:
assertmessage[\"type\"]in{receiveevent.receive,receiveevent.disconnect},(
\'websocketisconnectedbutreceivedinvalidevent\"%s\".\'
%message[\"type\"]
)
ifmessage[\"type\"]==receiveevent.disconnect:
self._client_state=state.disconnected

returnmessage缝合怪

做为合格的代码搬运工,为了提高搬运效率还是要造点轮子填点坑的,如何将上面的websocket类与paramiko结合起来,实现从前端接受字符传递给远程主机,并同时接受返回呢?


importasyncio
importtraceback
importparamiko
fromwebshell.sshimportbase,remotessh
fromwebshell.connectionimportwebsocket

classwebshell:
\"\"\"整理websocket和paramiko.channel,实现两者的数据互通\"\"\"

def__init__(self,ws_session:websocket,
ssh_session:paramiko.sshclient=none,
chanel_session:paramiko.channel=none
):
self.ws_session=ws_session
self.ssh_session=ssh_session
self.chanel_session=chanel_session

definit_ssh(self,host=none,port=22,user=\"admin\",passwd=\"admin@123\"):
self.ssh_session,self.chanel_session=remotessh(host,port,user,passwd).session()

defset_ssh(self,ssh_session,chanel_session):
self.ssh_session=ssh_session
self.chanel_session=chanel_session

asyncdefready(self):
awaitself.ws_session.accept()

asyncdefwelcome(self):
#展示linux欢迎相关内容
foriinrange(2):
ifself.chanel_session.send_ready():
message=self.chanel_session.recv(2048).decode(\'utf-8\')
ifnotmessage:
return
awaitself.ws_session.send_text(message)

asyncdefweb_to_ssh(self):
#print(\'--------web_to_ssh------->\')
whiletrue:
#print(\'--------------->\')
ifnotself.chanel_session.activeornotself.ws_session.status:
return
awaitasyncio.sleep(0.01)
shell=awaitself.ws_session.receive_text()
#print(\'-------shell-------->\',shell)
ifself.chanel_session.activeandself.chanel_session.send_ready():
self.chanel_session.send(bytes(shell,\'utf-8\'))
#print(\'--------------->\',\"end\")

asyncdefssh_to_web(self):
#print(\'<--------ssh_to_web-----------\')
whiletrue:
#print(\'<-------------------\')
ifnotself.chanel_session.active:
awaitself.ws_session.send_text(\'sshclosed\')
return
ifnotself.ws_session.status:
return
awaitasyncio.sleep(0.01)
ifself.chanel_session.recv_ready():
message=self.chanel_session.recv(2048).decode(\'utf-8\')
#print(\'<---------message----------\',message)
ifnotlen(message):
continue
awaitself.ws_session.send_text(message)
#print(\'<-------------------\',\"end\")

asyncdefrun(self):
ifnotself.ssh_session:
raiseexception(\"sshnotinit!\")
awaitself.ready()
awaitasyncio.gather(
self.web_to_ssh(),
self.ssh_to_web()
)

defclear(self):
try:
self.ws_session.close()
exceptexception:
traceback.print_stack()
try:
self.ssh_session.close()
exceptexception:
traceback.print_stack()前端

xterm.js完全满足,搜索下找个看着简单的就行。


exportclasstermextendsreact.component{
privateterminal!:htmldivelement;
privatefitaddon=newfitaddon();

componentdidmount(){
constxterm=newterminal();
xterm.loadaddon(this.fitaddon);
xterm.loadaddon(newweblinksaddon());

//usingwssforhttps
//constsocket=newwebsocket(\"ws://\"+window.location.host+\"/api/v1/ws\");
constsocket=newwebsocket(\"ws://localhost:8000/webshell/\");
//socket.onclose=(event)=>{
//this.props.onclose();
//}
socket.onopen=(event)=>{
xterm.loadaddon(newattachaddon(socket));
this.fitaddon.fit();
xterm.focus();
}

xterm.open(this.terminal);
xterm.onresize(({cols,rows})=>{
socket.send(\"<resize>\"+cols+\",\"+rows)
});

window.addeventlistener(\'resize\',this.onresize);
}

componentwillunmount(){
window.removeeventlistener(\'resize\',this.onresize);
}

onresize=()=>{
this.fitaddon.fit();
}

render(){
return<divclassname=\"terminal\"ref={(ref)=>this.terminal=refashtmldivelement}></div>;
}
}
上邦文化旅游网 山东自考之家

  • 关注微信关注微信

猜你喜欢

微信公众号