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

本文主要是介绍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中设置

<session-config>
<session-timeout> 30 </session-timeout>
</session-config>

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

  <!--设置session超时,单位分钟--><session-config><session-timeout>2</session-timeout></session-config>

3. 通过java代码设置

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

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

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

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

登录成功后访问了菜单查询:

2 分钟后再次访问菜单查询跳转到了超时页面,说明我们的 invalid-session-url="/s_timeout.jsp"  配置生效了。

二、session并发控制

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

        <!-- session管理,invalid-session-url:指定使用已经超时的sessionId(保存在Cookie中)进行请求需要重定向的页面或路径。max-sessions:默认值为1,session并发数量控制。控制同一用户在系统中同时允许存在的已经通过认证的session数量。当超过这个值时,Spring Security的默认策略是将先前的设为无效。如果要限制用户再次登录可以设置concurrency-control的error-if-maximum-exceeded的值为true。--><security:session-management invalid-session-url="/s_timeout.jsp"><security:concurrency-control max-sessions="1" error-if-maximum-exceeded="false"/></security:session-management>

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

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

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

  <!--加载SpringSecurity,Session并发控制--><listener><listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class></listener>

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

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

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<GrantedAuthority> 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@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return this.authorities;}public void setAuthorities(List<GrantedAuthority> authorities) {this.authorities = authorities;}// 返回用户密码,上面声明了属性 password@Overridepublic String getPassword() {return password;}// 返回用户名,上面声明了属性 username@Overridepublic String getUsername() {return username;}@Overridepublic boolean isAccountNonExpired() {return accountnonexpired;}public void setAccountnonexpired(boolean accountnonexpired) {this.accountnonexpired = accountnonexpired;}@Overridepublic boolean isAccountNonLocked() {return accountnonlocked;}public void setAccountnonlocked(boolean accountnonlocked) {this.accountnonlocked = accountnonlocked;}@Overridepublic boolean isCredentialsNonExpired() {return credentialsnonexpired;}public void setCredentialsnonexpired(boolean credentialsnonexpired) {this.credentialsnonexpired = credentialsnonexpired;}@Overridepublic boolean isEnabled() {return enabled;}public void setEnabled(boolean enabled) {this.enabled = enabled;}/*重写equals和hashCode方法,因为如果是自定义的UserDetails 则需要重定义equal和hashcode。另外Spring Security 中通过 SessionRegistryImpl 类来实现对会话信息的统一管理,而 SessionRegistryImpl 类中定义了private final ConcurrentMap<Object, Set<String>> principals; 是一个Map集合,其中的key是指定的对象。在 JavaSE 中用对象做 key,需要重写 equals 方法和 hashCode 方法,否则第一次存完数据,下次就找不到了。*/@Overridepublic boolean equals(Object obj) {if (obj instanceof SysUser) {return username.equals(((SysUser) obj).username);}return false;}@Overridepublic int hashCode() {return username.hashCode();}}

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

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

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

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

然后我们在另外一种浏览器(360)中再次登录,

登录成功,顺利请求到资源。且idea控制台打印的session信息:

可以看到360浏览器和谷歌浏览器访问的IDEA控制台打印出的session ID 不同,说明同一个用户发起了两次请求。此时再访问谷歌浏览器去请求一次资源得到如下结果:

这段英文翻译如下:

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

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

三、退出/logout设置

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

<!--<security:logout/>:注销功能logout-url="/logout":退出过滤器默认的拦截路径,springSecurity内LogoutFilter要拦截的url(向这/logout发送请求来注销)logout-success-url:用户退出后要被重定向的urlinvalidate-session:默认为true,用户在退出后Http session失效success-handler-ref:指定一个bean(需要实现LogoutSuccessHandler接口),用来自定义退出成功后的操作delete-cookies="JSESSIONID":删除session对应的cookie--><security:logout logout-url="/logout" logout-success-url="/login" invalidate-session="true" delete-cookies="JSESSIONID"/>

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

<security:intercept-url pattern="/login" access="permitAll()"/> 这样配置/login路径的放行策略。需要放到spring security过滤链策略外,配置如下:

