带有 Spring-boot 后端的 Flutter websocket
Posted
技术标签:
【中文标题】带有 Spring-boot 后端的 Flutter websocket【英文标题】:Flutter websocket with Spring-boot backend 【发布时间】:2019-11-16 03:11:15 【问题描述】:好的,Flutter 在食谱中有 WebSocket 配方 (here)。这对 websocket.org 测试服务器非常有效。
问题是我想连接我自己的 WebSocket 服务器。所以我首先使用了 SpringBoot 中的this tutorial。
尝试从应用程序(我在这里使用模拟器)向 Spring Boot 后端发出请求无效。然后我开始修补并从 Spring Boot 后端删除 STOMP,并留下一个简单的 WebSocket 传递字符串。它在使用邮递员甚至网页时有效,但在应用程序中无效
当前状态在这个 GitHub 上(包括 spring boot 和 flutter 项目):https://github.com/Flavsditz/websocket_sandbox
这里有人有什么建议吗?
我很感激!
【问题讨论】:
【参考方案1】:经过一番考虑,我发现了问题:
问题是我的spring-boot服务器在localhost
,但是flutter(也是android模拟器)有自己的环回服务。
所以在 Flutter 程序中调用 localhost
指的是另一个地方,而不是我想要的地方。
我已将 localhost
替换为 ip 10.0.2.2
,这是为帮助开发而设置的主机 PC 的别名。
欲了解更多信息,请查看此答案:here
当然,如果您想在真实设备上进行测试,则需要为外部发布后端,所以这个答案可能会更好:here
【讨论】:
【参考方案2】:在flutter-websocket_test/lib/message_page.dart
中,第 6-7 行有以下内容:
final WebSocketChannel channel = IOWebSocketChannel.connect(
Uri(scheme: "ws", host: "locahost", port: 8080, path: "/socket"),
您使用的是locahost
而不是localhost
,因此请尝试更改它,看看它是否有效。
【讨论】:
感谢您指出这一点,但这确实只是我准备示例时的一个错字。【参考方案3】:感谢您的解决方案,如果您想在真实设备中进行测试,请补充说明,
真实设备和电脑都必须在同一个网络中。 (在我的情况下,我使用从手机到电脑的热点)
使用 cmd 从电脑获取 IP,输入 ipconfig 获取 IP。
(在我的情况下是IPv4 Address. . . . . . . . . . . : 192.168.43.423
)
现在粘贴您的 IP 而不是 localhost 例如。
IOWebSocketChannel.connect(Uri(scheme: "ws",host: "192.168.43.423",port: 8080,path: "/socket"))
谢谢
【讨论】:
【参考方案4】:对于那些使用
stomp_dart_client: ^0.3.7
用sockjs,记得把token传给header
initClient() async
try
if (_stompClient != null && _stompClient.connected)
return;
SharedPreferences _prefs = await SharedPreferences.getInstance();
String token = _prefs.getString('access_token');
User currentUser = User.fromPrefJson(jsonDecode(_prefs.get('current_user')));
phone = currentUser.phone;
if (token != null)
String requestUrl = '$baseUrl/websocket/tracker?access_token=$token'; // please note <<<<<
StompClient stompClient = StompClient(
config: StompConfig.SockJS(
url: requestUrl,
stompConnectHeaders:
'Authorization' : 'Bearer $token', // please note <<<<<
,
webSocketConnectHeaders:
'Authorization' : 'Bearer $token', // please note <<<<<
,
onStompError: (StompFrame frame)
print('A stomp error occurred in web socket connection :: $frame.body');
,
onWebSocketError: (dynamic frame)
print('A Web socket error occurred in web socket connection :: $frame.toString()');
,
onDebugMessage: (dynamic frame)
print('A debug error occurred in web socket connection :: $frame.toString()');
,
onConnect: (StompClient client, StompFrame connectFrame)
print('$client.toString() connected with the following frames $connectFrame.body');
_stompClient = client;
clientUnSubscribeFn = _stompClient.subscribe(
destination: '/topic/client', headers: ,
callback: (frame)
// Received a frame for this subscription
print(frame.body);
clientController.add(frame.body);
);
)
);
stompClient.activate();
catch(e)
print('An error occurred $e.toString()');
sendClientMessage(String msg) async
if (_stompClient != null && _stompClient.connected)
_stompClient.send(
destination: '/topic/client',
body: msg,
headers:
);
别忘了更新 Spring 安全配置和 web socket 配置
@Configuration
public class WebsocketSecurityConfiguration extends AbstractSecurityWebSocketMessageBrokerConfigurer
@Override
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages)
messages
.nullDestMatcher().authenticated()
.simpDestMatchers("/topic/tracker").hasAuthority(AuthoritiesConstants.ADMIN)
.simpSubscribeDestMatchers("/topic/**").authenticated()
.simpDestMatchers("/topic/**").authenticated()
// message types other than MESSAGE and SUBSCRIBE
.simpTypeMatchers(SimpMessageType.MESSAGE, SimpMessageType.SUBSCRIBE).denyAll()
// catch all
.anyMessage().denyAll();
/**
* Disables CSRF for Websockets.
*/
@Override
protected boolean sameOriginDisabled()
return true;
// spring security configs for http
@Override
public void configure(HttpSecurity http) throws Exception
// @formatter:off
http
.csrf()
.disable()
.addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
.exceptionHandling()
.authenticationEntryPoint(problemSupport)
.accessDeniedHandler(problemSupport)
.and()
.headers()
.contentSecurityPolicy("default-src 'self'; frame-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://storage.googleapis.com; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:")
.and()
.referrerPolicy(ReferrerPolicyHeaderWriter.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN)
.and()
.featurePolicy("geolocation 'none'; midi 'none'; sync-xhr 'none'; microphone 'none'; camera 'none'; magnetometer 'none'; gyroscope 'none'; speaker 'none'; fullscreen 'self'; payment 'none'")
.and()
.frameOptions()
.deny()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/**").authenticated()
.antMatchers("/websocket/tracker").hasAnyAuthority(
AuthoritiesConstants.ADMIN, AuthoritiesConstants.MANAGER, AuthoritiesConstants.STAFF,
AuthoritiesConstants.CLIENT, AuthoritiesConstants.DRIVER
)
.antMatchers("/websocket/**").permitAll()
.httpBasic()
.and()
.apply(securityConfigurerAdapter());
// @formatter:on
干杯
【讨论】:
以上是关于带有 Spring-boot 后端的 Flutter websocket的主要内容,如果未能解决你的问题,请参考以下文章