我正在使用包含STOMP/SockJS WebSocket的Spring Boot(1.3.0.BUILD-SNAPSHOT)设置RESTful Web应用程序,我打算从iOS应用程序和Web浏览器中使用它.我想使用JSON Web令牌(JWT)来保护REST请求和WebSocket接口,但我对后者有困难.
该应用程序使用Spring Security进行保护: -
@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
public WebSecurityConfiguration() {
super(true);
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("steve").password("steve").roles("USER");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.exceptionHandling().and()
.anonymous().and()
.servletApi().and()
.headers().cacheControl().and().and()
// Relax CSRF on the WebSocket due to needing direct access from apps
.csrf().ignoringAntMatchers("/ws/**").and()
.authorizeRequests()
//allow anonymous resource requests
.antMatchers("/", "/index.html").permitAll()
.antMatchers("/resources/**").permitAll()
//allow anonymous POSTs to JWT
.antMatchers(HttpMethod.POST, "/rest/jwt/token").permitAll()
// Allow anonymous …Run Code Online (Sandbox Code Playgroud) 我想了解convertAndSendToUser如何在Spring SockJS + Websocket框架中工作.
在客户端,我们将连接为
stompClient.connect(login, password, callback())
Run Code Online (Sandbox Code Playgroud)
这将导致连接请求与登录和密码的"Stomp凭证",例如,如果我们处理SessionConnectEvent http://www.sergialmar.com/2014/03/detect-websocket-connects-and-disconnects-in -spring-4 /
但我仍然不清楚这是否是服务器端向队列发送操作的"用户":
simpMessagingTemplate.convertAndSendToUser(username, "/queue/reply", message);
Run Code Online (Sandbox Code Playgroud)
我能得到的最接近的是阅读这个帖子向Spring Websocket上的特定用户发送消息,由Thanh Nguyen Van回答,但目前还不清楚.
基本上我需要做的是订阅一些客户端到同一主题,但在服务器上,发送不同的数据.客户可以提供用户标识符.
我SimpUserRegistry用来获取在线用户数(带getUserCount()).它在我的本地机器上运行良好,但在AWS EC2实例(使用Amazon Linux和Ubuntu试用)上只有弹性IP且没有负载均衡器.
EC2上的问题是,某些用户在连接时从未添加到注册表中,因此我得到错误的结果.
我有会话监听器,为SessionConnectedEvent和SessionDisconnectEvent,在这里我使用SimpUserRegistry(自动装配Autowired)来获得用户的存在.如果重要的话,我也是SimpUserRegistry一个消息传递控制器.
以下是websocket消息代理配置:
@Order(Ordered.HIGHEST_PRECEDENCE + 99)
@Configuration
@EnableWebSocketMessageBroker
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class WebSocketMessageBrokerConfig extends AbstractWebSocketMessageBrokerConfigurer {
@NonNull
private SecurityChannelInterceptor securityChannelInterceptor;
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(1);
threadPoolTaskScheduler.setThreadGroupName("cb-heartbeat-");
threadPoolTaskScheduler.initialize();
config.enableSimpleBroker("/queue/", "/topic/")
.setTaskScheduler(threadPoolTaskScheduler)
.setHeartbeatValue(new long[] {1000, 1000});
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/websocket")
.setAllowedOrigins("*")
.withSockJS();
}
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(securityChannelInterceptor);
}
}
Run Code Online (Sandbox Code Playgroud)
以下是上面配置类中使用的通道拦截器:
@Slf4j …Run Code Online (Sandbox Code Playgroud) 总结 我想通过STOMP实现websocket通信.在第一次(HTTP请求)websocket握手时验证用户,并使用此Principal稍后授权websocket消息.
问题 系统在第一次尝试连接到websocket端点(HTTP握手的时间)时对客户端进行身份验证.我的Spring安全过滤器和身份验证提供程序完成其工作并正确验证客户端.在此之后,我可以检查客户端是否获得了角色,我的身份验证对象也存储在SecurityContext中.(此时已建立websocket连接,并且已丢弃HTTP协议.)但是,从第一个websocket通信我得到Authentication对象是Anonymous,因为SecurityContextHolder以某种方式被清除,因为SecurityContextChannelInterceptor清除它.
Spring文档声明如下:http://docs.spring.io/autorepo/docs/spring-security/current/reference/htmlsingle/#websocket-authentication
WebSockets重用与WebSocket连接时在HTTP请求中找到的相同身份验证信息.这意味着HttpServletRequest上的Principal将被移交给WebSockets.如果您使用的是Spring Security,则会自动覆盖HttpServletRequest上的Principal.
我非常简单的过滤器
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
try {
Authentication authResult =
new CertAuthenticationToken(null, Arrays.asList(new SimpleGrantedAuthority("ROLE_ADMIN")));
authResult = getAuthenticationManager().authenticate(authResult);
if (authResult.isAuthenticated()) {
SecurityContextHolder.getContext().setAuthentication(authResult);
LOGGER.info("Client was authenticated.");
}
chain.doFilter(request, response);
} catch (AuthenticationException ae) {
LOGGER.error("Client was not authenticated. {}", ae);
SecurityContextHolder.clearContext();
onUnsuccessfulAuthentication((HttpServletRequest) request, (HttpServletResponse) response, ae);
throw ae;
}
}
Run Code Online (Sandbox Code Playgroud)
我非常简单的认证提供商
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
authentication.setAuthenticated(true);
return …Run Code Online (Sandbox Code Playgroud) spring spring-security spring-boot spring-messaging spring-websocket
我遵循以下教程:使用 WebSocket 构建交互式 Web 应用程序
一切都按照描述进行,并且应用程序看起来很好。我只有一个很好的控制器:
@Controller
public class GreetingController {
@MessageMapping("/hello")
@SendTo("/topic/greetings")
public Greeting greeting(HelloMessage message) throws Exception {
Thread.sleep(1000); // simulated delay
return new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!");
}
}
Run Code Online (Sandbox Code Playgroud)
还有一点配置。
现在我想为这个简单的应用程序添加安全性。我花了时间找例子,但找不到。
一般来说,我找到了教程:
预览 Spring Security WebSocket 支持
但看起来这个例子只与 spring 4 相关,而不是 spring 5。而且我不明白我应该在哪里提供凭据等。描述不够详细,无法应用于我的示例。
我发现了另外一个教程: Websocket Authentication and Authorization in Spring Ask
它看起来更清晰,但我不确定它是目前最好的解决方案。
您能建议为我的应用程序配置 spring security 的最简单方法吗?
PS我还发现了不一样但熟悉的问题How to secure websocket application [Spring boot + STOMP],但目前没有答案。
现在我有以下配置:
@Configuration
@EnableWebSocketMessageBroker
public …Run Code Online (Sandbox Code Playgroud) 我正在尝试用侦听器捕获SessionDisconnectEvent事件:
@Component
public class WebSocketDisconnectListener implements ApplicationListener {
@Override
@EventListener
public void onApplicationEvent(SessionDisconnectEvent event) {
Principal principal = event.getUser();
logger.info("websocket disconnected {}, user {}", event, principal);
if (principal != null) {
}
}
}
Run Code Online (Sandbox Code Playgroud)
但本金始终为空。所以我发现SessionDisconnectEvent有一个名为 的事件sessionId,但是我该如何使用呢sessionId?
哪里可以获得会议或校长?