我可以建议你实施
own authentication mechanism
而不是利用Spring Security。
什么时候
WebSocket
即将建立其使用的连接
handshake
带有
UPGRADE
要求基于此,我们的想法是使用我们自己的处理程序处理请求并在那里执行身份验证。
幸运的是,Spring Boot
RequestUpgradeStrategy
为此目的。最重要的是,基于您使用的应用服务器,Spring提供了这些策略的默认实现。正如我所用
Netty
班上的人都在吼叫
ReactorNettyRequestUpgradeStrategy
。
以下是建议的原型:
/**
* Based on {@link ReactorNettyRequestUpgradeStrategy}
*/
@Slf4j
@Component
public class BasicAuthRequestUpgradeStrategy implements RequestUpgradeStrategy {
private int maxFramePayloadLength = NettyWebSocketSessionSupport.DEFAULT_FRAME_MAX_SIZE;
private final AuthenticationService service;
public BasicAuthRequestUpgradeStrategy(AuthenticationService service) {
this.service = service;
}
@Override
public Mono<Void> upgrade(ServerWebExchange exchange, //
WebSocketHandler handler, //
@Nullable String subProtocol, //
Supplier<HandshakeInfo> handshakeInfoFactory) {
ServerHttpResponse response = exchange.getResponse();
HttpServerResponse reactorResponse = getNativeResponse(response);
HandshakeInfo handshakeInfo = handshakeInfoFactory.get();
NettyDataBufferFactory bufferFactory = (NettyDataBufferFactory) response.bufferFactory();
String originHeader = handshakeInfo.getHeaders()
.getOrigin();// you will get ws://user:pass@localhost:8080
return service.authenticate(originHeader)//returns Mono<Boolean>
.filter(Boolean::booleanValue)// filter the result
.doOnNext(a -> log.info("AUTHORIZED"))
.flatMap(a -> reactorResponse.sendWebsocket(subProtocol, this.maxFramePayloadLength, (in, out) -> {
ReactorNettyWebSocketSession session = //
new ReactorNettyWebSocketSession(in, out, handshakeInfo, bufferFactory, this.maxFramePayloadLength);
return handler.handle(session);
}))
.switchIfEmpty(Mono.just("UNATHORIZED")
.doOnNext(log::info)
.then());
}
private static HttpServerResponse getNativeResponse(ServerHttpResponse response) {
if (response instanceof AbstractServerHttpResponse) {
return ((AbstractServerHttpResponse) response).getNativeResponse();
} else if (response instanceof ServerHttpResponseDecorator) {
return getNativeResponse(((ServerHttpResponseDecorator) response).getDelegate());
} else {
throw new IllegalArgumentException("Couldn't find native response in " + response.getClass()
.getName());
}
}
}
此外,如果您在项目中对Spring安全性没有重要的逻辑依赖性,例如复杂的ACL逻辑,那么我建议您放弃它,甚至不要使用它。
原因是,我认为Spring Security违反了反应式方法,因为它的MVC遗留思维方式。它使您的应用程序与大量的额外配置和“非表面”调谐纠缠在一起,并迫使工程师维护这些配置,使其变得越来越复杂。在大多数情况下,在根本不涉及Spring安全性的情况下,事情可以非常顺利地实现。只需创建一个组件并以适当的方式使用它。
希望有帮助。