<!--security="none"指定不受Spring Security管理-->
<security:http security="none" pattern="/login" />

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

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:security="http://www.springframework.org/schema/security"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/securityhttp://www.springframework.org/schema/security/spring-security.xsd"><!--指定不受Spring Security管理--><security:http security="none" pattern="/login" /><!--spring过滤器链配置1) 需要拦截什么资源2) 什么资源对应什么角色权限3) 定制认证方式: HttpBasic or FormLogin4) 自定义登录页面,定义登录请求地址,定义错误处理方式--><security:http><!--使用form-login的方式进行认证login-page:指定获取登录页面的url(需要编写controller返回登录页面)login-processing-url:指定登录页面中post请求提交到哪里的url(不需要编写controller,框架已实现)default-target-url:指定登录成功后,跳转到哪个url(需要编写controller)authentication-success-handler-ref:指定登录成功后,由哪个类来进行处理authentication-failure-handler-ref:指定登录失败后,由哪个类来进行处理username-parameter:指定登录表单中用户名的input中name值,如果这里不配置,则默认为usernamepassword-parameter:指定登录表单中密码的input中name值,如果这里不配置,则默认为password--><security:form-login login-page="/login" login-processing-url="/spring_security_check"authentication-success-handler-ref="myAuthenticationSuccessHandler"authentication-failure-handler-ref="myAuthenticationFailureHandler"/><!-- 关闭csrf的保护--><security:csrf disabled="true"/><!-- 配置资源拦截规则pattern属性指定资源目录: 即需要拦截的资源  /* 代表根目录下的一级目录  /** 代表根目录下的所有目录access(SpEL)方法执行Spring EL表达式。提供如下表达式:permitALL():设置那些路径可以直接访问,不需要认证。直接返回trueisAnonymous():只有匿名用户可以访问,登录用户不可访问isAuthenticated():需要身份认证成功才能访问。如果认证用户不是匿名用户,则返回true,认证通过isFullyAuthenticated():需要身份认证成功才能访问。如果认证用户不是匿名用户或记住我的用户,则返回true,认证通过其它自行查找......--><!--开始配置拦截规则,注意拦截规则的位置顺序(如不需要身份认证的规则,要放在前面,需要身份认证的规则放在后面)--><!--permitAll()不需要身份认证,无条件放行--><!--<security:intercept-url pattern="/login" access="permitAll()"/>--><security:intercept-url pattern="/system/index" access="permitAll()"/><security:intercept-url pattern="/s_timeout.jsp" access="permitAll()"/><!--进行权限划分:hasRole('ROLE_USER'):表示拥有 ROLE_USER 权限的用户可以访问hasRole('ROLE_ALL'):表示拥有 ROLE_ALL 权限的用户可以访问--><security:intercept-url pattern="/system/add" access="hasAuthority('admin')"/><security:intercept-url pattern="/system/list" access="hasAuthority('ROLE_ALL')"/><!--permitAll()不需要身份认证,无条件放行静态资源--><security:intercept-url pattern="/js/**" access="permitAll()"/><!--拦截所有页面,需要身份认证成功才能访问。如果认证用户不是匿名用户或记住我的用户,则返回true,认证通过--><security:intercept-url pattern="/**" access="isFullyAuthenticated()"/><!--结束配置拦截规则--><!-- 自定义用户访问权限不足的处理方式(需要编写controller返回权限不足的页面) --><security:access-denied-handler error-page="/accessDeny"/><!-- session管理,invalid-session-url:指定使用已经超时的sessionId(保存在Cookie中)进行请求需要重定向的页面或路径。max-sessions:默认值为1,session并发数量控制。控制同一用户在系统中同时允许存在的已经通过认证的session数量。当超过这个值时,Spring Security的默认策略是将先前的设为无效。如果要限制用户再次登录可以设置concurrency-control的error-if-maximum-exceeded的值为true。--><security:session-management invalid-session-url="/s_timeout.jsp"><security:concurrency-control max-sessions="1" error-if-maximum-exceeded="false"/></security:session-management><!--<security:session-management session-authentication-error-url="/session" invalid-session-url="/session"><security:concurrency-control max-sessions="1" session-registry-alias="sessionRegistry" error-if-maximum-exceeded="false" expired-url="/session"/></security:session-management>--><!--加上Remember Me功能,token-validity-seconds:有效时间(秒)--><!--<security:remember-me token-repository-ref="jdbcTokenRepository" token-validity-seconds="604800"/>--><!--<security:logout/>:注销功能logout-url="/logout":退出过滤器默认的拦截路径,springSecurity内LogoutFilter要拦截的url(向这/logout发送请求来注销)logout-success-url:用户退出后要被重定向的urlinvalidate-session:默认为true,用户在退出后Http session失效success-handler-ref:指定一个bean(需要实现LogoutSuccessHandler接口),用来自定义退出成功后的操作delete-cookies="JSESSIONID":删除session对应的cookie--><security:logout logout-url="/logout" logout-success-url="/login" invalidate-session="true" delete-cookies="JSESSIONID"/></security:http><!--身份验证管理器--><security:authentication-manager><!--自定义授权提供类MyUserDetailsService,获得登录用户的用户详情信息。此类实现UserDetailsService接口。user-service-ref="myUserDetailsService" : 指定 UserDetailsService 接口的实现类最终都要返回一个UserDetail,用户详情--><security:authentication-provider user-service-ref="myUserDetailsService"><!-- 配置:加密算法对用户输入的密码进行加密,然后和数据库的密码进行配对 --><!--<security:password-encoder ref="bCryptPasswordEncoder"/>--></security:authentication-provider></security:authentication-manager><!--创建 springSecurity 密码加密工具类,使用PasswordEncoder 接口的实现,也可以使用别的--><!--<bean id="bCryptPasswordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"></bean>--><!--springSecurity实现 remember me 功能:如果用户登录选择 remember me ,springSecurity会将其cookie值存入数据库,来实现remember me 功能JdbcTokenRepositoryImpl 用来存取cookie值--><!--<bean id="jdbcTokenRepository" class="org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl"><property name="dataSource" ref="dataSource"/> &lt;!&ndash;数据库数据源&ndash;&gt;&lt;!&ndash;<property name="createTableOnStartup" value="true"/>&ndash;&gt; &lt;!&ndash;createTableOnStartup属性是当项目启动时,springSecurity创建表存储remember me相关信息,第二次启动时要注释这个属性&ndash;&gt;</bean>--></beans>

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

