前言

刚开始想进行认证感觉还是蛮简单的,也只需要让websocket 的Headers上也带authorization在加上token 的值不就完事了吗。

但websocket 不支持自定义Headers头所以不行,但ws 允许在Sec-WebSocket-Protocol 的请求头加入自定义的参数,也就是说只需要在spring security 中在加入一个对于Sec-WebSocket-Protocol的检测就行了。

添加对 Sec-WebSocket-Protocol 的判断

这里的WSHeader其实就是Sec-WebSocket-Protocol

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 获取请求token
*
* @param request
* @return token
*/
private String getToken(HttpServletRequest request) {
String token = request.getHeader(header);
if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) {
token = token.replace(Constants.TOKEN_PREFIX, "");
} else {
//如果未从请求头中获取到token,则尝试从Sec-WebSocket-Protocol中取出
token = request.getHeader(WSHeader);
//如果有值,一定要在response的header中设置,否则还是会断开
if (StringUtils.isNotEmpty(token)) {
HttpServletResponse response = ServletUtils.getResponse();
response.setHeader(WSHeader, token);
}
}
return token;
}

经过这样的配置我们的Websocket 的连接端点是肯定可以通过认证了,但还要拿到SpringSecuiry中的已经登录的用户,判断它是谁?

添加获取用户信息

首先配置好Websocket 的配置

1
2
3
4
5
6
7
8
9
10
@Configuration
public class WebSocketConfig {
/**
* 注入一个ServerEndpointExporter,该Bean会自动注册使用@ServerEndpoint注解申明的websocket endpoint
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}

这里在配置一个websocket 的连接接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@ServerEndpoint("/ws")
@Component
public class WebSocketController {
private Session session;
@OnOpen
public void OnOpen(Session session) throws IOException {
this.session = session;
}

@OnMessage
public void OnMessage(String msg) throws IOException {
}

@OnClose
public void OnClose(Session session) throws IOException {
}
}

可以看到每一个用户连入/ws 的这个都会有他们自己唯一一个session 其实如果要是直接 session.或有一个叫做 ``getUserPrincipal() 的方法继续点下去有一个getName的方法。如果继续追下去打开debug就会惊奇的发现,getUserPrincipal()的类型尽然是SpringSecurity的UsernamePasswordAuthenticationToken`的类,那么这就好办了。我写了一方法。

Spring-Security-Config-3.webp

这里的LoginUserSysUser是我已经重写好SpringSecuriy中的登录用户的类,请改成你自己的即可。

1
2
3
4
5
6
7
8
9
/**
* 获取当前登录用户信息
* @return
*/
public SysUser getSysUser() {
UsernamePasswordAuthenticationToken userPrincipal = (UsernamePasswordAuthenticationToken) this.session.getUserPrincipal();
LoginUser principal = (LoginUser) userPrincipal.getPrincipal();
return principal.getUser();
}