Get current user with Spring #SubscribeMapping - java

I use #SubscribeMapping in a Spring Boot application which heavily relies on WebSockets for data exchange. The application is secured with Spring Security.
Client-side I use Stomp over WebSocket:
this.socket = new WebSocket(this.socketUrl);
this.stompClient = Stomp.over(this.socket);
this.stompClient.debug = null;
this.stompClient.connect({},
function(frame) {
this.$.chartLoader.generateRequest();
}.bind(this),
function(e) {
window.setTimeout(function() {
this.connectWs();
}.bind(this), 2500);
}.bind(this)
);
this.stompClient.subscribe('/topic/chart/' + chart.id,
function(message, headers) {
this.setChartData(chart, JSON.parse(message.body));
}.bind(this), {
"id" : "" + chart.id
}
);
Server-side, how can I get the currently logged user in the annotated methods ?
#SubscribeMapping("/chart/{id}")
public void subscribeMapping(#DestinationVariable("id") final short id) {
// Here I would need the current user...
messagingTemplate.convertAndSend("/topic/chart/" + id, chartService.getChartData(account, sensor, new Date(), new Date()));
}
I have tried SecurityContextHolder.getContext().getAuthentication(), but it returns null.

You can try to add a Principal object as a method parameter. This interface is extended by Authentication which has several implementations available (Spring will inject the good one according to your configuration).
public void subscribeMapping(#DestinationVariable("id") final short id, Principal principal) {
principal.getName();
}
This article may also help you.

Related

Implementing the password owner resource flow of OAuth2 in Play (Java)

For my Bachelor's-thesis I have to implement different kinds of Authentication and Authorization with different Frameworks.
Currently im at the OAuth2 chapter and have to implement it in the Play Framework (in Java). (I have already implemented it with Spring Boot)
While researching on how to approach this Problem, so far, I could not find a lot of helpful tips.
One of the main Questions I have is: after the Client authenticatet itselfe with the users credentials and has gotten the Token, how do I best verify the Token?
Basicly: What is the Play- counterpart to the "#PreAuthorize" annotation of Spring?
Any tip or link to a helpful website is appreciated.
So I think I solved my Problem. On the off chance that someone stumbles on the same question I'm gonna post the solution here:
As written in the Play-Docs (https://www.playframework.com/documentation/2.6.x/JavaOAuth) with OAuth2 and especially with the Password flow it is pretty simple.
First you need an Authorization-Service, here the implementation is straight forward. Just implement three methods:
POST /oauth/token for passing the user-credentials and recieving an access- and refresh-token
POST /oauth/refresh for when the access-token is no longer valid. Here the refresh-token is passed and a new acces token returned
POST /oauth/check_token for authorization. Here the access Token is passed and in my case I return the role, the user has. Alternativly it would also be possible and maybe eaven better to do the authorization process in the authorization-service. For this you would need to change the "check_token" method and pass the required role.
I just generated uuids as tokens and stored them in a database. I guess one could also use for example jwts and put the needed information (for example the expiration date) in the token.
Then my main question was about the annotations. I found this
https://github.com/bekce/oauthly
and just lookt at their implementation.
You basicly just need a class and a interface:
The Interface:
#With(AuthorizationServerAuthAction.class)
#Target({ElementType.TYPE, ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
public #interface AuthorizationServerSecure {
boolean requireAdmin() default false;
boolean requirePersonnel() default false;
boolean requireGuest() default false;
}
The Class:
private WSClient ws;
private final String url = "http://localhost:9001/oauth/check_token";
#Inject
public AuthorizationServerAuthAction(WSClient ws) {
this.ws = ws;
}
private CompletionStage<JsonNode> callApi(String accessToken) {
CompletionStage<WSResponse> eventualResponse = ws.url(url).setContentType("application/x-www-form-urlencoded").setRequestTimeout(Duration.ofSeconds(10))
.addHeader("Authorization" , accessToken).post("none");
return eventualResponse.thenApply(WSResponse::asJson);
}
#Override
public CompletionStage<Result> call(Http.Context ctx) {
Optional<String> accessTokenOptional = ctx.request().header("Authorization");
JsonNode result = null;
if(!accessTokenOptional.isPresent()){
return CompletableFuture.completedFuture(unauthorized(Json.newObject()
.put("message", "No token found in header!")
));
}
CompletionStage<JsonNode> apiResponse = callApi(accessTokenOptional.get());
try {
result = apiResponse.toCompletableFuture().get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
if(result == null) {
return CompletableFuture.completedFuture(unauthorized(Json.newObject()
.put("message", "an error occurred")
));
}
String role = result.get("role").asText();
if(configuration.requireAdmin()){
if(role.equals("admin")) {
return delegate.call(ctx);
} else {
return CompletableFuture.completedFuture(unauthorized(Json.newObject()
.put("message", "The user is not authorized to perform this action!")
));
}
} else if(configuration.requirePersonnel()) {
if(role.equals("personnel") || role.equals("admin")) {
return delegate.call(ctx);
} else {
return CompletableFuture.completedFuture(unauthorized(Json.newObject()
.put("message", "The user is not authorized to perform this action!")
));
}
} else if(configuration.requireGuest()) {
if(role.equals("guest") || role.equals("personnel") || role.equals("admin")) {
return delegate.call(ctx);
} else {
return CompletableFuture.completedFuture(unauthorized(Json.newObject()
.put("message", "The user is not authorized to perform this action!")
));
}
}
return CompletableFuture.completedFuture(unauthorized(Json.newObject()
.put("message", "an error occurred")
));
}
}

