-
-
Save vrudikov/1f521f2759c12dda06f0848707cf423e to your computer and use it in GitHub Desktop.
How to secure WebSocket connections (WebSocket over STOMP) ? Grails sample app : https://github.com/xmlking/grails-batch-rest
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
grails.plugin.springsecurity.controllerAnnotations.staticRules = [ | |
'/': ['permitAll'], | |
'/index': ['permitAll'], | |
'/index.gsp': ['permitAll'], | |
'/stomp/**': ['ROLE_USER'], //to secure WS handshake | |
'/api/**': ['ROLE_USER'], | |
'/assets/**': ['permitAll'], | |
'/**/js/**': ['permitAll'], | |
'/**/css/**': ['permitAll'], | |
'/**/images/**': ['permitAll'], | |
'/**/favicon.ico': ['permitAll'], | |
'/j_spring_security_switch_user': ['ROLE_SWITCH_USER'], | |
'/j_spring_security_exit_user': ['permitAll'], | |
'/dbdoc/**': ['ROLE_IT_ADMIN'], | |
'/dbconsole/**': ['ROLE_IT_ADMIN'], | |
] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import grails.plugin.springsecurity.annotation.Secured | |
import org.springframework.messaging.handler.annotation.DestinationVariable | |
import org.springframework.messaging.handler.annotation.MessageMapping | |
import org.springframework.messaging.handler.annotation.SendTo | |
import org.springframework.messaging.simp.annotation.SendToUser | |
import org.springframework.messaging.simp.annotation.SubscribeMapping | |
import org.sumo.apiapp.stomp.StompExceptionHandler | |
import java.security.Principal | |
@Secured('admin') | |
@MessageMapping("/chat") | |
class MessagingController implements StompExceptionHandler { | |
def messagingService | |
def index() { | |
//log.error "this is error" | |
//throw new IllegalStateException("test IllegalStateException") | |
render "index page" | |
} | |
// broadcast pattern. Join/Leave Announcements, send messages | |
@MessageMapping("/join") | |
@SendTo("/topic/chat/announcements.join") | |
protected String join(String message, Principal principal) { | |
log.debug "Join Message: ${message} from ${principal.name}" | |
messagingService.addUser(principal.name) | |
Date now = Calendar.getInstance().getTime() | |
return "${principal.name} joined the chatroom at ${now}. Message: ${message}!" | |
} | |
@MessageMapping("/leave") | |
@SendTo("/topic/chat/announcements.left") | |
protected String leave(String message, Principal principal) { | |
log.debug "Goodbye Message: ${message} from ${principal.name}" | |
messagingService.removeUser(principal.name) | |
Date now = Calendar.getInstance().getTime() | |
return "${principal.name} left the chatroom at ${now}. Message: ${message}!" | |
} | |
@MessageMapping("/messages") //without @SendTo or @SendToUser, Default response address: /topic/chat/messages | |
protected String sendToAll(String message, Principal principal) { | |
log.debug "message: [${message}] from [${principal.name}]" | |
return "[${principal.name}]: ${message}" | |
} | |
// reply only to sender pattern. | |
@MessageMapping("/self") | |
@SendToUser //SendToUser's Default response address: /user/queue/chat/self | |
protected String sendToUser(String message, Principal principal) { | |
log.debug "Hello [${principal.name}] you send: ${message}" | |
return "Hello [${principal.name}] you send: ${message}" | |
} | |
/** | |
* request-reply pattern. | |
* @SubscribeMapping method is sent as a message directly back to the connected client and does not pass through the broker | |
*/ | |
@SubscribeMapping("/rooms/{id}") | |
protected Set<String> getChatRooms(@DestinationVariable String id) { | |
log.debug "id: ${id}" | |
return messagingService.rooms | |
} | |
// request-reply pattern. | |
@SubscribeMapping("/users/{room}") | |
protected Set<String> getActiveUsers(@DestinationVariable String room) { | |
log.debug "serving users for room : ${room}" | |
return messagingService.getUsers(room) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import grails.async.Promise | |
import static grails.async.Promises.* | |
import grails.transaction.NotTransactional | |
import grails.transaction.Transactional | |
import org.springframework.scheduling.annotation.EnableScheduling | |
import org.springframework.scheduling.annotation.Scheduled | |
@Transactional | |
@EnableScheduling | |
class MessagingService { | |
def brokerMessagingTemplate | |
def users= ['All', 'user1', 'user2'] as Set | |
def rooms = ['Developers','Hackers', 'Stockers'] as Set | |
def symbols = ['AAPL','YHOO', 'GOOG'] as Set | |
@NotTransactional | |
//@Scheduled(fixedDelay=60000L) | |
@Scheduled(fixedRate=60000L) | |
void sendGreetingOnceMinute() { | |
log.debug "sending Scheduled greeting" | |
brokerMessagingTemplate.convertAndSend "/topic/self", "hello from service!" | |
} | |
@NotTransactional | |
Promise stock(String ticker = 'GOOG') { | |
task { | |
def url = new URL("http://download.finance.yahoo.com/d/quotes.csv?s=${ticker}&f=nsl1op&e=.csv") | |
Double price = url.text.split(',')[-1] as Double | |
return [ticker: ticker, price: price] | |
} | |
} | |
//Sending updates to clients periodically | |
@Scheduled(cron="*/60 * * * * ?") | |
@NotTransactional | |
public void sendQuotes() { | |
symbols.collect this.&stock each { | |
log.debug it.class | |
it.then { | |
log.debug it | |
brokerMessagingTemplate.convertAndSend "/topic/price.stock." + it.ticker, it.price | |
} | |
} | |
} | |
public Set<String> getRooms() { | |
rooms | |
} | |
@NotTransactional | |
public void addUser(String user) { | |
users.add(user) | |
} | |
@NotTransactional | |
public void removeUser(String user) { | |
users.remove(user) | |
} | |
public Set<String> getUsers(String room) { | |
log.debug "serving users for room: ${room}" | |
users | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import org.springframework.messaging.MessagingException | |
import org.springframework.messaging.handler.annotation.MessageExceptionHandler | |
import org.springframework.messaging.simp.annotation.SendToUser | |
trait StompExceptionHandler { | |
// failure handling pattern. | |
@MessageExceptionHandler | |
@SendToUser("/queue/errors") | |
public String handleMessagingException(MessagingException exception) { | |
log.error('handleMessagingException: ', exception) | |
return exception.failedMessage; | |
} | |
@MessageExceptionHandler | |
@SendToUser("/queue/errors") | |
public String handleIllegalStateException(IllegalStateException exception) { | |
log.error('handleIllegalStateException: ', exception) | |
return exception.getMessage(); | |
} | |
@MessageExceptionHandler | |
@SendToUser("/queue/errors") | |
public String handleException(Throwable exception) { | |
log.error('handleException: ', exception) | |
return exception.getMessage(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import grails.plugin.springsecurity.annotation.Secured | |
import org.codehaus.jackson.annotate.JsonProperty | |
import org.springframework.messaging.handler.annotation.DestinationVariable | |
import org.springframework.messaging.handler.annotation.MessageMapping | |
import org.springframework.messaging.simp.SimpMessageHeaderAccessor | |
import org.springframework.messaging.simp.annotation.SendToUser | |
import org.sumo.apiapp.stomp.StompExceptionHandler | |
import java.security.Principal | |
@Secured('permitAll') | |
@MessageMapping("/terminal") | |
class TerminalController implements StompExceptionHandler { | |
def index() { | |
render "index page" | |
} | |
@MessageMapping("/input/{containerId}") | |
@SendToUser | |
protected String execute(@DestinationVariable String containerId, String input, | |
Principal principal, SimpMessageHeaderAccessor headerAccessor) { | |
def session = headerAccessor.sessionAttributes | |
if (!session.command) { | |
session.command = new StringBuilder() | |
} | |
session.command << input | |
if (input == "\r") { | |
if (principal.name != 'businessadmin') return "\r\nYou don't have access to terminal...\r\n" | |
def result = new StringBuilder() << "\r\n" | |
try { | |
session.command.toString().execute().text.eachLine { line -> | |
result << line << "\r\n" | |
} | |
} catch (Exception e) { | |
result << e.message + "\r\n" | |
} finally { | |
session.command.setLength(0) | |
} | |
return result | |
} | |
return input | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment