403 Запрещено, используя веб -питания с пружинной загрузкойJAVA

Программисты JAVA общаются здесь
Anonymous
403 Запрещено, используя веб -питания с пружинной загрузкой

Сообщение Anonymous »

У меня есть Spring Security с JWT, реализованным в моем приложении, и пытался настроить соединение WebSocket. Первоначально я отправлял токен в заголовках соединения WebSocket с клиентом STOMP, однако я понял, что безопасность пружины, по -видимому, обрабатывает аутентификацию по -разному по сравнению с HTTP -запросами. Затем я попытался проверить, смог ли я поднять подключение и запустить, когда я установил все разрешение на конечную точку соединения WebSocketocket в цепочке фильтра, сделав это, я все еще получу 403 за ошибку. В настоящее время, если моя цепочка фильтра Spring Security позволяет подключению рукопожатия, соединение должно быть успешным, учитывая, что я уже настроил конфигурацию Spring Security Cors, чтобы принять моего клиента в качестве происхождения. Если бы я должен был гарантировать, что подключения к WebSocket разрешены пользователям, но для проверки моего токена для проверки сообщений требуется отправка сообщений для каждого сообщения, или я могу ли я просто расширить абстрактный конфигуратор WebSocketCocketMessagebroker и обеспечить все конечные точки с помощью /*, а также Applate ProtoLicate. Учитывая, что первоначальное соединение WebSocket отправляется под протоколом HTTP, даже если я должен был включить заголовок авторизации, то разве его не следует автоматически использовать мой фильтр JWT? Затем это настроило бы принцип, так что теперь вы можете настроить последующие сообщения, которые будут аутентифицированы, внедрив метод configureInbound. Однако, если я позволю рукопожатию не требовать аутентификации, мне нужно будет реализовать пользовательский интерцептор сообщений, который извлекает токен из функции отправки на стороне клиента. something like
this.stompClient.send("/app/chat/send", {"Authorization" : "Bearer " + this.token}, JSON.stringify(message))
Below is all the code

Код: Выделить всё


import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

/// this method initiates the web socket connection, when a client wants to upgrade their
/// protocol from HTTP to WebSocket
/// .  we also define the servers that can make initiate a websocket connection
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {

registry.addEndpoint("/ws")
.setAllowedOriginPatterns("http://localhost:49322")
.withSockJS();
}

/// we define the prefix of the placeholder in the url as 'app' in which this will be binded to the
/// MessageMapping annotation methods, similar to how requestMapping routed the endpoint to the specific method
/// the message broker is used to define the endpoint in which a user will be subscribed to
/// we set the user as the prefix
///
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/user");
registry.setApplicationDestinationPrefixes("/app");
}
}

< /code>

package com.example.Dormly.websocket;

import lombok.RequiredArgsConstructor;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.userdetails.UserDetails;

@org.springframework.stereotype.Controller
@RequiredArgsConstructor
public class Controller {

private final SimpMessagingTemplate simpMessagingTemplate;
///
/// The @MessageMapping is used to route all /app/placeholder destinations to their specific methods
/// we use the principal to define the user who is the sender, and via a UI action
/// we also include recipient to define who the end user is
/// we create an output message object which contains a sender and the content
/// we then send this output message to the user
/// The message object just has the recipient(to whom we send to) and the content, the sender is fetched from the principal
@MessageMapping("/chat/send")
public void sendMessage(@Payload  Message message , @AuthenticationPrincipal UserDetails user){
OutputMessage outputMessage = new OutputMessage(
message.getContent(),
user.getUsername() /// user who sent the message - the recipient will see this
);
/// the server will send back something like '/user/james/queue/chat'
simpMessagingTemplate.convertAndSendToUser(message.getRecipient(),"/queue/chat", outputMessage);

}
}
< /code>

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfiguration {

private final AuthenticationProvider authenticationProvider;
private final JwtAuthFilter jwtAuthFilter;

/**
* Configuration annotation tells spring that there is more than one bean that needs to be instantiated as a singleton
* SecurityFilterChain applies a set of filters to our HTTP requests.
*/

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
httpSecurity.
cors(Customizer.withDefaults())
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(http->http.
requestMatchers("/api/v1/Sign-up").permitAll()
.requestMatchers("/api/v1/login").permitAll()
.requestMatchers("/ws**").permitAll()
.anyRequest()
.authenticated()
)
/**
* ensure our session management remains stateless, as we authenticate once per request
*/

.sessionManagement(session->  session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)

)
/**
* when spring security intercepts the login request, the usernamepasswordfilter delegates
* this to the auth manager in which that uses the provider manager impl to find the auth provider.
* The auth provider is called to validate the credentials as it receives an auth object
* Also ensure the jwt filter gets called before the usernamepass filter
* as we always need to check if a JWT is present, if not the request is passed to other filters
* This way we handle requests for unauthenticated and authenticated users
* not authenticated(meaning no jwt) -> UsernamePassFilter gets used
* authenticated(has Jwt) -> jwtAuthFilter
*/

.authenticationProvider(authenticationProvider)
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);

return httpSecurity.build();

}
< /code>

import { Injectable } from '@angular/core';
import { TokenService } from '../auth/token/token.service';
import SockJS from 'sockjs-client';
import { Stomp } from '@stomp/stompjs';
import { Message } from '../models/Message';
import { MessagesComponent } from '../messages/messages.component';
import { BehaviorSubject } from 'rxjs';

@Injectable({
providedIn: 'root'
})
export class WebSocketApiService {
//create a subject to communicate the responses back to the messages component
private subject = new BehaviorSubject(null)
messageSubscription$ = this.subject.asObservable()

destination:string = "/user/queue/chat"
brokerURL:string = "http://localhost:8099/ws"
stompClient:any
token!:string

constructor(private tokenService:TokenService) {
this.token = this.tokenService.token as string
}
connect(){
console.log("connecting to websocket...")
const headers = { "Authorization": "Bearer " + this.token };
console.log("WebSocket headers:", headers); // Log the headers

let ws = new SockJS(this.brokerURL)
this.stompClient = Stomp.over(ws)
this.stompClient.connect({"Authorization" : `Bearer ${this.token}`}, () =>{
console.log("WebSocket connected");

///the stompclient takes 3 parameters which includes the headers and 2 callback functions,
///the frames defined the handshake agreement of protocol switches, and once the event occurs we can now subscribe to the destination

this.stompClient.subscribe(this.destination, (message:any)=>{
//the subscribe also triggers a callback which means when a user subscribes to a destination, an event of a message could be returned
this.onMessageRecieved(message)

})

},
///error callback
(error:Error | any)=>{
this.errorCallBack(error)
}
)

}
errorCallBack(error:Error):void{
console.log(error.message)

}

disconnect(){
if(this.stompClient!==null){
this.stompClient.disconnect()
console.log("disconnected")
}
setTimeout(()=>{
this.connect()
},
5000) //reconnect after 5 seconds

}

onMessageRecieved(message:any) {
if(message){
///manual deserialization with websockets.  using Json.parse() to convert the json into a javascript objecy
console.log("message recieved ", message)
this.subject.next(JSON.parse(message))
console.log("added message to the subject")
}

}

send(message:Message):void{
///when the user sends a message, our in memory message broker in spring will automatically forward it to the destination the user is subscribed to
///the user will recieve the message immediatley assuming the connection is still live - if not we persist the chat to the db
///all messages are sent with the /app prefix and users are able to send messages
///the message object includes the recieptent and the message itself.
///websocket does not serialize the object into a json like HTTP does, hence we do it manually
this.stompClient.send("/app/chat/send", {"Authorization" : "Bearer " + this.token}, JSON.stringify(message))
}

}
This is what shows up in the console in order
[Log] calling service class to connect to websocket (main.js, line 2372)
[Log] connecting to websocket... (main.js, line 2275)
[Warning] Stomp.over did not receive a factory, auto reconnect will not work. Пожалуйста, см. Https://stomp-js.github.io/api-
docs/latest/classes/stomp.html#over (@stomp_stompjs.js, line 1534)
[log] Открытие веб-сокета ... (@stomp_stompjs.js, строка 1286)
[velly] [v venzuz] (Клиент, строка 859)
[ошибка] Не удалось загрузить ресурс: сервер ответил статусом 403 () (информация, строка 0)
[log] соединение закрыто для http: // localhost: 8099/ws (@stomp_stompjs.js, line 1286)>

Подробнее здесь: https://stackoverflow.com/questions/794 ... pring-boot

Вернуться в «JAVA»