Spring Security 4.X(XML文件配置session超时,单点登录-session并发控制,退出/logout)

目录

前言

一、Java web设置session超时

二、session并发控制

三、退出/logout设置


前言

        本文是继SSM项目集成Spring Security 4.X版本(使用spring-security.xml 配置文件方式)_spring security4.x 会话管理配置文件版-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/u011529483/article/details/135699004?spm=1001.2014.3001.5501文章之后的配置,继续实现了 session超时,单点登录-session并发控制,退出/logout 三项配置。

在配置过程中遇到了一些问题。正因如此才使我想要写文章记录。


一、Java web设置session超时

java web应用设置session超时(默认是 30 分钟),也就是失效时间的方法有:

按优先执行权从高到底是:

1. 容器的配置中设置 如:tomcat的web.xml中设置


 30 

2. java项目的web.xml中设置(本文中采用了此种方式):

  
  
    2
  

3. 通过java代码设置

        HttpSession session = request.getSession();
        session.setMaxInactiveInterval(120); //单位秒

Spring Security 中给我们提供了security:session-management标签进行session的配置管理,spring-security.xml 配置文件截图如下:

Spring Security 4.X(XML文件配置session超时,单点登录-session并发控制,退出/logout)_第1张图片

先来看看 invalid-session-url="/s_timeout.jsp" 配置的运行效果,手动设置session超时时间为 2 分钟,项目的web.xml文件中设置:

Spring Security 4.X(XML文件配置session超时,单点登录-session并发控制,退出/logout)_第2张图片

运行项目,登录成功后,等待2分钟后访问服务器。2分钟后session超时失效。

登录成功后访问了菜单查询:Spring Security 4.X(XML文件配置session超时,单点登录-session并发控制,退出/logout)_第3张图片

2 分钟后再次访问菜单查询跳转到了超时页面,说明我们的 invalid-session-url="/s_timeout.jsp"  配置生效了。Spring Security 4.X(XML文件配置session超时,单点登录-session并发控制,退出/logout)_第4张图片

二、session并发控制

        什么是session并发控制,就是同一个账号多处客户端同一时间段发起的请求,也就生成了多个session会话。session并发控制就是控制这些session会话的数量。现在我们将session会话数量设置为 1 ,即同时段同账号只能有一个用户登录成功,后面登录的挤掉前面登录的。spring-security.xml 配置如下:

        
        
            
        

max-sessions="1" 设置session并发数量为 1。

error-if-maximum-exceeded="false" 设置为false后面登录的挤掉前面登录的。设置为true表示前面登录的保留,后面登录的被拒绝。

要使以上设置生效还需要在项目的web.xml文件中添加监听

  
  
    org.springframework.security.web.session.HttpSessionEventPublisher
  

Spring Security 4.X(XML文件配置session超时,单点登录-session并发控制,退出/logout)_第5张图片

网上很多都说了这两步配置,但是我配置后不生效。

后面我在网上查了原因,经过调试后实现了单点登录(同一个账号只能有一处登录在线)的效果。需要在 SysUser 实体类中(或自定义用户详情类中)重写equals和hashCode方法:

Spring Security 4.X(XML文件配置session超时,单点登录-session并发控制,退出/logout)_第6张图片

package com.wqbr.wqdemotwo.domain;


import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.*;

/**
 * 系统用户,封装用户数据,实现 UserDetails 接口
 * @author lv
 * @date 2024年1月11日
 */
public class SysUser implements UserDetails {

  private static final long serialVersionUID = 1L;

  private String id;
  private String username; //从UserDetails的重写方法中返回
  private String password; //从UserDetails的重写方法中返回
  private Date addtime;
  private boolean accountnonexpired; //账户是否过期,从UserDetails的重写方法中返回
  private boolean accountnonlocked; //账户是否锁定,从UserDetails的重写方法中返回
  private boolean credentialsnonexpired; //密码是否过期,从UserDetails的重写方法中返回
  private boolean enabled; //账户是否可用,从UserDetails的重写方法中返回

  // 储存用户拥有的所有权限
  private List authorities = new ArrayList<>(); //从UserDetails的重写方法中返回


  public String getId() {
    return id;
  }

  public void setId(String id) {
    this.id = id;
  }

  public void setUsername(String username) {
    this.username = username;
  }

  public void setPassword(String password) {
    this.password = password;
  }

  public Date getAddtime() {
    return addtime;
  }

  public void setAddtime(Date addtime) {
    this.addtime = addtime;
  }

  // 返回用户权限,上面声明了权限集合对象 authorities
  @Override
  public Collection getAuthorities() {
    return this.authorities;
  }

  public void setAuthorities(List authorities) {
    this.authorities = authorities;
  }

  // 返回用户密码,上面声明了属性 password
  @Override
  public String getPassword() {
    return password;
  }

  // 返回用户名,上面声明了属性 username
  @Override
  public String getUsername() {
    return username;
  }

