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 :
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 :
3、 If we enter a wrong token, What will happen? ?
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 :
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 :
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 sample address
The above code has been uploaded git, The sample address :
Code cloud :sa-token-demo-websocket
Reference material
- Gitee Address :https://gitee.com/dromara/sa-token
- GitHub Address :https://github.com/dromara/sa-token
- Sa-Token Official website :https://sa-token.dev33.cn/