±à¼ÍƼö: |
±¾ÎÄÀ´×ÔÓÚlrwinx.github.io,±¾ÆªÎÄÕÂÒÔwebsocketµÄÔÀíºÍÂäµØÎªºËÐÄ£¬À´ÐðÊöwebsocketµÄʹÓã¬ÒÔ¼°Ïà¹ØÓ¦Óó¡¾°¡£ |
|
websocket¸ÅÊö
httpÓëwebsocket
ÈçÎÒÃÇËùÁ˽⣬httpÁ¬½ÓΪһ´ÎÇëÇóÒ»´ÎÏìÓ¦(request->response)£¬±ØÐëΪͬ²½µ÷Ó÷½Ê½¡£
¶øwebsocketΪһ´ÎÁ¬½ÓÒԺ󣬻ὨÁ¢tcpÁ¬½Ó£¬ºóÐø¿Í»§¶ËÓë·þÎñÆ÷½»»¥ÎªÈ«Ë«¹¤·½Ê½µÄ½»»¥·½Ê½£¬¿Í»§¶Ë¿ÉÒÔ·¢ËÍÏûÏ¢µ½·þÎñ¶Ë£¬·þÎñ¶ËÒ²¿É½«ÏûÏ¢·¢Ë͸ø¿Í»§¶Ë¡£

http,websocket
´ËͼÀ´Ô´ÓÚWebsocketÐÒéµÄѧϰ¡¢µ÷ÑкÍʵÏÖ,ÈçÓÐÇÖȨÎÊÌ⣬¸æÖªºó£¬É¾³ý¡£
¸ù¾ÝÉÏͼ£¬ÎÒÃÇ´óÖ¿ÉÒÔÁ˽⵽httpÓëwebsocketÖ®¼äµÄÇø±ðºÍ²»Í¬¡£
ΪʲôҪʹÓÃwebsocket
ÄÇôÁ˽âhttpÓëwebsocketÖ®¼äµÄ²»Í¬ÒÔºó£¬ÎÒÃÇΪʲôҪʹÓÃwebsocketÄØ£¿ ËûµÄÓ¦Óó¡¾°ÊÇÊ²Ã´ÄØ£¿
ÎÒÕÒµ½ÁËÒ»¸ö±È½Ï·ûºÏwebsocketʹÓó¡¾°µÄÃèÊö
¡°The best fit for WebSocket is in
web applications where the client and server need
to exchange events at high frequency and with low
latency.¡±
·Òë: ÔÚ¿Í»§¶ËÓë·þÎñÆ÷¶Ë½»»¥µÄwebÓ¦ÓÃÖУ¬websocket×îÊʺÏÔÚ¸ßÆµÂʵÍÑӳٵij¡¾°Ï£¬½øÐÐʼþµÄ½»»»ºÍ´¦Àí
´Ë¶ÎÀ´Ô´ÓÚspring websocketµÄ¹Ù·½Îĵµ
Á˽âÒÔÉÏ֪ʶºó£¬ÎÒ¾Ù³ö¼¸¸ö±È½Ï³£¼ûµÄ³¡¾°:
ÓÎÏ·ÖеÄÊý¾Ý´«Êä
¹ÉƱKÏßͼÊý¾Ý
¿Í·þϵͳ
¸ù¾ÝÈçÉÏËùÊö£¬¸÷¸öϵͳ¶¼À´Ê¹ÓÃwebsocket²»ÊǸüºÃÂð£¿
Æäʵ²¢²»ÊÇ£¬websocket½¨Á¢Á¬½ÓÖ®ºó£¬ºó±ß½»»¥¶¼ÓÉtcpÐÒé½øÐн»»¥£¬¹Ê¿ª·¢µÄ¸´ÔÓ¶È»á½Ï¸ß¡£µ±È»websocketͨѶ£¬±¾ÉíÒª¿¼ÂǵÄÊÂÇéÒª±ÈHTTPÐÒéµÄͨѶ¿¼Âǵĸü¶à.
ËùÒÔÈç¹û²»ÊÇÓÐÌØÊâÒªÇó(¼´ Ó¦Óò»ÊÇ¡±¸ßƵÂʵÍÑÓ³Ù¡±µÄÒªÇó),ÐèÒªÓÅÏÈ¿¼ÂÇHTTPÐÒéÊÇ·ñ¿ÉÒÔÂú×ã¡£
±ÈÈçÐÂÎÅϵͳ£¬ÐÂÎŵÄÊý¾ÝÍíÉÏ10·ÖÖÓ-30·ÖÖÓ£¬ÊÇ¿ÉÒÔ½ÓÊܵģ¬ÄÇô¾Í¿ÉÒÔ²ÉÓÃHTTPµÄ·½Ê½½øÐÐÂÖѯ(polling)²Ù×÷µ÷ÓÃREST½Ó¿Ú¡£
µ±È»ÓÐʱÎÒÃǽ¨Á¢ÁËwebsocketͨѶ£¬²¢ÇÒÏ£Íûͨ¹ýHTTPÌṩµÄREST½Ó¿ÚÍÆË͸øÄ³¿Í»§¶Ë£¬´ËʱÐèÒª¿¼ÂÇREST½Ó¿Ú½ÓÊÜÊý¾Ý´«Ë͸øwebsocketÖУ¬½øÐй㲥ʽµÄͨѶ·½Ê½¡£
ÖÁ´Ë£¬ÎÒÒѾ½²ÊöÁËÈýÖÖ½»»¥·½Ê½µÄʹÓó¡¾°:
websocket¶ÀÁ¢Ê¹Óó¡¾°
HTTP¶ÀÁ¢Ê¹Óó¡¾°
HTTPÖÐתwebsocketʹÓó¡¾°
Ïà¹Ø¼¼Êõ¸ÅÄî
websocket
websocketΪһ´ÎHTTPÎÕÊֺ󣬺óÐøÍ¨Ñ¶ÎªtcpÐÒéµÄͨѶ·½Ê½¡£
µ±È»£¬ºÍHTTPÒ»Ñù£¬websocketÒ²ÓÐһЩԼ¶¨µÄͨѶ·½Ê½£¬httpͨѶ·½Ê½Îªhttp¿ªÍ·µÄ·½Ê½,e.g.
http://xxx.com/path ,websocketͨѶ·½Ê½ÔòΪws¿ªÍ·µÄ·½Ê½,e.g. ws://xxx.com/path
SSL:
HTTP: https://xxx.com/path
WEBSOCKET: wss://xxx.com/path

