Cometd,Spring-security:当前经过身份验证的用户在侦听器中不可用
Cometd, Spring-security : Currently authenticated user not available inside a Listener
我正在开发一个 Spring-MVC 项目,我在其中使用 Spring-security 进行身份验证和授权。除此之外,我还使用 Cometd 库通过 websockets 发送消息。在监听器中收到消息后,当我尝试获取当前经过身份验证的用户时,它始终为空。
如何确保Cometd中的每个请求至少包含Spring-security识别所需的JSESSIONID?
或者 Spring-security 中是否有一些设置可以使这成为可能。
我查了一下,有很多用户面临这个问题,但没有明确的答案或有用的代码。
测试代码:
@Listener(value = "/service/testlistener/{id}")
public void testlistener(ServerSession remote,
ServerMessage message, @Param("id") String id) {
try {
Person user = this.personService.getCurrentlyAuthenticatedUser();
Map<String, Object> data = message.getDataAsMap();
System.out.println("currentlyAuthenticatedUser: " + user + " \nTransmitted Data in map:" + data.get("name"));
Map<String, Object> output = new HashMap<>();
output.put("name", user.getFirstName());
ServerChannel serverChannel = bayeux.createChannelIfAbsent("/person/" + id).getReference();
serverChannel.setPersistent(false);
serverChannel.publish(serverSession, output);
} catch (Exception e) {
e.printStackTrace();
}
}
cometd.js :
var connectionIntervall = null;
var onlineElement = document.getElementById("browser-online");
var cometd = $.cometd;
cometd.configure({
url: navigationController.generateCometDUrl(),
logLevel: 'error',
stickyReconnect: false,
appendMessageTypeToURL: false,
requestHeaders : navigationController.generateCometDHeaders()
});
var connects = 0;
cometd.addListener('/meta/handshake', _metaHandshake);
cometd.addListener('/meta/connect', _metaConnect);
cometd.websocketEnabled = true;
cometd.handshake();
解决方案:
- CometD 4.0.2
- Spring 4.3.9.RELEASE
- Spring 安全 4.1.0.RELEASE
在我的项目中,我们在从 "long pooling" 连接切换到 "websocket" 连接时遇到了类似的问题。为了澄清 - 一个区别 - 我们仅在 "handshake".
期间使用 "user context"
使用 "long pooling" 带有 JSESSIONID 的 Cookie 被 Spring 正确解释,我们正在 "user context" 中工作。切换到 "websocket" 后,看起来 "user context" 在 运行 消息接收代码之前丢失了。
我们找不到合适的解决方案 - 以下代码在我看来只是一个 hack。但它对我们有用。
import org.cometd.bayeux.server.BayeuxServer;
import org.cometd.bayeux.server.ServerMessage;
import org.cometd.bayeux.server.ServerSession;
import org.cometd.server.DefaultSecurityPolicy;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
public class BayeuxAuthenticator extends DefaultSecurityPolicy implements ServerSession.RemoveListener {
// (...)
@Override
public boolean canHandshake( BayeuxServer server, ServerSession session, ServerMessage message ) {
SecurityContextHolder.getContext().setAuthentication( ( UsernamePasswordAuthenticationToken ) message.getBayeuxContext().getUserPrincipal() );
String user = securityService.getUserName();
// (...)
return true;
}
// (...)
}
下一行最重要
SecurityContextHolder.getContext().setAuthentication( ( UsernamePasswordAuthenticationToken ) message.getBayeuxContext().getUserPrincipal() );
编辑 2:
我们正在从
接收数据
String user = securityService.getUserName();
其中 securityService 是 Spring @Service protected with Spring @Secured(/* expected roles */)。 (@Secured 注释在设置上下文之前为我们生成授权异常)
里面有上下文读取,用户数据:
SecurityContextHolder.getContext().getUserPrincipal().getName()
但老实说,如果您只需要 "user principals",阅读它可能就足够了:
message.getBayeuxContext().getUserPrincipal() // instanceof java.security.Principal
在我们的例子中是类型
org.springframework.security.authentication.UsernamePasswordAuthenticationToken
也许你的情况是不同的子类?
澄清一下 - 在 Web 套接字连接启动期间,我们在请求中传递了带有正确 JSESSIONID 的 Cookie。
WebSocket 连接请求:
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9,pl;q=0.8
Cache-Control: no-cache
Connection: Upgrade
Cookie: XSRF-TOKEN=<some token>; Idea-<some token>; lastOpenTab=sourcing/content; io=<some token>; BAYEUX_BROWSER=<some token>; JSESSIONID=<some token>
Host: localhost:8090
Origin: http://localhost:8090
Pragma: no-cache
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Sec-WebSocket-Key: NTtWtXAL8ReIB0LjvI2G0g==
Sec-WebSocket-Version: 13
Upgrade: websocket
User-Agent: Mozilla/5.0 ...
我正在开发一个 Spring-MVC 项目,我在其中使用 Spring-security 进行身份验证和授权。除此之外,我还使用 Cometd 库通过 websockets 发送消息。在监听器中收到消息后,当我尝试获取当前经过身份验证的用户时,它始终为空。
如何确保Cometd中的每个请求至少包含Spring-security识别所需的JSESSIONID?
或者 Spring-security 中是否有一些设置可以使这成为可能。
我查了一下,有很多用户面临这个问题,但没有明确的答案或有用的代码。
测试代码:
@Listener(value = "/service/testlistener/{id}")
public void testlistener(ServerSession remote,
ServerMessage message, @Param("id") String id) {
try {
Person user = this.personService.getCurrentlyAuthenticatedUser();
Map<String, Object> data = message.getDataAsMap();
System.out.println("currentlyAuthenticatedUser: " + user + " \nTransmitted Data in map:" + data.get("name"));
Map<String, Object> output = new HashMap<>();
output.put("name", user.getFirstName());
ServerChannel serverChannel = bayeux.createChannelIfAbsent("/person/" + id).getReference();
serverChannel.setPersistent(false);
serverChannel.publish(serverSession, output);
} catch (Exception e) {
e.printStackTrace();
}
}
cometd.js :
var connectionIntervall = null;
var onlineElement = document.getElementById("browser-online");
var cometd = $.cometd;
cometd.configure({
url: navigationController.generateCometDUrl(),
logLevel: 'error',
stickyReconnect: false,
appendMessageTypeToURL: false,
requestHeaders : navigationController.generateCometDHeaders()
});
var connects = 0;
cometd.addListener('/meta/handshake', _metaHandshake);
cometd.addListener('/meta/connect', _metaConnect);
cometd.websocketEnabled = true;
cometd.handshake();
解决方案: - CometD 4.0.2 - Spring 4.3.9.RELEASE - Spring 安全 4.1.0.RELEASE
在我的项目中,我们在从 "long pooling" 连接切换到 "websocket" 连接时遇到了类似的问题。为了澄清 - 一个区别 - 我们仅在 "handshake".
期间使用 "user context"使用 "long pooling" 带有 JSESSIONID 的 Cookie 被 Spring 正确解释,我们正在 "user context" 中工作。切换到 "websocket" 后,看起来 "user context" 在 运行 消息接收代码之前丢失了。
我们找不到合适的解决方案 - 以下代码在我看来只是一个 hack。但它对我们有用。
import org.cometd.bayeux.server.BayeuxServer;
import org.cometd.bayeux.server.ServerMessage;
import org.cometd.bayeux.server.ServerSession;
import org.cometd.server.DefaultSecurityPolicy;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
public class BayeuxAuthenticator extends DefaultSecurityPolicy implements ServerSession.RemoveListener {
// (...)
@Override
public boolean canHandshake( BayeuxServer server, ServerSession session, ServerMessage message ) {
SecurityContextHolder.getContext().setAuthentication( ( UsernamePasswordAuthenticationToken ) message.getBayeuxContext().getUserPrincipal() );
String user = securityService.getUserName();
// (...)
return true;
}
// (...)
}
下一行最重要
SecurityContextHolder.getContext().setAuthentication( ( UsernamePasswordAuthenticationToken ) message.getBayeuxContext().getUserPrincipal() );
编辑 2:
我们正在从
接收数据String user = securityService.getUserName();
其中 securityService 是 Spring @Service protected with Spring @Secured(/* expected roles */)。 (@Secured 注释在设置上下文之前为我们生成授权异常)
里面有上下文读取,用户数据:
SecurityContextHolder.getContext().getUserPrincipal().getName()
但老实说,如果您只需要 "user principals",阅读它可能就足够了:
message.getBayeuxContext().getUserPrincipal() // instanceof java.security.Principal
在我们的例子中是类型
org.springframework.security.authentication.UsernamePasswordAuthenticationToken
也许你的情况是不同的子类?
澄清一下 - 在 Web 套接字连接启动期间,我们在请求中传递了带有正确 JSESSIONID 的 Cookie。
WebSocket 连接请求:
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9,pl;q=0.8
Cache-Control: no-cache
Connection: Upgrade
Cookie: XSRF-TOKEN=<some token>; Idea-<some token>; lastOpenTab=sourcing/content; io=<some token>; BAYEUX_BROWSER=<some token>; JSESSIONID=<some token>
Host: localhost:8090
Origin: http://localhost:8090
Pragma: no-cache
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Sec-WebSocket-Key: NTtWtXAL8ReIB0LjvI2G0g==
Sec-WebSocket-Version: 13
Upgrade: websocket
User-Agent: Mozilla/5.0 ...