当前位置:网站首页>Using SA token to solve websocket handshake authentication

Using SA token to solve websocket handshake authentication

2022-07-06 01:58:00 Here comes the governor

Preface

Compared with Http Single communication mode ,WebSocket You can actively push messages from the server to the browser , This feature can help us accomplish things like Order message push 、IM Live chat And other specific businesses .

However WebSocket I am right “ Identity Authentication ” No direct support , The default connection to the client is “ All's fish that comes to his net ”, So authentication and authorization , We have to do it ourselves .

Sa-Token It's a java Authorization framework , It mainly solves login authentication 、 Permission authentication 、 Single sign on 、OAuth2、 Micro service gateway Authentication And so on .
GitHub Open source address :https://github.com/dromara/sa-token

Now let's introduce how to WebSocket In the integration Sa-Token Identity Authentication , Ensure the security of the connection .

Two ways to integrate

We will introduce the two most common integrations in turn WebSocket The way :

  • Java Original version :javax.websocket.Session
  • Spring Package version :WebSocketSession

I don't say much nonsense , Directly :

Mode one :Java Original version javax.websocket.Session

1、 The first is introduction pom.xml rely on
<!-- SpringBoot rely on  -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- WebScoket  rely on  -->
<dependency>  
	<groupId>org.springframework.boot</groupId>  
	<artifactId>spring-boot-starter-websocket</artifactId>  
</dependency>

<!-- Sa-Token  Permission authentication ,  Online document :http://sa-token.dev33.cn/ -->
<dependency>
	<groupId>cn.dev33</groupId>
	<artifactId>sa-token-spring-boot-starter</artifactId>
	<version>1.29.0</version>
</dependency>
2、 Login interface , Used to get a session token
/**
 *  Log on to the test  
 */
@RestController
@RequestMapping("/acc/")
public class LoginController {

	//  Test login   ---- http://localhost:8081/acc/doLogin?name=zhang&pwd=123456
	@RequestMapping("doLogin")
	public SaResult doLogin(String name, String pwd) {
		//  Here is only a simulation example , Real projects need to query data from the database for comparison  
		if("zhang".equals(name) && "123456".equals(pwd)) {
			StpUtil.login(10001);
			return SaResult.ok(" Login successful ").set("token", StpUtil.getTokenValue());
		}
		return SaResult.error(" Login failed ");
	}

	// ... 
	
}
3、WebSocket Connection processing
@Component
@ServerEndpoint("/ws-connect/{satoken}")
public class WebSocketConnect {

    /**
     *  Fixed prefix  
     */
    private static final String USER_ID = "user_id_";
	
	 /** 
	  *  Deposit Session aggregate , Easy to push messages  (javax.websocket.Session)  
	  */
    private static ConcurrentHashMap<String, Session> sessionMap = new ConcurrentHashMap<>();
    
	//  monitor : Successful connection 
	@OnOpen
	public void onOpen(Session session, @PathParam("satoken") String satoken) throws IOException {
		
		//  according to  token  Get the corresponding  userId 
		Object loginId = StpUtil.getLoginIdByToken(satoken);
		if(loginId == null) {
			session.close();
			throw new SaTokenException(" The connection fails , Invalid Token:" + satoken);
		}
		
		// put To the assembly , Convenient follow-up operation  
		long userId = SaFoxUtil.getValueByType(loginId, long.class);
		sessionMap.put(USER_ID + userId, session);
		
		//  Give me a hint.  
		String tips = "Web-Socket  Successful connection ,sid=" + session.getId() + ",userId=" + userId;
		System.out.println(tips);
		sendMessage(session, tips);
	}

	//  monitor :  Connection is closed 
	@OnClose
	public void onClose(Session session) {
		System.out.println(" Connection is closed ,sid=" + session.getId());
		for (String key : sessionMap.keySet()) {
			if(sessionMap.get(key).getId().equals(session.getId())) {
				sessionMap.remove(key);
			}
		}
	}
	
