How to get session id in Java Spring WebSocketStompClient?
I have WebSocketStompClient and StompSessionHandlerAdapter, which instances connect fine to websocket on my server. WebSocketStompClient use SockJsClient.
But I don't know how get session id of websocket connection. In the code with stomp session handler on client side
private class ProducerStompSessionHandler extends StompSessionHandlerAdapter {
...
#Override
public void afterConnected(StompSession session, StompHeaders connectedHeaders) {
...
}
stomp session contains session id, which is different from session id on the server.
So from this ids:
DEBUG ... Processing SockJS open frame in WebSocketClientSockJsSession[id='d6aaeacf90b84278b358528e7d96454a...
DEBUG ... DefaultStompSession - Connection established in session id=42e95c88-cbc9-642d-2ff9-e5c98fb85754
I need first session id, from WebSocketClientSockJsSession.
But I didn't found in WebSocketStompClient or SockJsClient any method to retrieve something like session id...
You can use #Header annotation to access sessionId:
#MessageMapping("/login")
public void login(#Header("simpSessionId") String sessionId) {
System.out.println(sessionId);
}
And it works fine for me without any custom interceptors
To get session id you need to define your own interceptor as below and set the session id as a custom attribute.
public class HttpHandshakeInterceptor implements HandshakeInterceptor {
#Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
Map attributes) throws Exception {
if (request instanceof ServletServerHttpRequest) {
ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
HttpSession session = servletRequest.getServletRequest().getSession();
attributes.put("sessionId", session.getId());
}
return true;
}
Now you can get the same session id in the controller class.
#MessageMapping("/message")
public void processMessageFromClient(#Payload String message, SimpMessageHeaderAccessor headerAccessor) throws Exception {
String sessionId = headerAccessor.getSessionAttributes().get("sessionId").toString();
}
There is a way to extract sockjs sessionId via Reflection API:
public void afterConnected(StompSession session, StompHeaders connectedHeaders) {
// we need another sessionId!
System.out.println("New session established : " + session.getSessionId());
DefaultStompSession defaultStompSession =
(DefaultStompSession) session;
Field fieldConnection = ReflectionUtils.findField(DefaultStompSession.class, "connection");
fieldConnection.setAccessible(true);
String sockjsSessionId = "";
try {
TcpConnection<byte[]> connection = (TcpConnection<byte[]>) fieldConnection.get(defaultStompSession);
try {
Class adapter = Class.forName("org.springframework.web.socket.messaging.WebSocketStompClient$WebSocketTcpConnectionHandlerAdapter");
Field fieldSession = ReflectionUtils.findField(adapter, "session");
fieldSession.setAccessible(true);
WebSocketClientSockJsSession sockjsSession = (WebSocketClientSockJsSession) fieldSession.get(connection);
sockjsSessionId = sockjsSession.getId(); // gotcha!
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
if (StringUtils.isBlank(sockjsSessionId)) {
throw new IllegalStateException("couldn't extract sock.js session id");
}
String subscribeLink = "/topic/auth-user" + sockjsSessionId;
session.subscribe(subscribeLink, this);
System.out.println("Subscribed to " + subscribeLink);
String sendLink = "/app/user";
session.send(sendLink, getSampleMessage());
System.out.println("Message sent to websocket server");
}
Can be seen here: tutorial
Related
I'm testing a Ocpp Server comunication using Spring Websocket. The handshake works well, I can interact with the client when a station send a message (BootNotification,StatusNotification...). But sometimes I need to send things with the server (request remote transaction, get informations, etc), without the station send first.
How can I access a open session (example: ws:localhost:8080/central/station01) with another service?
My Wesocket config:
#Configuration
#EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
#Bean
public WebSocketHandler myHandler() {
return new MyHandler();
}
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(myHandler(), "/central/**")
.addInterceptors(new HttpSessionHandshakeInterceptor());
}
}
My WebSocket Handler:
public class MyHandler extends TextWebSocketHandler implements SubProtocolCapable {
private final String[] subProtocols = {"ocpp1.6", "ocpp2.0"};
#Autowired
private ClientRepository clientRepo;
#Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
Global.id="";
Global.session="";
Global.client="Close";
System.out.print("\n Connection Close \n"+"Session: "+session.getId()+"\n");
session.getHandshakeHeaders();
System.out.print("session enabled"+session);
}
#Override
public void afterConnectionEstablished(WebSocketSession session)
throws Exception{
Global.id=session.getId();
Global.session=session.getUri().toString();
Global.client="ok";
Client aa= new Client(null,session.getId(),session.getUri().toString(),"ok","");
System.out.print("\n Connected \n"+"Session: "+session.getId()+"\n");
System.out.print(session.getUri());
}
#Override
public void handleMessage(WebSocketSession session,WebSocketMessage<?> message) throws Exception {
//WebSocketHttpHeaders expectedHeaders = new WebSocketHttpHeaders();
System.out.print("\n "+message.getPayload());
Integer id=0;
Global.ocpp=Global.ocpp+" \n "+message.getPayload().toString();
ZonedDateTime data = ZonedDateTime.now();
BootNotificationResponse stat=new BootNotificationResponse("Accepted",data.toString().substring(0,data.toString().length()-"[America/Sao_Paulo]".length()),300);
JSONArray mm=new JSONArray((message.getPayload()).toString());
id=(int )mm.get(0)+1;
// session.sendMessage(new TextMessage(message.getPayload().toString()));
// System.out.print("\n Remote: "+session.getRemoteAddress()+"\n");
JSONObject ss=new JSONObject(stat);
System.out.print(session.getHandshakeHeaders());
JSONArray ja = new JSONArray();
ja.put(3);
ja.put(mm.get(1));
//
ja.put(ss);
// System.out.print("\n"+message.getPayload()+"\n");
// System.out.print(mm.get(2)+"\n");
Client dados=new Client(null,Global.id,Global.session,Global.client,message.getPayload().toString());
clientRepo.save(dados);
if(mm.get(2).equals("Authorize")) {
JSONArray nob = new JSONArray();
JSONObject iii=new JSONObject(new Auth(new AuthorizeResponse("1233434","ddfd","Accepted")));
nob.put(3);
nob.put(mm.get(1));
nob.put(iii);
System.out.print(nob);
//[2,"4","Authorize",{"idToken":{"idToken":"111111","type":"ISO14443"},"evseId":[1]}]
session.sendMessage(new TextMessage(nob.toString()));
}
if(mm.get(2).equals("BootNotification")) {
System.out.print("Boot \n");
session.sendMessage(new TextMessage(ja.toString()));
}
}
#Override
public List<String> getSubProtocols() {
System.out.print(Arrays.asList(subProtocols));
return Arrays.asList(subProtocols);
}
}
You need to do plenty of things to send command from your side to station.
When station connect to your server, you need to keep that session
in a Map with chargepointId probably.
If you would like to send
command then use that session then use websocket instance to send
command to station.
To initiate Step-2, you need to have service or
API to initiate it.
Should you need information, follow this URL: https://github.com/ChargeTimeEU/Java-OCA-OCPP
I'm writing custom REST endpoint.
When I call my Resource I trying to update some users in keycloak by getting instanse of UserResource class.
But after updating first one user transaction in KeycloakTransactionManager is closing and after what im getting exception java.lang.IllegalStateException: cannot access delegate without a transaction.
I tried to begin transaction by:
(for example)
KeycloakSession session = ... ; //got it from context
if(!session.getTransactionManager().isActive()){
session.getTransactionManager().begin();
}
But got "java.lang.IllegalStateException: Transaction in illegal state for rolleback: FINISHED" and after that "java.lang.IllegalStateException: No transaction associated with the current thread".
// custom resource class
public class MyResource {
#Context
private HttpHeaders headers;
private MyAdminAuth auth;
private MyHelper helper;
private AppAuthManager authManager;
// I've correctly getting session and with open/active transaction
private KeycloakSession session;
public MyResource(KeycloakSession session) {
this.authManager = new AppAuthManager();
this.session = session;
}
#Path("sync-users-run")
#POST
#NoCache
#ResponseObject
public String syncUsersRun(String json) {
Gson gson = new Gson();
helper = new MyHelper(authManager, session, headers);
SyncMain sync = new SyncMain(params, helper);
String response = gson.toJson(sync.run());
return response;
}
}
private boolean updateUserInKeycloak(UserRepresentation existingUser, User newData) {
existingUser.setEmail(newData.getEmail());
existingUser.setEnabled(newData.isActive());
existingUser.setFirstName(newData.getName());
existingUser.setLastName(newData.getLastName());
try {
KeycloakSession session = helper.getSession();
UserModel user = session.users().getUserById(existingUser.getId(), helper.getRealmFromSession(session));
UserResource userResource = helper.getInstanceOfUserResource(user, MyAdminAuth.ROLE_ALLOW_SYNC_USERS);
//trying to update user but it works only for the first one
Response response = userResource.updateUser(existingUser);
if (response.getStatus() != NO_CONTENT_CODE) {
return false;
}
} catch (IllegalStateException | WebApplicationException e) {
return false;
}
return true;
}
How can I update users without keycloak admin client corretly?
Maybe exist more easy and right way?
I develop gwt client application for web and i used google app engine for server
I write the server in java
I send the requests to server with RequestBuilder
how I can sync the cookies between server and client.
every request client send to server the server create new session
I want to avoid it
in server I write code like this in every request
private void addCookies(){
String jsessionId = request.getParameter("JSESSIONID");
if (jsessionId != null && !jsessionId.isEmpty()) {
Cookie userCookie = new Cookie("JSESSIONID", jsessionId);
response.addCookie(userCookie);
} else {
String sessionId = request.getSession().getId();
Cookie userCookie = new Cookie("JSESSIONID", sessionId);
response.addCookie(userCookie);
}
}
So for the first request I created cookie with sessionId and I return the session id to client in response
In client I write code like this
public void setSessionId(String sessionId) {
Cookies.setCookie(JSESSIONID, sessionId);
}
I get the sessionId from server and add to cookie
When i send request with RequestBuilder is see like this
public void sendRequest(final BaseRequest baseReuest){
long currentTime = getApplicationServices().getTimeManager().getCurrentDate().getTime();
if(isSessionIdle()){
getBaseClientFactory().restart(true);
return;
}
if(baseReuest.getOptionsRequest().isShowLoader()){
showLoader(true);
loaderRequestsCounter++;
}
try {
String url = baseUrl + baseReuest.getMethodName();
RequestBuilder requestBuilder = new RequestBuilder(RequestBuilder.POST, url);
requestBuilder.setHeader("Content-type", "application/x-www-form-urlencoded");
baseReuest.addParam(JSESSIONID, getSessionId());
requestBuilder.sendRequest(baseReuest.getParamsAsString(), new RequestCallback() {
#Override
public void onResponseReceived(Request request, Response response) {
requestFinish(baseReuest);
int status = response.getStatusCode();
if(status == 200){
String json = response.getText();
BaseRequestResponse baseRequestResponse = baseReuest.buildResponse(json);
if(!baseRequestResponse.isSuccess()){
showFailureMessage(baseRequestResponse.getFailureResponse());
}
baseReuest.notifyResponse(baseRequestResponse);
}
else{
onErrorResponse();
}
}
#Override
public void onError(Request request, Throwable exception) {
onErrorResponse();
}
private void onErrorResponse(){
requestFinish(baseReuest);
String message = getLabels().getLabel(IGeneralInfoLabels.THERE_IS_PROBLEM_ACCESSING_THE_SERVER);
if(!isOnline()){
message = getApplicationServices().getGeneralManager().getLocalLabel().connectionNotAvailable();
}
FailureResponse failureResponse = new FailureResponse(message);
showFailureMessage(failureResponse);
baseReuest.notifyFailureResponse(failureResponse);
}
private void showFailureMessage(FailureResponse failureResponse){
if(baseReuest.getOptionsRequest().isShowMessage()){
BaseRequestManager.this.showFailureMessage(failureResponse);
}
}
});
lastRequestSentTime = currentTime;
} catch (RequestException e) {
showLoader(false);
requestFinish(baseReuest);
}
}
for example for first request the sessionId is jk7br57mo1e5 so I add the sessionId to cookie in client
but in every request the server created new sessionId why?
thank you
I need to get list of all the active Session so that I can manage them. Basically I need to manage all the logged in Users in application.
Using HttpServletRequest req I am able to get current session but
need to get all the sessions
Something like this:
public EmployeeTO getEmployeeById(int id, HttpServletRequest req) {
EmployeeTO employeeTO = null;
try{
HttpSession session = req.getSession();
HttpSessionContext httpSessionContext = session.getSessionContext();
}catch(Exception e){
e.printStackTrace();
}
return employeeTO;
}
Am using RESTFul implementation with JASS for Login
I have a screen which shows the list of all active Users. If I check one
User and click close session. I need to terminate that users session.
To do that I need to have sessions somewhere accessible.
Using the HttpServletRequest, you will be able to get only the current request's (user's) session object. But if you want to track all session objects, you can do that by implementing HttpSessionListener as shown below:
public class MyProjectSessionListenerAndInvalidator
implements HttpSessionListener {
private static Map<String,Session> sessions = new HashMap<>();
#Override
public void sessionCreated(HttpSessionEvent event) {
//add your code here,
//this will be invoked whenever there is a new session object created
//Get the newly created session
Session session = event.getSession();
//get userId or unique field from session
sessions.put(userId, session);
}
#Override
public void sessionDestroyed(HttpSessionEvent event) {
//add your code here
//this will be invoked whenever there is a new session object removed
//Get the removed session
Session session = event.getSession();
//get userId or unique field from session
sessions.remove(userId);
}
public R getSessions() {
//add code here
}
public void invalidateSession(String userId) {
//add code here
}
}
N.B.: I recommend to use getSessions() and invalidateSession() carefully.
I'd like to use websockets with at HttpServer. Here is the HttpHandler I came up with ... and the corresponding EchoServer
public class WebSocketHandler implements HttpHandler {
#Override
public void handle(final HttpExchange exchange) throws IOException {
String requestMethod = exchange.getRequestMethod();
if (requestMethod.equalsIgnoreCase("GET")) {
System.out.println("Well formed websocket Upgrade request");
/*
* HTTP/1.1 101 Switching Protocols
* Upgrade: websocket
* Connection: Upgrade
* Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
* ''Sec-WebSocket-Protocol: chat
*/
Headers responseHeaders = exchange.getResponseHeaders();
responseHeaders.set("Upgrade", "websocket");
responseHeaders.set("Connection", "Upgrade");
responseHeaders.set("Sec-WebSocket-Accept", "XXXX");
exchange.sendResponseHeaders(101, 0);
exchange.getResponseBody().write("ok".getBytes());
ServerEndpointConfig.Builder.create(EchoServer.class, "/echo").build();
} else {
System.out.println("Server: non-GET websocket upgrade request....");
exchange.sendResponseHeaders(200, 0);
exchange.getResponseBody().write("ok".getBytes());
}
}
}
The EchoServer class is:
#ServerEndpoint(value = "/echo")
public class EchoServer {
private Logger logger = Logger.getLogger(this.getClass().getName());
int id = 0;
#OnOpen
public void onOpen(Session session) {
logger.info("Connected ... " + session.getId());
}
#OnMessage
public String onMessage(String message, Session session) {
switch (message) {
case "quit":
try {
session.close(new CloseReason(CloseCodes.NORMAL_CLOSURE, "Game ended"));
} catch (IOException e) {
throw new RuntimeException(e);
}
break;
}
id++;
return String.format("%d:%s", id, message);
}
#OnClose
public void onClose(Session session, CloseReason closeReason) {
logger.info(String.format("Session %s closed because of %s", session.getId(), closeReason));
}
}
I don't have much hope that this will work because I don't think the connection IDs are exchanged.
How would you complete this code to get a functional websocket connection?
Using com.sun.net.httpserver.HttpExchange for WebSocket isn't going to work.
That API for that doesn't allow for a true upgraded connection. In other words, a connection where there is no HTTP connection encoding or encapsulation (such as chunked, or gziped, etc)
Also, your protocol handling of the WebSocket upgrade isn't valid, no browser out there will find that response to be valid and continue the upgrade.
2 examples in your code.
Sec-WebSocket-Accept must be provided, and be correctly calculated from the incoming request headers
Sec-WebSocket-Protocol must be provided, if the incoming request has one defined