退出页面代码:

<div>
<%--    <form action="<c:url value='/logout'/>" method="post"><input id="logout" type="submit" value="退出系统"></form>--%><a href="<c:url value='/logout'/>">退出系统</a>
</div>

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


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

这篇关于Spring Security 4.X(XML文件配置session超时,单点登录-session并发控制,退出/logout)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/685825

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

Security OAuth2 单点登录流程

单点登录(英语:Single sign-on,缩写为 SSO),又译为单一签入,一种对于许多相互关连,但是又是各自独立的软件系统,提供访问控制的属性。当拥有这项属性时,当用户登录时,就可以获取所有系统的访问权限,不用对每个单一系统都逐一登录。这项功能通常是以轻型目录访问协议(LDAP)来实现,在服务器上会将用户信息存储到LDAP数据库中。相同的,单一注销(single sign-off)就是指

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

Zookeeper安装和配置说明

一、Zookeeper的搭建方式 Zookeeper安装方式有三种,单机模式和集群模式以及伪集群模式。 ■ 单机模式:Zookeeper只运行在一台服务器上,适合测试环境; ■ 伪集群模式:就是在一台物理机上运行多个Zookeeper 实例; ■ 集群模式:Zookeeper运行于一个集群上,适合生产环境,这个计算机集群被称为一个“集合体”(ensemble) Zookeeper通过复制来实现

CentOS7安装配置mysql5.7 tar免安装版

一、CentOS7.4系统自带mariadb # 查看系统自带的Mariadb[root@localhost~]# rpm -qa|grep mariadbmariadb-libs-5.5.44-2.el7.centos.x86_64# 卸载系统自带的Mariadb[root@localhost ~]# rpm -e --nodeps mariadb-libs-5.5.44-2.el7