如何使用Spring Security以编程方式注销用户
问题内容:
我正在使用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"/>
然后,我有一个类似Rest的控制器来执行注销。
@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";
}
}
当我从同一台计算机对其进行测试时,此代码“ kinda”有效。假设我们有一个用户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的(受弹簧安全性保护的)页面(首次登录)不会强制将其重定向到登录页面。
- 在Firefox(第二次登录)中刷新USER_A的受保护页面也不会强制他被重定向。
关于如何完全使USER_A的第一个/先前的登录会话无效/销毁的方法有什么想法,以便如果他尝试访问受保护的页面,spring
security将知道:“嘿,此人的会话无效或已过期,请将其发送到登录页面”?
任何帮助表示赞赏。谢谢。
问题答案:
看起来您差不多已经掌握了,但是我认为问题是您正在过早地从中删除信息SessionRegistry
。在ConcurrentSessionFilter执行对当用户发出请求的当前会话的检查,并且在这一点上,注销过期会话并使其无效。由于您已经删除了该会话的信息,因此找不到该信息,并且不会执行任何操作。
尝试删除该行:
sessionRegistry.removeSessionInformation(info.getSessionId());