Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/spring/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/amazon-s3/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何使用spring安全性以编程方式注销用户_Java_Spring_Spring Mvc_Spring Security - Fatal编程技术网

Java 如何使用spring安全性以编程方式注销用户

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

我使用的是SpringSecurity v3.1.4。我想要实现的是让管理员能够注销普通用户(使其会话无效)。用户在任何给定时间只能登录一次,但如果他忘记注销,那么当他尝试从其他位置登录时,他将无法登录。因此,他将向管理员提交一张罚单,管理员将使他以前登录的所有会话无效(希望只有一个会话)

在我的web.xml中,我定义了以下内容

<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登录该应用程序
然而,用户_A现在登录了两次,一次是在chrome上,一次是在firefox上

  • 在chrome(首次登录)中刷新用户_A的(受spring安全保护的)页面不会强制他重定向(登录页面)
  • 在firefox中刷新用户_A的受保护页面(第二次登录)也不会强制用户重定向
关于如何完全使用户的第一次/以前的登录会话失效/销毁,以便他尝试访问受保护的页面,spring security会知道,“嘿,这家伙的会话无效或过期,请将他发送到登录页面”的方法,您有什么想法吗


感谢您的帮助。谢谢。

看起来您已经差不多拿到了,但我认为问题是您过早地从
会话注册表中删除了信息。当用户发出请求时,将对当前会话执行检查,此时,它将注销过期的会话并使其无效。由于您已经删除了该会话的信息,因此该会话将找不到该信息,并且不会执行任何操作

尝试删除该行:

sessionRegistry.removeSessionInformation(info.getSessionId());

您只需修改
equals()
hashCode()
。您的代码稍后将运行。这可能会有帮助:

谢谢,删除这一行肯定会消除这种副作用。但是,当我刷新受spring保护的页面时,会收到这样一条消息,“此会话已过期(可能是由于同一用户尝试了多个并发登录)。”重定向是正确的,它重定向到/home,但不显示/home的内容,而是显示该消息,然后我必须刷新它才能显示真正的内容。这个消息是从哪里来的?看起来不像是JEE集装箱。我该如何定制它呢?好的,我的后续工作已经有了答案。您可以在concurrency-control.Right中控制过期的url。是查找配置属性的好地方。另外,它们将出现在一个像样的XML编辑器中。这里的旁注是:我使用一个自定义会话注册表,在启动时将tomcat会话从磁盘加载到会话注册表中。删除这一行使会话注册表在会话过期时不会删除会话,直到下一个请求。如果服务器在下一个请求之前重新启动,则tomcat将持久化会话,在启动时加载,然后在下一个请求时重新启动。我通过创建一个事件监听器修复了这个问题,该监听器在
SessionDestroyedEvent
发布时侦听,并在那里读取这一行。