websocketͨѶ
´ËͼÀ´Ô´ÓÚWebSocket ½Ì³Ì,ÈçÓÐÇÖȨÎÊÌ⣬¸æÖªºó£¬É¾³ý¡£
SockJS
ÕýÈçÎÒÃÇËùÖª,websocketÐÒéËäÈ»ÒѾ±»Öƶ¨£¬µ±Ê±»¹ÓÐºÜ¶à°æ±¾µÄä¯ÀÀÆ÷»òä¯ÀÀÆ÷³§ÉÌ»¹Ã»ÓÐÖ§³ÖµÄºÜºÃ¡£
ËùÒÔ,SockJS,¿ÉÒÔÀí½âΪÊÇwebsocketµÄÒ»¸ö±¸Ñ¡·½°¸¡£
ÄÇËüÈçºÎ¹æ¶¨±¸Ñ¡·½°¸µÄÄØ£¿
Ëü´ó¸ÅÖ§³ÖÕâÑù¼¸¸ö·½°¸:
Websockets
Streaming
Polling
µ±È»£¬¿ªÆô²¢Ê¹ÓÃSockJSºó£¬Ëü»áÓÅÏÈÑ¡ÓÃwebsocketÐÒé×÷Ϊ´«ÊäÐÒ飬Èç¹ûä¯ÀÀÆ÷²»Ö§³ÖwebsocketÐÒ飬Ôò»áÔÚÆäËû·½°¸ÖУ¬Ñ¡ÔñÒ»¸ö½ÏºÃµÄÐÒé½øÐÐͨѶ¡£
¿´Ò»ÏÂĿǰä¯ÀÀÆ÷µÄÖ§³ÖÇé¿ö:

Supported transports,
by browser
´ËͼÀ´Ô´ÓÚgithub: sockjs-client
ËùÒÔ£¬Èç¹ûʹÓÃSockJS½øÐÐͨѶ£¬Ëü½«ÔÚʹÓÃÉϱ£³ÖÒ»Ö£¬µ×²ãÓÉËü×Ô¼ºÈ¥Ñ¡ÔñÏàÓ¦µÄÐÒé¡£
¿ÉÒÔÈÏΪSockJSÊÇwebsocketͨѶ²ãÉϵÄÉϲãÐÒé¡£
µ×²ã¶ÔÓÚ¿ª·¢ÕßÀ´ËµÊÇ͸Ã÷µÄ¡£
STOMP
STOMP ÖÐÎÄΪ: ÃæÏòÏûÏ¢µÄ¼òµ¥Îı¾ÐÒé
websocket¶¨ÒåÁËÁ½ÖÖ´«ÊäÐÅÏ¢ÀàÐÍ: Îı¾ÐÅÏ¢ ºÍ ¶þ½øÖÆÐÅÏ¢ ( text and binary
).
ÀàÐÍËäÈ»±»È·¶¨£¬µ«ÊÇËûÃǵĴ«ÊäÌåÊÇûÓй涨µÄ¡£
µ±È»Äã¿ÉÒÔ×Ô¼ºÀ´Ð´´«ÊäÌ壬À´¹æ¶¨´«ÊäÄÚÈÝ¡£(µ±È»£¬ÕâÑùµÄ¸´ÔÓ¶ÈÊǺܸߵÄ)
ËùÒÔ,ÐèÒªÓÃÒ»ÖÖ¼òµ¥µÄÎı¾´«ÊäÀàÐÍÀ´¹æ¶¨´«ÊäÄÚÈÝ£¬Ëü¿ÉÒÔ×÷ΪͨѶÖеÄÎı¾´«ÊäÐÒé,¼´½»»¥Öеĸ߼¶ÐÒéÀ´¶¨Òå½»»¥ÐÅÏ¢¡£
STOMP±¾Éí¿ÉÒÔÖ§³ÖÁ÷ÀàÐ͵ÄÍøÂç´«ÊäÐÒé: websocketÐÒéºÍtcpÐÒé
ËüµÄ¸ñʽΪ:
COMMAND
header1:value1
header2:value2
Body^@
SUBSCRIBE
id:sub-1
destination:/topic/price.stock.*
^@
SEND
destination:/queue/trade
content-type:application/json
content-length:44
{"action":"BUY","ticker":"MMM","shares",44}^@ |
µ±È»STOMPÒѾӦÓÃÓںܶàÏûÏ¢´úÀíÖУ¬×÷Ϊһ¸ö´«ÊäÐÒéµÄ¹æ¶¨£¬Èç:RabbitMQ, ActiveMQ
ÎÒÃǽԿÉÒÔÓÃSTOMPºÍÕâÀàMQ½øÐÐÏûÏ¢½»»¥.
³ýÁËSTOMPÏà¹ØµÄ´úÀíÍ⣬ʵ¼ÊÉÏ»¹ÌṩÁËÒ»¸östomp.js,ÓÃÓÚä¯ÀÀÆ÷¿Í»§¶ËʹÓÃSTOMPÏûÏ¢ÐÒé´«ÊäµÄjs¿â¡£
ÈÃÎÒÃǺܷ½±ãµÄʹÓÃstomp.js½øÐÐÓëSTOMPÐÒéÏà¹ØµÄ´úÀí½øÐн»»¥.
ÕýÈçÎÒÃÇËùÖª£¬Èç¹ûwebsocketÄÚÈÝ´«ÊäÐÅϢʹÓÃSTOMPÀ´½øÐн»»¥£¬websocketÒ²ºÜºÃµÄÓÚÏûÏ¢´úÀíÆ÷½øÐн»»¥(Èç:RabbitMQ,
ActiveMQ)
ÕâÑù¾ÍºÜºÃµÄÌṩÁËÏûÏ¢´úÀíµÄ¼¯³É·½°¸¡£
×ܽᣬʹÓÃSTOMPµÄÓŵãÈçÏÂ:
²»ÐèÒª×Ô½¨Ò»Ì××Ô¶¨ÒåµÄÏûÏ¢¸ñʽ
ÏÖÓÐstomp.js¿Í»§¶Ë(ä¯ÀÀÆ÷ÖÐʹÓÃ)¿ÉÒÔÖ±½ÓʹÓÃ
ÄÜ·ÓÉÐÅÏ¢µ½Ö¸¶¨ÏûÏ¢µØµã
¿ÉÒÔÖ±½ÓʹÓóÉÊìµÄSTOMP´úÀí½øÐй㲥 Èç:RabbitMQ, ActiveMQ
¼¼ÊõÂ䵨
ºó¶Ë¼¼Êõ·½°¸Ñ¡ÐÍ
websocket·þÎñ¶ËÑ¡ÐÍ:spring websocket
Ö§³ÖSockJS,¿ªÆôSockJSºó£¬¿ÉÓ¦¶Ô²»Í¬ä¯ÀÀÆ÷µÄͨѶ֧³Ö
Ö§³ÖSTOMP´«ÊäÐÒ飬¿ÉÎÞ·ì¶Ô½ÓSTOMPÐÒéϵÄÏûÏ¢´úÀíÆ÷(Èç:RabbitMQ, ActiveMQ)
ǰ¶Ë¼¼Êõ·½°¸Ñ¡ÐÍ
ǰ¶ËÑ¡ÐÍ: stomp.js,sockjs.js
ºó¶Ë¿ªÆôSOMPºÍSockJSÖ§³Öºó£¬Ç°¶ÔÓ¦ÓжÔÓ¦µÄjs¿â½øÐÐÖ§³Ö.
ËùÒÔÑ¡ÓôËÁ½¸ö¿â.
×ܽá
ÉÏÊöËùÓü¼Êõ£¬ÊÇÕâÑùµÄÂß¼:
¿ªÆôsocktJS:
Èç¹ûÓÐä¯ÀÀÆ÷²»Ö§³ÖwebsocketÐÒ飬¿ÉÒÔÔÚÆäËûÁ½ÖÖÐÒéÖнøÐÐÑ¡Ôñ£¬µ«ÊǶÔÓÚÓ¦ÓòãÀ´½²£¬Ê¹ÓÃÆðÀ´ÊÇÒ»ÑùµÄ¡£
ÕâÊÇΪÁËÖ§³Öä¯ÀÀÆ÷²»Ö§³ÖwebsocketÐÒéµÄÒ»ÖÖ±¸Ñ¡·½°¸
ʹÓÃSTOMP:
ʹÓÃSTOMP½øÐн»»¥£¬Ç°¶Ë¿ÉÒÔʹÓÃstomp.jsÀà¿â½øÐн»»¥£¬ÏûÏ¢Ò»STOMPÐÒé¸ñʽ½øÐд«Ê䣬ÕâÑù¾Í¹æ¶¨ÁËÏûÏ¢´«Êä¸ñʽ¡£
ÏûÏ¢½øÈëºó¶ËÒԺ󣬿ÉÒÔ½«ÏûÏ¢ÓëʵÏÖSTOMP¸ñʽµÄ´úÀíÆ÷½øÐÐÕûºÏ¡£
ÕâÊÇΪÁËÏûϢͳһ¹ÜÀí£¬½øÐлúÆ÷À©ÈÝʱ£¬¿É½øÐиºÔؾùºâ²¿Êð
ʹÓÃspring websocket:
ʹÓÃspring websocket,ÊÇÒòΪËûÌṩÁËSTOMPµÄ´«Êä×ÔÐÒéµÄͬʱ£¬»¹ÌṩÁËStockJSµÄÖ§³Ö¡£
µ±È»£¬³ý´ËÖ®Í⣬spring websocket»¹ÌṩÁËȨÏÞÕûºÏµÄ¹¦ÄÜ£¬»¹ÓÐ×Ô´øÌìÉúÓëspring¼Ò×åµÈÏà¹Ø¿ò¼Ü½øÐÐÎÞ·ìÕûºÏ¡£
Ó¦Óó¡¾°
Ó¦Óñ³¾°
2016Ä꣬ÔÚ¹«Ë¾ÓëͬÊÂÒ»ÆðÌÖÂۺͿª·¢Á˹«Ë¾ÄÚ²¿µÄ¿Í·þϵͳ£¬ÓÉÓÚǰ¶Ë¼¼ÄܵIJ»×㣬ºÜ¶àͨѶ·½ÃæµÄÎÊÌ⣬ÎÞ·¨Ç××Ôµ÷ÊÔǰ¶ËÀ´½â¾öÎÊÌâ¡£
ÒòΪ¹«Ë¾¼¼Êõ¼Ü¹¹ÌåϵÒÔǰºó¶Ë·ÖÀëΪÖ÷£¬¹Êǰ¶ËÎÞ·¨ÐÖúºó¶Ëµ÷ÊÔ£¬ºó¶ËÎÞ·¨ÐÖúǰ¶Ëµ÷ÊÔ
ÔÚ¼ÓÉÏwebsocketΪ¹«Ë¾¸ÕÆôÓõÄÐÒ飬Á˽âµÄÈ˲»¶à£¬µ¼ÖÂǰºó¶Ëµ÷ÊÔÎÊÌâÖØÖØ¡£
Ò»ÄêºóµÄ½ñÌ죬ÎÒ´òË㽫ǰ¶ËÖØÎ£¬×Ô¼ºÀ´µ÷ÊÔÒ»ÏÂǰºó¶Ë£¬À´·¢¾òÒ»ÏÂ֮ǰÁªµ÷µÄÎÊÌâ.
µ±È»£¬Ç°¶Ë£¬ÎÒÖ»ÊÇ¿¼ÂÇstomp.jsºÍsockt.jsµÄʹÓá£
´úÂë½×¶ÎÉè¼Æ
½ÇÉ«
¿Í·þ
¿Í»§
µÇ¼Óû§×´Ì¬
ÉÏÏß
ÏÂÏß
·ÖÅä²ßÂÔ
Óû§µÇ½ºó£¬Ó¦¸Ã¸ù¾ÝÓû§½ÇÉ«½øÐзÖÅä
¹ØÏµ±£´æ²ßÂÔ
Ó¦¸ÃÌṩ¹ØÏµÐͱ£´æ²ßÂÔ: ¿¼ÂÇÄÚ´æÊ½²ßÂÔ(¿ÉÓÃÓÚ²âÊÔ)£¬redisʽ²ßÂÔ
±¸×¢:ÓÅÏÈÓ¦¸Ã¿¼ÂÇʵÏÖInmemory²ßÂÔ£¬ÓÃÓÚ²âÊÔ£¬ÈùØÏµ±£´æ²ßÂÔÓë´æ´¢Æ½Ì¨ÎÞ¹Ø
ͨѶ²ãÉè¼Æ
¹éÀàtopicµÄ¹ã²¥Éè¼Æ(ͨѶ·½Ê½:1-n)
¹éÀàqueueµÄµ¥µãÉè¼Æ(ͨѶ·½Ê½:1-1)
´úÂëʵÏÖ
½ÇÉ«
import org.springframework.security .core.GrantedAuthority;
import org.springframework.security .core.authority.SimpleGrantedAuthority;
import org.springframework .security.core.userdetails.User;
import java.util.Collection;
public enum Role {
CUSTOMER_SERVICE,
CUSTOMER;
public static boolean isCustomer (User user)
{
Collection< GrantedAuthority> authorities
= user.getAuthorities();
SimpleGrantedAuthority customerGrantedAuthority
= new SimpleGrantedAuthority ("ROLE_"
+ Role.CUSTOMER.name());
return authorities.contains (customerGrantedAuthority);
}
public static boolean isCustomerService(User
user) {
Collection<GrantedAuthority> authorities
= user.getAuthorities();
SimpleGrantedAuthority customerServiceGrantedAuthority
= new SimpleGrantedAuthority ("ROLE_"
+ Role.CUSTOMER_SERVICE.name());
return authorities.contains (customerServiceGrantedAuthority);
}
} |
´úÂëÖÐUser¶ÔÏó£¬Îª°²È«¶ÔÏ󣬼´ springÖÐorg.springframework.
security.core.userdetails.User£¬ÎªUserDetailsµÄʵÏÖÀà¡£
User¶ÔÏóÖУ¬±£´æÁËÓû§ÊÚȨºóµÄºÜ¶à»ù´¡È¨ÏÞÐÅÏ¢£¬ºÍÓû§ÐÅÏ¢¡£
ÈçÏÂ:
public interface
UserDetails extends Serializable {
Collection<? extends GrantedAuthority> getAuthorities();
String getPassword();
String getUsername();
boolean isAccountNonExpired();
boolean isAccountNonLocked();
boolean isCredentialsNonExpired();
boolean isEnabled();
} |
·½·¨ #isCustomer ºÍ #isCustomerService ÓÃÀ´ÅжÏÓû§µ±Ç°ÊÇ·ñÊǹ˿ͻòÕßÊǿͷþ¡£
µÇ¼Óû§×´Ì¬
public interface
StatesManager {
enum StatesManagerEnum{
ON_LINE,
OFF_LINE
}
void changeState (User user , StatesManagerEnum
statesManagerEnum);
StatesManagerEnum currentState(User user);
} |
Éè¼ÆµÇ¼״̬ʱ£¬Ó¦´æÔڵǼ״̬¹ÜÀíÏà¹ØµÄ״̬¹ÜÀíÆ÷£¬´Ë¹ÜÀíÆ÷Ö»¸ºÔð¸ü¸ÄÓû§×´Ì¬ºÍ»ñÈ¡Óû§×´Ì¬Ïà¹Ø²Ù×÷¡£
²¢²»Éæ¼°ÆäËû¹ØÁªÂß¼£¬ÕâÑùµÄ´úÂë»®·Ö£¬¸üÓÐÖúÓÚÃæÏò½Ó¿Ú±à³ÌµÄÀ©Õ¹ÐÔ
·ÖÅä²ßÂÔ
public interface
DistributionUsers {
void distribution(User user);
} |
·ÖÅä½ÇÉ«½Ó¿ÚÉè¼Æ£¬Ö»¹Ø×¢´«ÈëµÄÓû§£¬²¢²»¹Ø×¢´ËÓû§Êǿͷþ»òÕßÓû§£¬¾ßÌåÐèÒªÈçºÎÈ¥×ö£¬ÓɾßÌåµÄ·ÖÅä²ßÂÔÀ´¾ö¶¨¡£
¹ØÏµ±£´æ²ßÂÔ
public interface
RelationHandler {
void saveRelation(User customerService,User
customer);
List<User> listCustomers (User customerService);
void deleteRelation (User customerService,User
customer);
void saveCustomerService (User customerService);
List<User> listCustomerService();
User getCustomerService(User customer);
boolean exist(User user);
User availableNextCustomerService();
} |
¹ØÏµ±£´æ²ßÂÔ£¬ÒàÊÇÖ»¹Ø×¢¹ØÏµ±£´æÏà¹Ø£¬²¢²»ÔÚºõÓÚ±£´æµ½Äĸö´æ´¢½éÖÊÖС£
ʵÏÖÀàÓÉInmemory»¹ÊÇredis»¹ÊÇmysql,Ëü²¢²»×¨×¢¡£
µ«ÊÇ£¬´Ë´¦ÐèҪעÒ⣬¶ÔÓÚÕâÖÖ¹ØÏµ±£´æ²ßÂÔ£¬¿ª·¢²âÊÔʱ£¬²¢²»Éæ¼°¸ß¿ÉÓ㬿ɽ«InmemoryÏÈ×ö³öÀ´ÓÃÓÚ²âÊÔ¡£
¿ª·¢¹¦ÄÜͬʱ£¬Ïà¹ØÍ¬ÊÂÔÙÀ´¿ª·¢ÆäËû½éÖÊ´æ´¢µÄ²ßÂÔ£¬ÐÔÄܲâÊÔÒÔ¼°UATÏà¹Ø²âÊÔʱ£¬Ó¦Çл»Îª´Ë½éÖÊ´æ´¢µÄ²ßÂÔÔÙ½øÐвâÊÔ¡£
Óû§×ۺϹÜÀí
¶ÔÓÚ²»Í¬¹¦ÄܵÄʵÏÖ²ßÂÔ£¬Óɸ÷¸ö¹¦ÄÜ×Ô¼ºÀ´ÊµÏÖ£¬ÔÚʹÓÃÉÏ£¬ÎÒÃǽö½ö¸ù¾Ý½Ó¿Ú±à³Ì¼´¿É¡£
ËùÒÔ£¬Òª½«ÉÏÊöËùÓй¦ÄÜ·â×°³ÉÒ»¸ö¹¤¾ßÀà½øÐÐʹÓã¬Õâ¾ÍÊÇËùνµÄ Éè¼ÆÄ£Ê½: ÃÅÃæÄ£Ê½
@Component
public class UserManagerFacade {
@Autowired
private DistributionUsers distributionUsers;
@Autowired
private StatesManager statesManager;
@Autowired
private RelationHandler relationHandler;
public void login(User user) {
if (roleSemanticsMistiness(user)) {
throw new SessionAuthenticationException ("½ÇÉ«ÓïÒå²»ÇåÎú");
}
distributionUsers.distribution(user);
statesManager.changeState (user, StatesManager .StatesManagerEnum.ON_LINE);
}
private boolean roleSemanticsMistiness(User
user) {
Collection<GrantedAuthority> authorities
= user.getAuthorities();
SimpleGrantedAuthority customerGrantedAuthority
= new SimpleGrantedAuthority ("ROLE_" +Role.CUSTOMER.name());
SimpleGrantedAuthority customerServiceGrantedAuthority
= new SimpleGrantedAuthority ("ROLE_" +Role.CUSTOMER_SERVICE.name());
if (authorities.contains(customerGrantedAuthority)
&& authorities.contains(customerServiceGrantedAuthority))
{
return true;
}
return false;
}
public void logout(User user){
statesManager.changeState (user, StatesManager .StatesManagerEnum.OFF_LINE);
}
public User getCustomerService(User user){
return relationHandler.getCustomerService(user);
}
public List<User> listCustomers(User
user){
return relationHandler.listCustomers(user);
}
public StatesManager .StatesManagerEnum getStates(User
user){
return statesManager.currentState(user);
}
} |
UserManagerFacade ÖÐ×¢ÈëÈý¸öÏà¹ØµÄ¹¦ÄܽӿÚ:
@Autowired
private DistributionUsers distributionUsers;
@Autowired
private StatesManager statesManager;
@Autowired
private RelationHandler relationHandler; |
¿ÉÌṩ:
怬(#login)
µÇ³ö(#logout)
»ñÈ¡¶ÔÓ¦¿Í·þ(#getCustomerService)
»ñÈ¡¶ÔÓ¦Óû§Áбí(#listCustomers)
µ±Ç°Óû§µÇ¼״̬(#getStates)
ÕâÑùµÄÉè¼Æ£¬¿É±£Ö¤¶ÔÓÚÓû§¹ØÏµµÄ¹ÜÀí¶¼ÓÉUserManagerFacadeÀ´¾ö¶¨
ÆäËûÄÚ²¿µÄ²Ù×÷À࣬¶ÔÓÚʹÓÃÕßÀ´Ëµ£¬²¢²»¹ØÐÄ£¬¶Ô¿ª·¢À´½²£¬²»Í¬¹¦ÄܵIJßÂÔ¶¼ÊÇ͸Ã÷µÄ¡£
ͨѶ²ãÉè¼Æ - µÇ¼£¬ÊÚȨ
spring websocketËäÈ»²¢Ã»ÓÐÒªÇóconnectʱ£¬±ØÐëÊÚȨ£¬ÒòΪÁ¬½ÓÒԺ󣬻á·Ö·¢¸ø¿Í»§¶ËwebsocketµÄsession
id£¬À´Çø·Ö¿Í»§¶ËµÄ²»Í¬¡£
µ«ÊǶÔÓÚ´ó¶àÊýÓ¦ÓÃÀ´½²£¬µÇ¼ÊÚȨÒԺ󣬽øÐÐwebsocketÁ¬½ÓÊÇ×îºÏÀíµÄ£¬ÎÒÃÇ¿ÉÒÔ½øÐÐȨÏ޵ķÖÅ䣬ºÍȨÏÞÏà¹ØµÄ¹ÜÀí¡£
ÎÒÄ£ÄâÀý×ÓÖУ¬Ê¹ÓõÄÊÇspring securityµÄInmemoryµÄÏà¹ØÅäÖÃ:
public class
WebSecurityConfig extends WebSecurityConfigurerAdapter
{
@Override
protected void configure (AuthenticationManagerBuilder
auth) throws Exception {
auth.inMemoryAuthentication() .withUser("admin").password
("admin").roles (Role.CUSTOMER_SERVICE.name());
auth.inMemoryAuthentication() .withUser("admin1").password
("admin").roles (Role.CUSTOMER_SERVICE.name());
auth.inMemoryAuthentication() .withUser("user").password
("user").roles(Role.CUSTOMER.name());
auth.inMemoryAuthentication() .withUser("user1").password
("user").roles(Role.CUSTOMER.name());
auth.inMemoryAuthentication() .withUser("user2").password
("user").roles(Role.CUSTOMER.name());
auth.inMemoryAuthentication() .withUser("user3").password
("user").roles(Role.CUSTOMER.name());
}
@Override
protected void configure (HttpSecurity http)
throws Exception {
http.csrf().disable()
.formLogin()
.and()
.authorizeRequests()
.anyRequest()
.authenticated();
}
} |
Ïà¶Ô½ÏΪ¼òµ¥£¬´´½¨2¸ö¿Í»§£¬4¸öÆÕͨÓû§¡£
µ±ÈÏÖ¤¹ÜÀíÆ÷ÈÏÖ¤ºó£¬»á½«ÈÏÖ¤ºóµÄºÏ·¨ÈÏÖ¤°²È«¶ÔÏóuser(¼´ ÈÏÖ¤ºóµÄtoken)·ÅÈëSTOMPµÄheaderÖÐ.
´ËÀýÖУ¬ÈÏÖ¤¹ÜÀíÈÏÖ¤Ö®ºó£¬ÈÏÖ¤µÄtokenΪorg.springframework
.security.authentication .UsernamePasswordAuthenticationToken,
´ËtokenÈÏÖ¤ºó£¬½«·ÅÈëwebsocketµÄheaderÖС££¨¼´ ºó±ß»á̸µ½µÄ°²È«¶ÔÏó java.security.Principal)
ͨѶ²ãÉè¼Æ - websocketÅäÖÃ
@Order(Ordered.HIGHEST_PRECEDENCE
+ 99)
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBroker Configurer
{
@Override
public void registerStompEndpoints(StompEndpointRegistry
registry) {
registry.addEndpoint("/portfolio").withSockJS();
}
@Override
public void configureMessageBroker (MessageBrokerRegistry
config) {
config.setApplicationDestinationPrefixes("/app");
config.enableSimpleBroker("/topic",
"/queue");
}
} |
´ËÅäÖÃÖУ¬Óм¸µãÐè½øÐн²½â£º
ÆäÖж˵㡱portfolio¡±,ÓÃÓÚsocktJs½øÐÐwebsocketÁ¬½ÓʱʹÓã¬Ö»ÓÃÓÚ½¨Á¢Á¬½Ó¡£
¡°/topic¡±, ¡°/queue¡±,ÔòΪSTOMPµÄÓïÒåÔ¼Êø£¬topicÓïÒåΪ1-n(¹ã²¥»úÖÆ),queueÓïÒåΪ1-1(µ¥µã»úÖÆ)
¡°app¡±,´ËΪӦÓü¶±ðµÄÓ³ÉäÖÕµãǰ׺£¬ÕâÑù˵ÓÐЩ»Þɬ£¬Ò»»á¿´Ò»ÏÂʾÀý½«»áÇåÎúºÜ¶à¡£
ͨѶ²ãÉè¼Æ - ´´½¨Á¬½Ó
ÓÃÓÚÁ¬½Óspring websocketµÄ¶ËµãΪportfolio£¬Ëü¿ÉÓÃÓÚÁ¬½Ó£¬¿´Ò»Ï¾ßÌåʵÏÖ:
<script src= "http://cdn.bootcss.com/sockjs-client/1.1.1/sockjs.min.js"></script>
<script src= "http://cdn.bootcss.com/stomp.js/2.3.3/stomp.js"></script>
<script src= "http://cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script>
var socket = new SockJS("/portfolio");
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
showGreeting("µÇ¼Óû§: " + frame.headers["user-name"]);
}); |
ÕâÑù±ã½¨Á¢ÁËÁ¬½Ó¡£ ºóÐøµÄÆäËû²Ù×÷¾Í¿ÉÒÔͨ¹ýstompClient¾ä±ú½øÐÐʹÓÃÁË¡£
ͨѶ²ãÉè¼Æ - spring websocketÏûϢģÐÍ
¼ûÄ£ÐÍͼ:

message-flow-simple-broker
´ËͼÀ´Ô´spring-websocket¹Ù·½Îĵµ
¿ÉÒÔ¿´³ö¶ÔÓÚͬһ¶¨ÓÚÄ¿±ê¶¼Îª:/topic/broadcast,ËüµÄ·¢ËÍÇþµÀΪÁ½ÖÖ:/app/broadcastºÍ/topic/broadcast
Èç¹ûΪ/topic/broadcast,Ö±½Ó¿É½«ÏûÏ¢Ìå·¢Ë͸ø¶¨ÓÚÄ¿±ê£¨/topic/broadcast£©¡£
Èç¹ûÊÇ/app/broadcast£¬Ëü½«ÏûÏ¢¶ÔÓ¦ÔÚMessageHandler·½·¨ÖнøÐд¦Àí£¬´¦ÀíºóµÄ½á¹û·¢·Åµ½broker
channelÖУ¬×îºóÔÙ½²ÏûÏ¢Ìå·¢Ë͸øÄ¿±ê(/topic/broadcast)
µ±È»£¬ÕâÀï±ßËù˵µÄappǰ׺¾ÍÊǸղÅÎÒÃÇÔÚwebsocketÅäÖÃÖеÄǰ׺.
¿´Ò»¸öÀý×Ó:
ǰ¶Ë¶©ÔÄ:
stompClient.subscribe
('/topic/broadcast', function(greeting){
showGreeting(greeting.body);
}); |
ºó¶Ë·þÎñ:
@Controller
public class ChatWebSocket extends AbstractWebSocket{
@MessageMapping("broadcast")
public String broadcast (@Payload @Validated Message
message, Principal principal) {
return "·¢ËÍÈË: " + principal.getName()
+ " ÄÚÈÝ: " + message.toString();
}
}
@Data
public class Message {
@NotNull(message = "±êÌâ²»ÄÜΪ¿Õ")
private String title;
private String content;
} |
ǰ¶Ë·¢ËÍ:
function sendBroadcast()
{
stompClient.send ("/app/broadcast",{},JSON.stringify({'content':'message
content'}));
} |
ÕâÖÖ·¢Ëͽ«ÏûÏ¢·¢Ë͸øºó¶Ë´øÓÐ@MessageMapping×¢½âµÄ·½·¨£¬È»ºó×éºÏÍêÊý¾ÝÒÔºó£¬ÔÚÍÆË͸ø¶©ÔÄ/topic/broadcastµÄǰ¶Ë
function sendBroadcast()
{
stompClient.send ("/topic/broadcast",{},JSON.stringify({'content':'message
content'}));
} |
ÕâÖÖ·¢ËÍÖ±½Ó½«ÏûÏ¢·¢Ë͸ø¶©ÔÄ/topic/broadcastµÄǰ¶Ë£¬²¢²»Í¨¹ý×¢½â·½·¨½øÐÐÁ÷ת¡£
ÎÒÏàÐÅÉÏÊöÕâ¸öÀí½âÒѾ½âÊÍÇå³þÁËspring websocketµÄÏûϢģÐÍͼ
ͨѶ²ãÉè¼Æ - @MessageMapping
´øÓÐÕâ¸ö×¢½âµÄ@ControllerÏµķ½·¨£¬ÕýÊǶÔÓ¦websocketÖеÄÖÐתÊý¾ÝµÄ´¦Àí·½·¨¡£
ÄÇôÕâ¸ö×¢½âÏµķ½·¨¾¿¾¹¿ÉÒÔ»ñÈ¡ÄÄЩÊý¾Ý£¬ÆäÖÐÓÐʲôÔÀíÄØ£¿

·½·¨ËµÃ÷
ÎÒ´ó¸Å˵һÏ£º
Message£¬@Payload£¬@Header£¬@Headers
£¬MessageHeaders£¬ MessageHeaderAccessor, SimpMessageHeaderAccessor£¬StompHeaderAccessor
ÒÔÉÏÕâЩ¶¼ÊÇ»ñÈ¡ÏûϢͷ£¬ÏûÏ¢Ì壬»òÕû¸öÏûÏ¢µÄ»ù±¾¶ÔÏóÄ£ÐÍ¡£
@DestinationVariable
Õâ¸ö×¢½âÓÃÓÚ¶¯Ì¬¼àÌý·¾¶£¬ºÜÏërestÖеÄ@PathVariable:
e.g.:
@MessageMapping("/queue/chat/{uid}")
public void chat (@Payload @Validated Message
message, @DestinationVariable("uid")
String uid, Principal principal) {
String msg = "·¢ËÍÈË: " + principal.getName()
+ " chat ";
simpMessagingTemplate .convertAndSendToUser (uid,"/queue/chat",msg);
} |
java.security.Principal
Õâ¸ö¶ÔÏóÎÒÐèÒªÖØµã˵һÏ¡£
ËûÔòÊÇspring securityÈÏÖ¤Ö®ºó£¬²úÉúµÄToken¶ÔÏó,¼´±¾ÀýÖеÄUsernamePasswordAuthenticationToken.

UsernamePasswordAuthenticationTokenÀàͼ
²»ÄÑ·¢ÏÖUsernamePasswordAuthenticationTokenÊÇPrincipalµÄÒ»¸öʵÏÖ.
¿ÉÒÔ½«PrincipalÖ±½Óת³ÉÊÚȨºóµÄtoken,½øÐвÙ×÷:
UsernamePasswordAuthenticationToken
user = (UsernamePasswordAuthenticationToken) principal; |
ÕýÈçǰ±ßÉè¼ÆÕ½ÚËù˵£¬Õû¸öÓû§Éè¼Æ¶¼ÊǶÔorg.springframework.security.core.userdetails.User½øÐвÙ×÷£¬ÄÇÈçºÎÄõ½User¶ÔÏóÄØ¡£
ºÜ¼òµ¥,ÈçÏÂ:
UsernamePasswordAuthenticationToken
user = (UsernamePasswordAuthenticationToken) principal;
User user = (User) user.getPrincipal() |
ͨѶ²ãÉè¼Æ - 1-1 && 1-n
1-n topic:
´Ë·½Ê½£¬ÉÏÊöÏûϢģÐÍÕ½ÚÒѾ½²¹ý£¬´Ë´¦²»ÔÙ׸Êö
1-1 queue:
¿Í·þ-Óû§¹µÍ¨Îª1-1Óû§½»»¥µÄ°¸Àý
ǰ¶Ë:
stompClient.subscribe
('/user/queue/chat',function(greeting){
showGreeting(greeting.body);
}); |
ºó¶Ë:
@MessageMapping("/queue/chat/{uid}")
public void chat (@Payload @Validated Message message,
@DestinationVariable ("uid") String uid,
Principal principal) {
String msg = "·¢ËÍÈË: " + principal.getName()
+ " chat ";
simpMessagingTemplate. convertAndSendToUser (uid,"/queue/chat",msg);
}; |
·¢ËͶË:
function chat(uid)
{
stompClient.send ("/app/queue/chat/" +uid, {},JSON.stringify ({'title': 'hello','content' :'message
content'}));
} |
ÉÏÊöµÄת»¯£¬¿´ÉÏȥûÓÐtopicÄÇÑù1-nµÄ¹ã²¥ÒªÁ÷³©£¬ÒòΪ´úÂëÖвÉÓÃÔ¼¶¨µÄ·½Ê½½øÐпª·¢£¬µ±È»ÕâÊÇÓÉspringÔ¼¶¨µÄ¡£
Ô¼¶¨×ª»¯µÄ´¦ÀíÆ÷ΪUserDestinationMessageHandler¡£
´ó¸ÅµÄÓïÒåÂß¼ÈçÏÂ:
¡°An application can send messages targeting a specific
user, and Spring¡¯s STOMP support recognizes destinations
prefixed with ¡°/user/¡° for this purpose. For example,
a client might subscribe to the destination ¡°/user/queue/position-updates¡±.
This destination will be handled by the UserDestinationMessageHandler
and transformed into a destination unique to the user
session, e.g. ¡°/queue/position-updates-user123¡±. This
provides the convenience of subscribing to a generically
named destination while at the same time ensuring
no collisions with other users subscribing to the
same destination so that each user can receive unique
stock position updates.¡±
´óÖµÄÒâ˼ÊÇ˵:Èç¹ûÊǿͻ§¶Ë¶©ÔÄÁË/user/queue/position-updates,
½«ÓÉUserDestinationMessageHandlerת»¯ÎªÒ»¸ö»ùÓÚÓû§»á»°µÄ¶©ÔĵØÖ·,±ÈÈç/queue/position-updates-user123£¬È»ºó¿ÉÒÔ½øÐÐͨѶ¡£
Àý×ÓÖУ¬ÎÒÃÇ¿ÉÒÔ°Ñuidµ±³ÉÓû§µÄ»á»°£¬ÒòΪÓû§1-1ͨѶÊÇͨ¹ýspring securityÊÚȨµÄ£¬ËùÒÔÎÒÃÇ¿ÉÒ԰ѻỰµ±×öÊÚȨºóµÄtoken.
ÈçµÇ¼Óû§tokenΪ: UsernamePassword AuthenticationToken newToken
= new UsernamePassword AuthenticationToken (¡°admin¡±,¡±user¡±);
ÇÒÕâ¸ötokenÊǺϷ¨µÄ£¬ÄÇô/user/queue/chat¶©ÔÄÔòΪ/queue/chat-admin
·¢ËÍʱ£¬Èç¹ûͨ¹ý/user/admin/queue/chat,Ôò²»Í¨¹ý@MessageMappingÖ±½Ó½øÐÐÍÆËÍ¡£
Èç¹ûͨ¹ý/app/queue/chat/admin,Ôò½«ÏûÏ¢ÓÉ@MessageMapping×¢½â´¦Àí£¬×îÖÕ·¢Ë͸ø/user/admin/queue/chatÖÕµã
×·×Ù´úÂësimpMessaging Templat e.convertAndSendToUser:
@Override
public void convertAndSendToUser(String user,
String destination, Object payload, Map<String,
Object> headers,
MessagePostProcessor postProcessor) throws MessagingException
{
Assert.notNull(user, "User must not be
null");
user = StringUtils.replace (user, "/",
"%2F");
super.convertAndSend (this.destinationPrefix
+ user + destination, payload, headers, postProcessor);
} |
˵Ã÷×îºóµÄ·¾¶ÒÀÈ»ÊÇ/user/admin/queue/chatÖÕµã.
ͨѶ²ãÉè¼Æ - @SubscribeMapping
@SubscribeMapping×¢½â¿ÉÒÔÍê³É¶©Ôļ´·µ»ØµÄ¹¦ÄÜ¡£
Õâ¸öºÜÏñHTTPµÄrequest-response£¬µ«²»Í¬µÄÊÇHTTPµÄÇëÇóºÍÏìÓ¦ÊÇͬ²½µÄ£¬Ã¿´ÎÇëÇó±ØÐëµÃµ½ÏìÓ¦¡£
¶ø@SubscribeMappingÔòÊÇÒì²½µÄ¡£Òâ˼ÊÇ˵£ºµ±¶©ÔÄʱ£¬Ö±µ½»ØÓ¦¿ÉÏìӦʱÔÚ½øÐд¦Àí¡£
ͨѶ²ãÉè¼Æ - Òì³£´¦Àí
@MessageMappingÊÇÖ§³Öjsr 303УÑéµÄ£¬ËüÖ§³Ö@Validated×¢½â£¬¿ÉÅ׳ö´íÎóÒì³£,ÈçÏÂ:
@MessageMapping ("broadcast")
public String broadcast (@Payload @Validated Message
message, Principal principal) {
return "·¢ËÍÈË: " + principal.getName()
+ " ÄÚÈÝ: " + message.toString();
} |
ÄÇÒì³£ÈçºÎ´¦ÀíÄØ
@MessageExceptionHandler,Ëü¿ÉÒÔ½øÐÐÏûÏ¢²ãµÄÒì³£´¦Àí
@MessageExceptionHandler
@SendToUser (value = "/queue/error",broadcast
= false)
public String handleException (MethodArgumentNotValidException
methodArgumentNotValidException) {
BindingResult bindingResult = methodArgumentNotValidException
.getBindingResult();
if (!bindingResult.hasErrors()) {
return "δ֪´íÎó";
}
List<FieldError> allErrors = bindingResult.getFieldErrors();
return "jsr 303 ´íÎó: " + allErrors.iterator().next() .getDefaultMessage();
} |
ÆäÖÐ@SendToUser£¬ÊÇÖ¸Ö»½«ÏûÏ¢·¢Ë͸øµ±Ç°Óû§£¬µ±È»£¬µ±Ç°Óû§ÐèÒª¶©ÔÄ/user/queue/errorµØÖ·¡£
×¢½âÖÐbroadcast,Ôò±íÃ÷ÏûÏ¢²»½øÐжà»á»°µÄ´«²¥(ÓпÉÄÜÒ»¸öÓû§µÇ¼3¸öä¯ÀÀÆ÷£¬ÓÐÈý¸ö»á»°)£¬Èç¹û´Ëbroadcast=false£¬ÔòÖ»´«¸øµ±Ç°»á»°£¬²»½øÐÐÆäËû»á»°´«²¥
×ܽá
±¾ÎÄ´ÓwebsocketµÄÔÀíºÍÐÒ飬ÒÔ¼°ÄÚÈÝÏà¹ØÐÒéµÈ²»Í¬Î¬¶È½øÐÐÁËÏêϸ½éÉÜ¡£
×îÖÕÒÔÒ»¸öÓ¦Ó󡾰ΪÀý£¬´ÓÏîÄ¿µÄ½á¹¹Éè¼Æ£¬ÒÔ¼°´úÂë²ßÂÔÉè¼Æ£¬Éè¼ÆÄ£Ê½µÈ²»Í¬·½ÃæÕ¹Ê¾ÁËwebsocketµÄͨѶ¹¦ÄÜÔÚÏîÄ¿ÖеÄʹÓá£
ÈçºÎʵÏÖijһ¹¦ÄÜÆäʵ²¢²»ÖØÒª£¬ÖØÒªµÄÊǵÃÁ˽âÀíÂÛ£¬ÉîÈëÀíÂÛÖ®ºó£¬ÔÙ½øÐпª·¢¡£
|