Oauth2 / Password flow / check permission for a specific entity

The main data information and information in my API is linked to a project(Entity),
What is good approach for password flow: to manage specific permission linked to a project with spring security and OAuth2?
In this application you have 5 micro service:
UAA microservice : the authorization server
Catalog microservice
Order microservice
Invoice microservice
Customer microservice
Zoom permission :
Each user can have many project, and can have the permission for each project:
CAN_MANAGE_CATALOG
CAN_VIEW_CATALOG
CAN_MANAGE_ORDER
CAN_VIEW_ORDER
CAN_MANAGE_INVOICE
CAN_VIEW_INVOICE
...
I have many idea but i am not sure if i have the good approach :
USE CASE : I want to securise the endpoint :
http://catalog-service/{project_key}/catalogs
Only USER who have permission VIEW_CATALOG OR MANAGE_CATALOG for the project {project_key} can list all catalog present in the project
My first idea : USE ProjectAccessExpression with preauthorize
CatalogController.java
#Controller
public class CatalogController {
#PreAuthorize("#projectAccessExpression.hasPermission(#projectKey, 'manageCatalog', principal)" +
" or #projectAccessExpression.hasPermission(#projectKey, 'viewCatalog', principal)")
#RequestMapping(
value = "/{projectKey}/catalogs",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE
)
public #ResponseBody List<Catalog> findByProject(#PathVariable("projectKey") String projectKey) {
return catalogService.find();
}
}
ProjectAccessExpression.java
#Component
public class ProjectAccessExpression {
private RestTemplate restTemplate;
public boolean havePermission(String projectKey, String permission , String username) {
Boolean havePermission = restTemplate.getForObject(String.format("http://uaa-service/permission/check?project=%1&permission=%2&username=%3",
projectKey, permission, username
), Boolean.class);
return havePermission;
}
}
The inconvenient : is need to call UAA Service for each time
Second idea : USE USER_ROLE
With user_role
username | role
mylogin1 | SHOP1.CAN_MANAGE_CATALOG
mylogin1 | SHOP1.CAN_VIEW_CATALOG
mylogin1 | SHOP2.CAN_MANAGE_CATALOG
mylogin1 | SHOP2.CAN_VIEW_CATALOG
mylogin1 | SHOP2.CAN_MANAGE_ORDER
mylogin1 | SHOP2.CAN_VIEW_ORDER
...
SHOP1 is SHOP2 is projectKey
The inconvenient : i am not sure but if user change permission, i need to revoke all token associate
Third idea : add specific permission in authentication blob
I don't know how to do for storing...
And in controller a annotation :
#PreAuthorize("#ProjectAccessExpression.hasPermission(authentication, 'manageCatalog||viewCatalog', #projectKey)
The inconvenient : same inconvenient at second idea
It basically looks like your just trying to leverage roles with OAuth 2.0 for your project. Here is an excerpt of some spring documentation on OAuth 2.0
Mapping User Roles to Scopes: http://projects.spring.io/spring-security-oauth/docs/oauth2.html
It is sometimes useful to limit the scope of tokens not only by the scopes assigned to the client, but also according to the user's own permissions. If you use a DefaultOAuth2RequestFactory in your AuthorizationEndpoint you can set a flag checkUserScopes=true to restrict permitted scopes to only those that match the user's roles. You can also inject an OAuth2RequestFactory into the TokenEndpoint but that only works (i.e. with password grants) if you also install a TokenEndpointAuthenticationFilter - you just need to add that filter after the HTTP BasicAuthenticationFilter. Of course, you can also implement your own rules for mapping scopes to roles and install your own version of the OAuth2RequestFactory. The AuthorizationServerEndpointsConfigurer allows you to inject a custom OAuth2RequestFactory so you can use that feature to set up a factory if you use #EnableAuthorizationServer.
All this basically boils down to is that you can protect your endpoints with different scopes by mapping scopes to your own custom roles. This will allow you to get really fine grained with your security.
I found a pretty good walk-through you can use as a reference: (Obviously you'll have to configure the settings to your own use case)
https://raymondhlee.wordpress.com/2014/12/21/implementing-oauth2-with-spring-security/
This solution i use and work fine
** 1 - Load Business Logic Security when user sign **
This example find user with role persist in database, and add all role depending project. After the operation i have authentication token with
GrantedAuthority : ROLE_USER, ROLE_MANAGE_CATALOG:project1, ROLE_VIEW_PROFILE:project1, ROLE_MANAGE_PROJECT:project2, ...
#Service
public class CustomUserDetailsService implements UserDetailsService {
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Optional<User> user = userService.findByLogin(username);
if (!user.isPresent()) {
Object args[] = {username};
throw new UsernameNotFoundException(
messageSource.getMessage("user.notexist", args, "User {0} doesn't not exist", LocaleContextHolder.getLocale())
);
}
if (!user.get().isActivated()) {
//throw new UserNotActivatedException(String.format("User %s was not activated!", username));
Object args[] = {username};
throw new UserNotActivatedException(
messageSource.getMessage("user.notactivated", args, "User {0} was not activated", LocaleContextHolder.getLocale()));
}
//Here implement your proper logic
//Add busness logic security Roles
// eg ROLE_MANAGE_PROJECT:{project_key}, ROLE_MANAGE_CATALOG:{project_key}
List<Role> bRoles = projectService.getRolesForUser(username)
user.get().getRoles().addAll(
bRoles
);
UserRepositoryUserDetails userDetails = new UserRepositoryUserDetails(user.get());
return userDetails;
}
}
** 2 check security with preauthorize expression **
In this example only user who have this permission can do this operation:
ROLE_ADMIN OR
ROLE_MANAGE_PROJECT:{projectKey}
#PreAuthorize("#oauthUserAccess.hasPermission(authentication, '"+Constants.PP_MANAGE_PROJECT+"', #projectKey)")
#RequestMapping(
value="/projects/{projectKey}",
method = RequestMethod.PUT,
produces = MediaType.APPLICATION_JSON_VALUE
)
public ResponseEntity updateProject(#PathVariable("projectKey") String projectKey,#Valid #RequestBody Project project)
The OauthUserAccess class :
#Component("oauthUserAccess")
public class OauthUserAccess {
/**
* Check if it is the administrator of the application IMASTER
* #param authentication
* #param projectKey
* #return
*/
public boolean hasAdminPermission(OAuth2Authentication authentication, String projectKey) {
if(authentication.getOAuth2Request().getAuthorities().contains("ROLE_ADMIN")) return true;
return false;
}
/**
*
* #param authentication
* #param permissionType
* #param projectKey
* #return
*/
public boolean hasPermission(OAuth2Authentication authentication, String permissionType, String projectKey) {
if (!ProjectPermissionType.exist(permissionType) ||
projectKey.isEmpty() ||
!projectKey.matches(Constants.PROJECT_REGEX))
return false;
if (authentication.isClientOnly()) {
//TODO check scope permission
if(authentication.getOAuth2Request().getScope().contains(permissionType+":"+projectKey)) return true;
}
if (hasAdminPermission(authentication, projectKey)) return true;
String projectPermission = "ROLE_" + permissionType + ":" + projectKey;
String projectPermissionManage = "ROLE_" + permissionType.replace("VIEW", "MANAGE") + ":" + projectKey;
String manageProject = "ROLE_" + Constants.PP_MANAGE_PROJECT + ":" + projectKey;
Predicate<GrantedAuthority> p = r -> r.getAuthority().equals(projectPermission) || r.getAuthority().equals(projectPermissionManage) || r.getAuthority().equals(manageProject);
if (authentication.getAuthorities().stream().anyMatch(p)) {
return true;
};
return false;
}
}
3 - Advantage / disadvantage
Advantage
the business logic permission is loaded only when user login to the application and not every times so it is powerfull solution for microservice architecture.
Disadvantage
Need to update authentication token or revoke the token when permission change else
when you update the permission for user, the user required to logout and login. But you have same issue without this security logic, for example when user is disabled or enable.
My solution i use for example in a controller :
newAuthorities = projectService.getRolesForUser(username);
UsernamePasswordAuthenticationToken newAuth = new UsernamePasswordAuthenticationToken(auth.getPrincipal(), auth.getCredentials(), newAuthorities);
OAuth2Authentication authentication = (OAuth2Authentication)SecurityContextHolder.getContext().getAuthentication();
Collection<OAuth2AccessToken> accessTokens = tokenStore.findTokensByUserName(principal.getName());
OAuth2Authentication auth2 = new OAuth2Authentication(authentication.getOAuth2Request(), newAuth);
accessTokens.forEach(token -> {
if (!token.isExpired()) {
tokenStore.storeAccessToken(token, auth2);
}
});

Play2.5 Java WebSockets

Play 2.5 Highlights states
Better control over WebSocket frames
The Play 2.5 WebSocket API gives you direct control over WebSocket frames. You can now send and receive binary, text, ping, pong and close frames. If you don’t want to worry about this level of detail, Play will still automatically convert your JSON or XML data into the right kind of frame.
However
https://www.playframework.com/documentation/2.5.x/JavaWebSockets has examples around LegacyWebSocket which is deprecated
What is the recommended API/pattern for Java WebSockets? Is using
LegacyWebSocket the only option for java websockets?
Are there any examples using new Message types ping/pong to implement a heartbeat?
The official documentation on this is disappointingly very sparse. Perhaps in Play 2.6 we'll see an update to this. However, I will provide an example below on how to configure a chat websocket in Play 2.5, just to help out those in need.
Setup
AController.java
#Inject
private Materializer materializer;
private ActorRef chatSocketRouter;
#Inject
public AController(#Named("chatSocketRouter") ActorRef chatInjectedActor) {
this.chatSocketRouter = chatInjectedActor;
}
// Make a chat websocket for a user
public WebSocket chatSocket() {
return WebSocket.Json.acceptOrResult(request -> {
String authToken = getAuthToken();
// Checking of token
if (authToken == null) {
return forbiddenResult("No [authToken] supplied.");
}
// Could we find the token in the database?
final AuthToken token = AuthToken.findByToken(authToken);
if (token == null) {
return forbiddenResult("Could not find [authToken] in DB. Login again.");
}
User user = token.getUser();
if (user == null) {
return forbiddenResult("You are not logged in to view this stream.");
}
Long userId = user.getId();
// Create a function to be run when we initialise a flow.
// A flow basically links actors together.
AbstractFunction1<ActorRef, Props> getWebSocketActor = new AbstractFunction1<ActorRef, Props>() {
#Override
public Props apply(ActorRef connectionProperties) {
// We use the ActorRef provided in the param above to make some properties.
// An ActorRef is a fancy word for thread reference.
// The WebSocketActor manages the web socket connection for one user.
// WebSocketActor.props() means "make one thread (from the WebSocketActor) and return the properties on how to reference it".
// The resulting Props basically state how to construct that thread.
Props properties = ChatSocketActor.props(connectionProperties, chatSocketRouter, userId);
// We can have many connections per user. So we need many ActorRefs (threads) per user. As you can see from the code below, we do exactly that. We have an object called
// chatSocketRouter which holds a Map of userIds -> connectionsThreads and we "tell"
// it a lightweight object (UserMessage) that is made up of this connecting user's ID and the connection.
// As stated above, Props are basically a way of describing an Actor, or dumbed-down, a thread.
// In this line, we are using the Props above to
// reference the ActorRef we've just created above
ActorRef anotherUserDevice = actorSystem.actorOf(properties);
// Create a lightweight object...
UserMessage routeThisUser = new UserMessage(userId, anotherUserDevice);
// ... to tell the thread that has our Map that we have a new connection
// from a user.
chatSocketRouter.tell(routeThisUser, ActorRef.noSender());
// We return the properties to the thread that will be managing this user's connection
return properties;
}
};
final Flow<JsonNode, JsonNode, ?> jsonNodeFlow =
ActorFlow.<JsonNode, JsonNode>actorRef(getWebSocketActor,
100,
OverflowStrategy.dropTail(),
actorSystem,
materializer).asJava();
final F.Either<Result, Flow<JsonNode, JsonNode, ?>> right = F.Either.Right(jsonNodeFlow);
return CompletableFuture.completedFuture(right);
});
}
// Return this whenever we want to reject a
// user from connecting to a websocket
private CompletionStage<F.Either<Result, Flow<JsonNode, JsonNode, ?>>> forbiddenResult(String msg) {
final Result forbidden = Results.forbidden(msg);
final F.Either<Result, Flow<JsonNode, JsonNode, ?>> left = F.Either.Left(forbidden);
return CompletableFuture.completedFuture(left);
}
ChatSocketActor.java
public class ChatSocketActor extends UntypedActor {
private final ActorRef out;
private final Long userId;
private ActorRef chatSocketRouter;
public ChatSocketActor(ActorRef out, ActorRef chatSocketRouter, Long userId) {
this.out = out;
this.userId = userId;
this.chatSocketRouter = chatSocketRouter;
}
public static Props props(ActorRef out, ActorRef chatSocketRouter, Long userId) {
return Props.create(ChatSocketActor.class, out, chatSocketRouter, userId);
}
// Add methods here handling each chat connection...
}
ChatSocketRouter.java
public class ChatSocketRouter extends UntypedActor {
public ChatSocketRouter() {}
// Stores userIds to websockets
private final HashMap<Long, List<ActorRef>> senders = new HashMap<>();
private void addSender(Long userId, ActorRef actorRef){
if (senders.containsKey(userId)) {
final List<ActorRef> actors = senders.get(userId);
actors.add(actorRef);
senders.replace(userId, actors);
} else {
List<ActorRef> l = new ArrayList<>();
l.add(actorRef);
senders.put(userId, l);
}
}
private void removeSender(ActorRef actorRef){
for (List<ActorRef> refs : senders.values()) {
refs.remove(actorRef);
}
}
#Override
public void onReceive(Object message) throws Exception {
ActorRef sender = getSender();
// Handle messages sent to this 'router' here
if (message instanceof UserMessage) {
UserMessage userMessage = (UserMessage) message;
addSender(userMessage.userId, userMessage.actorRef);
// Watch sender so we can detect when they die.
getContext().watch(sender);
} else if (message instanceof Terminated) {
// One of our watched senders has died.
removeSender(sender);
} else {
unhandled(message);
}
}
}
Example
Now whenever you want to send a client with a websocket connection a message you can do something like:
ChatSenderController.java
private ActorRef chatSocketRouter;
#Inject
public ChatSenderController(#Named("chatSocketRouter") ActorRef chatInjectedActor) {
this.chatSocketRouter = chatInjectedActor;
}
public static void sendMessage(Long sendToId) {
// E.g. send the chat router a message that says hi
chatSocketRouter.tell(new Message(sendToId, "Hi"));
}
ChatSocketRouter.java
#Override
public void onReceive(Object message) throws Exception {
// ...
if (message instanceof Message) {
Message messageToSend = (Message) message;
// Loop through the list above and send the message to
// each connection. For example...
for (ActorRef wsConnection : senders.get(messageToSend.getSendToId())) {
// Send "Hi" to each of the other client's
// connected sessions
wsConnection.tell(messageToSend.getMessage());
}
}
// ...
}
Again, I wrote the above to help out those in need. After scouring the web I could not find a reasonable and simple example. There is an open issue for this exact topic. There are also some examples online but none of them were easy to follow. Akka has some great documentation but mixing it in with Play was a tough mental task.
Please help improve this answer if you see anything that is amiss.

Play framework and ionic for mobile I need Security without cookies but token

I have an issue with using mobile app that I created in Ionic and back-end APIs that I coded in play framework, my issue simply is I need way to handle security matter for calling APIs that need to be secured, mean user must be logged in to do actions, as example guest can view group but can join only if logged in.
My issue that I believe that cookies is not supported for Mobile, i have code checking session that stored in cookies, its working for website, but it will not work for mobile, right?
Currently I'm trying to send a token that generated in back-end and returned with login response and stored in localStorage in Ionic, but my issue is that I can't sent token to be validated with request.
Front End:
I have the following http interceptor :
angular
.module('app.core')
.factory('sessionInjector', sessionInjector);
/** #ngInject */
function sessionInjector($q, sessionService, $rootScope) {
return {
request: function (config) {
$rootScope.$broadcast('loading:show');
if (!sessionService.isAnonymous())
config.headers['x-session-token'] = sessionService.getToken();
}
return config;
}
}
Back-End:
Controller:
#Security.Authenticated(Secure.class)
public Result joinOrganization() {
// Do some business
}
Secure.java :
#Override
public String getUsername(Http.Context ctx) {
// Is this correct way? I get null here
String str = ctx.request().getHeader("x-session-token");
String userId = ctx.session().get("usedId");
if (userId == null) {
return null;
}
User user = Play.application().injector().instanceOf(UserService.class).findUserById(Integer.parseInt(userId));
if (user != null && user.isActive) {
return user.id;
} else {
return null;
}
}
#Override
public Result onUnauthorized(Http.Context ctx) {
return unauthorized(results);
}
Note: Tokens stored in database:
Entity:
#Entity
#Table(name = "AUTHTOKEN")
public class AuthToken extends BaseModel {
#OneToOne(targetEntity = User.class, cascade = CascadeType.REFRESH, optional = false)
public User user;
#Column(nullable = false)
public String token;
#Column
public long expiration;
public AuthToken() {
}
}
For cookies working, but need to remove cookies and use tokens, or use them together cookies for website, tokens for mobile .
Mobile is no different to otherwise in regards of cookies. There are restrictions if AJAX, or cross-domain requests are used, and specially with Apple stuff, but they apply to non-mobile too. If your cookies work on a PC/Mac, they should do on a mobile device just as well. It's more of a form factor than anything else...
I found solution and it was complicated because there are many issues starting from that ngResource does not apply request interceptor its an issue opened from long time.
Second issue was how to send the token with ngResource, its simply with adding headers, the another issue here is getting dynamically the token, this "dynamically" means because the localStorage in memory getting lost when refresh so you need to get back it, this can be done with service, and function call for getting the token, something like this :
$resource('/user/:userId/card/:cardId', {userId:123, cardId:'#id'}, {
charge: {method:'POST', params:{charge:true}, headers = {
'x-session-token': function () {
return sessionService.getToken()
}}
});
Inside sessionService :
// this to recreate the cache in memory once the user refresh, it will keep the data if exisit but will point to it again in memory
if (CacheFactory.get('profileCache') == undefined) {
//if there is no cache already then create new one and assign it to profileCache
CacheFactory.createCache('profileCache');
}
function getCurrentSession() {
var profileCache = CacheFactory.get('profileCache');
if (profileCache !== undefined && profileCache.get('Token') !== undefined) {
return profileCache.get('Token');
}
return null;
}
function getToken() {
var currentSession = getCurrentSession();
if (currentSession != null && currentSession != '') {
return currentSession.token;
}
return null;
}
And then this method will work inside Secure.java
protected User getUser(Http.Context ctx) {
String token = ctx.request().getHeader("x-session-token");
if (token != null) {
return securityService.validateToken(token);
}
return null;
}

Websocket: How To Push A Message To A Target User

I'm trying to implement a push notification in spring using websocket and with the use of sock.js.
These are the code snippets:
public class NotifyController {
#MessageMapping("/notifications")
#SendTo("/get/notifications")
public Greeting greeting(HelloMessage message) throws Exception {
new Greeting("Hello, " + message.getName() + "!");
}
}
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/get/notifications");
config.setApplicationDestinationPrefixes("/gssocket");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/notifications").withSockJS();
}
}
This is the code in the front..
function connect() {
var notificationSocket = new SockJS('/notifications');
stompNotificationClient = Stomp.over(notificationSocket);
stompNotificationClient.connect({}, function(frame) {
console.log('Connected: ' + frame);
stompNotificationClient.subscribe('/get/notifications', function(greeting){
showGreeting(JSON.parse(greeting.body).content);
});
});
}
function sendNotification() {
var name = "test"
stompNotificationClient.send("/gssocket/notifications", {}, JSON.stringify({ 'name': name }));
}
I've already managed to make it work. But the question is, how can I push the message to certain target users. For example, there are 5 online users namely: user1, user2, user3, user4 and user5. I want the notification to be published to user1 and user2 only. Can you give me any idea on how to achieve this one? I'm thinking of either doing it in the backend or in the frontend. Or there is another way to achieve this using spring.
Somebody help me please.
Thank you.
You can check User destinations to target specific users with your messages, this is done using the SimpMessagingTemplate.convertAndSendToUser() call.
Note that to enable this functionality your users must be HTTP authenticated.

Categories