Java 如何使用spring安全性以编程方式注销用户
我使用的是SpringSecurity v3.1.4。我想要实现的是让管理员能够注销普通用户(使其会话无效)。用户在任何给定时间只能登录一次,但如果他忘记注销,那么当他尝试从其他位置登录时,他将无法登录。因此,他将向管理员提交一张罚单,管理员将使他以前登录的所有会话无效(希望只有一个会话) 在我的web.xml中,我定义了以下内容Java 如何使用spring安全性以编程方式注销用户,java,spring,spring-mvc,spring-security,Java,Spring,Spring Mvc,Spring Security,我使用的是SpringSecurity v3.1.4。我想要实现的是让管理员能够注销普通用户(使其会话无效)。用户在任何给定时间只能登录一次,但如果他忘记注销,那么当他尝试从其他位置登录时,他将无法登录。因此,他将向管理员提交一张罚单,管理员将使他以前登录的所有会话无效(希望只有一个会话) 在我的web.xml中,我定义了以下内容 <listener> <listener-class>org.springframework.security.web.session.Ht
<listener>
<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>
<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"/>
org.springframework.security.web.session.HttpSessionEventPublisher
在我的SpringSecurityXML中定义了以下内容
<listener>
<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>
<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";
}
}
@控制器
@请求映射(“/api/admin”)
公共类重新管理控制器{
静态最终集AUTHS=newhashset();
静止的{
添加(新的SimpleGrantedAuthority(“角色用户”);
}
@自动连线
非公开会议登记处会议登记处;
@请求映射(“/user/logout”);
public@ResponseBody字符串注销(@RequestBody帐户){
User User=新用户(account.getUsername(),“”,AUTHS);
List infos=sessionRegistry.getAllSessions(u,false);
用于(会话信息:infos){
info.expireNow();//使会话过期
sessionRegistry.removeSessionInformation(info.getSessionId());//删除会话
}
返回“ok”;
}
}
当我在同一台计算机上测试它时,这个代码“有点”起作用。假设我们有一个用户,user_a和一个管理员,ADMIN_a
- 用户_A使用chrome登录应用程序
- 用户_A使用firefox登录应用程序。他被拒绝,因为用户一次只能有一个登录会话
- ADMIN_A进入,并调用其他类似服务(上面的代码)来“踢出”用户_A的所有会话
- 用户_A现在可以使用firefox登录该应用程序
- 在chrome(首次登录)中刷新用户_A的(受spring安全保护的)页面不会强制他重定向(登录页面)李>
- 在firefox中刷新用户_A的受保护页面(第二次登录)也不会强制用户重定向
感谢您的帮助。谢谢。看起来您已经差不多拿到了,但我认为问题是您过早地从
会话注册表中删除了信息。当用户发出请求时,将对当前会话执行检查,此时,它将注销过期的会话并使其无效。由于您已经删除了该会话的信息,因此该会话将找不到该信息,并且不会执行任何操作
尝试删除该行:
sessionRegistry.removeSessionInformation(info.getSessionId());
您只需修改equals()
和hashCode()
。您的代码稍后将运行。这可能会有帮助:谢谢,删除这一行肯定会消除这种副作用。但是,当我刷新受spring保护的页面时,会收到这样一条消息,“此会话已过期(可能是由于同一用户尝试了多个并发登录)。”重定向是正确的,它重定向到/home,但不显示/home的内容,而是显示该消息,然后我必须刷新它才能显示真正的内容。这个消息是从哪里来的?看起来不像是JEE集装箱。我该如何定制它呢?好的,我的后续工作已经有了答案。您可以在concurrency-control.Right中控制过期的url。是查找配置属性的好地方。另外,它们将出现在一个像样的XML编辑器中。这里的旁注是:我使用一个自定义会话注册表,在启动时将tomcat会话从磁盘加载到会话注册表中。删除这一行使会话注册表在会话过期时不会删除会话,直到下一个请求。如果服务器在下一个请求之前重新启动,则tomcat将持久化会话,在启动时加载,然后在下一个请求时重新启动。我通过创建一个事件监听器修复了这个问题,该监听器在SessionDestroyedEvent
发布时侦听,并在那里读取这一行。