  @Override
  public boolean isAccountNonExpired() {
    return accountnonexpired;
  }

  public void setAccountnonexpired(boolean accountnonexpired) {
    this.accountnonexpired = accountnonexpired;
  }

  @Override
  public boolean isAccountNonLocked() {
    return accountnonlocked;
  }

  public void setAccountnonlocked(boolean accountnonlocked) {
    this.accountnonlocked = accountnonlocked;
  }

  @Override
  public boolean isCredentialsNonExpired() {
    return credentialsnonexpired;
  }

  public void setCredentialsnonexpired(boolean credentialsnonexpired) {
    this.credentialsnonexpired = credentialsnonexpired;
  }

  @Override
  public boolean isEnabled() {
    return enabled;
  }

  public void setEnabled(boolean enabled) {
    this.enabled = enabled;
  }

  /*重写equals和hashCode方法,因为如果是自定义的UserDetails 则需要重定义equal和hashcode。
  另外Spring Security 中通过 SessionRegistryImpl 类来实现对会话信息的统一管理,而 SessionRegistryImpl 类中定义了
  private final ConcurrentMap> principals; 是一个Map集合,其中的key是指定的对象。
  在 JavaSE 中用对象做 key,需要重写 equals 方法和 hashCode 方法,否则第一次存完数据,下次就找不到了。*/
  @Override
  public boolean equals(Object obj) {
    if (obj instanceof SysUser) {
      return username.equals(((SysUser) obj).username);
    }
    return false;
  }
  @Override
  public int hashCode() {
    return username.hashCode();
  }

}

为什么要重写这两个方法呢?因为自己动手创建用户实体类实现自定义用户详情验证,需要显式实现equals和hashCode方法。而我的用户实体类 SysUser 实现了 UserDetails接口。没有重写equals和hashCode方法,所以

导致这样配置的效果没有实现。另外Spring Security 中通过 SessionRegistryImpl 类来实现对会话信息的统一管理,而 SessionRegistryImpl 类中定义了 private final ConcurrentMap> principals; 是一个Map集合,其中的key是指定的对象。 在 JavaSE 中用对象做 key,需要重写 equals 方法和 hashCode 方法。

现在来看看我们重写 equals 方法和 hashCode 方法后项目的运行效果:项目启动完毕后先在谷歌浏览器登录,登录成功后我访问了菜单请求如图:

Spring Security 4.X(XML文件配置session超时,单点登录-session并发控制,退出/logout)_第7张图片

 说明登录成功,并顺利访问资源。且idea控制台打印的session信息:

Spring Security 4.X(XML文件配置session超时,单点登录-session并发控制,退出/logout)_第8张图片

然后我们在另外一种浏览器(360)中再次登录,Spring Security 4.X(XML文件配置session超时,单点登录-session并发控制,退出/logout)_第9张图片

Spring Security 4.X(XML文件配置session超时,单点登录-session并发控制,退出/logout)_第10张图片

登录成功,顺利请求到资源。且idea控制台打印的session信息:Spring Security 4.X(XML文件配置session超时,单点登录-session并发控制,退出/logout)_第11张图片

可以看到360浏览器和谷歌浏览器访问的IDEA控制台打印出的session ID 不同,说明同一个用户发起了两次请求。此时再访问谷歌浏览器去请求一次资源得到如下结果:Spring Security 4.X(XML文件配置session超时,单点登录-session并发控制,退出/logout)_第12张图片

这段英文翻译如下:

继续刷新请求一次就重定向到会话超时页面:

Spring Security 4.X(XML文件配置session超时,单点登录-session并发控制,退出/logout)_第13张图片

此时360浏览器登录的用户是可以继续请求资源的。spring-security控制session并发数量,实现单点登录就实现了。

三、退出/logout设置

spring-security.xml 文件中的配置:


        

配置后运行项目,点击 退出 后 logout-success-url="/login" 重定向配置没有生效。查到原因:不能

这样配置/login路径的放行策略。需要放到spring security过滤链策略外,配置如下:


spring-security.xml 完整的配置代码:




    
    
    
    

        
        

        
        

        
        
        
        
        
        

        
        
        

        
        

        
        
        

        
        

        
        
            
        
        

        
        

        
        
    

    
    
        
        
            
            
        
    

    
    


    
    


如此配置运行效果,点击退出后重定向到了登录页面。即logout-success-url="/login"配置生效。

退出页面代码:

<%--
--%> 退出系统

另外再看看退出时的 delete-cookies="JSESSIONID" 配置,如下idea控制台的截图可以看出,退出后重定向到退出的Controller请求时session ID重新生成了,说明浏览器cookies中的上一次JSESSIONID已经失效。

Spring Security 4.X(XML文件配置session超时,单点登录-session并发控制,退出/logout)_第14张图片


好了,小伙伴们这篇文章就到这里了,希望多留下你们的足迹,谢谢。

你可能感兴趣的:(java,spring,后端)