使用Spring Security如何以编程方式注销用户

7
我正在使用Spring Security v3.1.4。我想要实现的目标是让管理员能够注销普通用户(使其会话无效)。任何时候一个用户只能登录一次,但如果他忘记注销,则当他试图在另一个位置登录时,将无法登录。所以他会向管理员提出申请,并且管理员将使他之前登录的所有会话无效(希望只有一个)。
在我的web.xml中,我已经定义了以下内容。
<listener>
 <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>

在我的Spring Security XML文件中,我定义了以下内容:

<session-management invalid-session-url="/home">
 <concurrency-control max-sessions="1" error-if-maximum-exceeded="true" session-registry-ref="sessionRegistry"/>
</session-management>
<beans:bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl"/>

我有一个类似于休息的控制器来执行注销操作。
@Controller
@RequestMapping("/api/admin")
public class RestAdminController {
 static final Set<SimpleGrantedAuthority> AUTHS = new HashSet<>();
 static {
  AUTHS.add(new SimpleGrantedAuthority("ROLE_USER"));
 }

 @Autowired
 private SessionRegistry sessionRegistry;

 @RequestMapping("/user/logout");
 public @ResponseBody String logout(@RequestBody Account account) {
  User user = new User(account.getUsername(), "", AUTHS);
  List<SessionInformation> infos = sessionRegistry.getAllSessions(u, false);

  for(SessionInformation info : infos) {
   info.expireNow(); //expire the session
   sessionRegistry.removeSessionInformation(info.getSessionId()); //remove session
  }

  return "ok";
 }
}

当我从同一台计算机测试代码时,“有点”可以工作。假设我们有一个用户USER_A和一个管理员ADMIN_A。
- USER_A使用Chrome登录APP。 - USER_A使用Firefox登录APP,但被拒绝了,因为一个用户只能同时拥有1个登录会话。 - ADMIN_A进入并调用REST风格的服务(上面的代码)来“踢出”USER_A的所有会话。 - USER_A现在可以使用Firefox登录APP。
然而,USER_A现在在Chrome和Firefox中都已经登录了两次。
- 在Chrome(第一次登录)中刷新USER_A的受Spring Security保护的页面不会强制重定向到登录页面。 - 在Firefox(第二次登录)中刷新USER_A的受保护页面也不会强制重定向。
有什么方法可以完全使USER_A的第一个/之前的登录会话失效/销毁,以便如果他尝试访问受Spring Security保护的页面,Spring Security将知道“嘿,这个家伙的会话无效或过期了,请将他发送到登录页面”?
感谢任何帮助。谢谢。
2个回答

15
似乎你已经接近成功了,但我认为问题在于你过早地从SessionRegistry中移除了信息。当用户发出请求时,ConcurrentSessionFilter会对当前会话进行检查,并在此时记录并使过期的会话注销并失效。因为你已经删除了该会话的信息,所以它将找不到该会话并且什么也不会做。
尝试删除这一行:
sessionRegistry.removeSessionInformation(info.getSessionId());

1
谢谢,删除那行代码确实解决了那个副作用。但是,当我刷新受Spring保护的页面时,会出现这个消息:“此会话已过期(可能是由于尝试使用相同用户进行多个并发登录)。” 重定向是正确的,它重定向到 /home,但是它不显示 /home 的内容,而是显示该消息,然后我必须刷新才能显示真正的内容。这个消息来自哪里?看起来不像是 JEE 容器。我该如何自定义它? - Jane Wayne
好的,Stack Overflow已经回答了我的后续问题。你可以在并发控制中控制过期URL。 - Jane Wayne
没错。命名空间附录是查找配置属性的好地方。此外,它们将在一个不错的XML编辑器中显示。 - Shaun the Sheep
这个注释是:我正在使用一个自定义会话注册表,它在启动时将Tomcat会话从磁盘加载到我的会话注册表中。移除此行代码将导致会话注册表直到下一个请求才删除已过期的会话。如果服务器在下一个请求之前重新启动,则会话将由Tomcat保存,加载启动时,并在下一个请求上恢复。我通过创建一个事件监听器来修复这个问题,该监听器侦听“SessionDestroyedEvent”发布的时间,并在那里重新添加此行代码。 - aradil

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接