	//  monitor : Received a message from the client  
	@OnMessage
	public void onMessage(Session session, String message) {
		System.out.println("sid by :" + session.getId() + ", From :" + message);
	}
	
	//  monitor : Something goes wrong  
	@OnError
	public void onError(Session session, Throwable error) {
		System.out.println("sid by :" + session.getId() + ", An error occurred ");
		error.printStackTrace();
	}
	
	// ---------
	
	//  Push a message to the specified client  
	public static void sendMessage(Session session, String message) {
		try {
			System.out.println(" towards sid by :" + session.getId() + ", send out :" + message);
			session.getBasicRemote().sendText(message);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
	
	//  Push a message to a specified user  
	public static void sendMessage(long userId, String message) {
		Session session = sessionMap.get(USER_ID + userId);
		if(session != null) {
			sendMessage(session, message);
		}
	}
	
}
4、WebSocket To configure
/**
 *  Turn on WebSocket Support 
 */
@Configuration  
public class WebSocketConfig { 
	
	@Bean  
	public ServerEndpointExporter serverEndpointExporter() {  
		return new ServerEndpointExporter();  
	}
	
} 
5、 Start class
@SpringBootApplication
public class SaTokenWebSocketApplication {

	public static void main(String[] args) {
		SpringApplication.run(SaTokenWebSocketApplication.class, args); 
	}
	
}

Set up , Start project

6、 test

1、 First, we access the login interface , Get the conversation token

http://localhost:8081/acc/doLogin?name=zhang&pwd=123456

As shown in the figure :

 token

2、 Then we'll find one WebSocket Connect to the online test page
, for example :https://www.bejson.com/httputil/websocket/

Connection address :

ws://localhost:8081/ws-connect/302ee2f8-60aa-42aa-8ecb-eeae5ba57015

As shown in the figure :

 Test connection

3、 If we enter a wrong token, What will happen? ?

 The connection fails

You can see , The connection will be immediately disconnected !

Mode two :Spring Package version :WebSocketSession

1、 ditto : The first is introduction pom.xml rely on
<!-- SpringBoot rely on  -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- WebScoket  rely on  -->
<dependency>  
	<groupId>org.springframework.boot</groupId>  
	<artifactId>spring-boot-starter-websocket</artifactId>  
</dependency>

<!-- Sa-Token  Permission authentication ,  Online document :http://sa-token.dev33.cn/ -->
<dependency>
	<groupId>cn.dev33</groupId>
	<artifactId>sa-token-spring-boot-starter</artifactId>
	<version>1.29.0</version>
</dependency>
2、 Login interface , Used to get a session token
/**
 *  Log on to the test  
 */
@RestController
@RequestMapping("/acc/")
public class LoginController {

	//  Test login   ---- http://localhost:8081/acc/doLogin?name=zhang&pwd=123456
	@RequestMapping("doLogin")
	public SaResult doLogin(String name, String pwd) {
		//  Here is only a simulation example , Real projects need to query data from the database for comparison  
		if("zhang".equals(name) && "123456".equals(pwd)) {
			StpUtil.login(10001);
			return SaResult.ok(" Login successful ").set("token", StpUtil.getTokenValue());
		}
		return SaResult.error(" Login failed ");
	}

	// ... 
	
}
3、WebSocket Connection processing
/**
 *  Handle  WebSocket  Connect  
 */
public class MyWebSocketHandler extends TextWebSocketHandler {

    /**
     *  Fixed prefix  
     */
    private static final String USER_ID = "user_id_";
    
    /**
     *  Deposit Session aggregate , Easy to push messages 
     */
    private static ConcurrentHashMap<String, WebSocketSession> webSocketSessionMaps = new ConcurrentHashMap<>();

    //  monitor : Connection on  
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {

    	// put To the assembly , Convenient follow-up operation  
        String userId = session.getAttributes().get("userId").toString();
        webSocketSessionMaps.put(USER_ID + userId, session);
        

		//  Give me a hint.  
		String tips = "Web-Socket  Successful connection ,sid=" + session.getId() + ",userId=" + userId;
		System.out.println(tips);
		sendMessage(session, tips);
    }
    
    //  monitor : Connection is closed  
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
    	//  Remove from collection  
        String userId = session.getAttributes().get("userId").toString();
        webSocketSessionMaps.remove(USER_ID + userId);
        
        //  Give me a hint.  
        String tips = "Web-Socket  Connection is closed ,sid=" + session.getId() + ",userId=" + userId;
    	System.out.println(tips);
    }

    //  Received a message  
    @Override
    public void handleTextMessage(WebSocketSession session, TextMessage message) throws IOException {
    	System.out.println("sid by :" + session.getId() + ", From :" + message);
    }

    // ----------- 
    
    //  Push a message to the specified client  
 	public static void sendMessage(WebSocketSession session, String message) {
 		try {
 			System.out.println(" towards sid by :" + session.getId() + ", send out :" + message);
 			session.sendMessage(new TextMessage(message));
 		} catch (IOException e) {
 			throw new RuntimeException(e);
 		}
 	}
 	
 	//  Push a message to a specified user  
 	public static void sendMessage(long userId, String message) {
 		WebSocketSession session = webSocketSessionMaps.get(USER_ID + userId);
		if(session != null) {
			sendMessage(session, message);
		}
 	}
    
}
4、WebSocket Front interceptor
/**
 * WebSocket  The front interceptor of handshake  
 */
public class WebSocketInterceptor implements HandshakeInterceptor {

	//  Trigger before handshake  (return true  Will shake hands successfully  )
	@Override
	public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler handler,
			Map<String, Object> attr) {
		
		System.out.println("----  Trigger before handshake  " + StpUtil.getTokenValue());
		
		//  Refuse to shake hands without logging in  
		if(StpUtil.isLogin() == false) {
			System.out.println("----  Unauthorized client , The connection fails ");
			return false;
		}
		
		//  Mark  userId, Shake hands with success  
		attr.put("userId", StpUtil.getLoginIdAsLong());
		return true;
	}

	//  Trigger after handshake  
	@Override
	public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
			Exception exception) {
		System.out.println("----  Trigger after handshake  ");
	}
	
}
5、WebSocket To configure
/**
 * WebSocket  Related configuration  
 */
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
	
	//  register  WebSocket  processor  
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {
        webSocketHandlerRegistry
        		// WebSocket  Connecting the processor  
                .addHandler(new MyWebSocketHandler(), "/ws-connect")
                // WebSocket  Interceptor  
                .addInterceptors(new WebSocketInterceptor())
                //  Allow cross-domain  
                .setAllowedOrigins("*");
    }

}
6、 Start class
/**
 * Sa-Token  Integrate  WebSocket  Authentication example  
 */
@SpringBootApplication
public class SaTokenWebSocketSpringApplication {

	public static void main(String[] args) {
		SpringApplication.run(SaTokenWebSocketSpringApplication.class, args); 
	}
	
}

Start project , Start testing

7、 test

1、 First access the login interface , Get the conversation token

http://localhost:8081/acc/doLogin?name=zhang&pwd=123456

As shown in the figure :

 token

2、 Then open the WebSocket Connect to the online test page
, for example :https://www.bejson.com/httputil/websocket/

Connection address :

ws://localhost:8081/ws-connect?satoken=fe6e7dbd-38b8-4de2-ae05-cda7e36bf2f7

As shown in the figure :

 Test connection

notes : Here the url Pass on Token Because it is more convenient on the third-party test page , In real projects, you can learn from Cookie、Header Parameters 、url Parameters Choose one of three ways to pass the session token , Same effect

3、 If you enter an incorrect Token

 The connection fails

The connection fails !

The sample address

The above code has been uploaded git, The sample address :
Code cloud :sa-token-demo-websocket

Reference material

原网站

版权声明
本文为[Here comes the governor]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/02/202202140053534371.html