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已经失效。


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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/671789.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DAY5 作业

1.TCP三次握手 2.TCP四次挥手 3.TCP和UDP的区别 相同点&#xff1a;都属于传输层的协议 不同点&#xff1a; TCP&#xff1a;1&#xff09;提供面向连接的&#xff0c;可靠的数据传输服务&#xff1b; 2&#xff09;传输过程中&#xff0c;数据无误&#xff0c;数据无丢失、数…

【C++第二阶段】空指针访问成员函数常成员函数常成员属性

你好你好&#xff01; 以下内容仅为当前认识&#xff0c;可能有不足之处&#xff0c;欢迎讨论&#xff01; 文章目录 空指针访问成员函数常成员函数&常成员属性 空指针访问成员函数 类对象类型的空指针可以访问成员函数&#xff0c;但是不能够访问带有成员属性的成员函数。…

大数据调用链监控平台技术原理

一、AOP技术总结 二、监控逻辑动态织入原理 三、JVM动态织入流程 四、调用链监控平台技术简要总结 &#xff08;一&#xff09;、单服务的无感知自动日志埋点 jvm层面的字节码织入埋点&#xff0c;javaagent方式启动。 &#xff08;二&#xff09;、多服务调用链串联 trace…

transformer剪枝论文汇总

文章目录 NN Pruning摘要实验 大模型剪枝LLM-PrunerSparseGPT LTPVTPWidth & Depth PruningPatch SlimmingDynamicViTSPViTDynamicBERTViT SlimmingFastFormersNViTUVCPost-training pruning NN Pruning 《Block Pruning For Faster Transformers》 《为更快的transformer…

关于TDSQL(MySQL)的简单知识分享

0. 前言 最近在系统改造过程中&#xff0c;接触到了国产分布式数据库TDSQL&#xff0c;记录一下关于TDSQL的部分知识点。 1. TDSQL简介 TDSQL是腾讯推出的一款兼容MySQL的自主可控、高一致性分布式数据库产品。 1.1 TDSQL优点&#xff1a; 数据强一致性高性能低成本线性水…

OPC UA客户端工具Softing OPC Client使用_推荐使用

OPC UA客户端工具Softing OPC Client使用_推荐使用 Softing OPC Client工具介绍 Softing OPC Client工具是德国Softing公司出品的标准OPC客户端。 是我数年来用过的最完备的OPC UA客户端工具。全部的数据类型都支持&#xff0c;功能齐备。 是查看或测试OPC服务器的最好工具了…

使用esp32 cam + SR602人体感应模块制作一个小型的监控

需求&#xff1a; 做一个小型的监控&#xff0c;类似电子猫眼&#xff0c;监测到人之后&#xff0c;取一张图 然后发送到自己的邮箱。 架构&#xff1a; 1.sr602 传感器监测到人 2. esp32 cam 取图 并通过mqtt协议传到远端服务器 3, 服务器利用python 搭建一个mqtt客户端&…

【Jenkins】Jenkins关闭Jenkins关闭、重启

目录 一、Jenkins关闭、重启 二、Jenkins服务的启动、停止方法。 一、Jenkins关闭、重启 1.关闭Jenkins 只需要在访问jenkins服务器的网址url地址后加上exit&#xff0c;关闭Jenkins服务。 例如&#xff1a;http://localhost:8081/exit 2.重启Jenkies 只有在Jenkins服务启动…

webrtc native api的几个要点

文章目录 基本流程状态回调类sdp的中媒体行pc对象 基本流程 webrtc native的接口&#xff0c;主要就是围绕着PeerConnection对象&#xff0c;一个PeerConnection对象它代表了一次音视频会话。 那么通过PeerConnection对象建立音视频通话&#xff0c;包括如下步骤&#xff1a; …

第三篇:跨平台QT开发-正则表达式和文件处理

正则表达式 正则表达式即一个文本匹配字符串的一种模式&#xff0c;Qt 中 QRegExp 类实现使用正则表达式 进行模式匹配&#xff0c;且完全支持 Unicode&#xff0c;主要应用&#xff1a;字符串验证、搜索、查找替换、分割。 正则表达式中字符及字符集 元素含义 c 匹配字符本…

【WebSocket】微信小程序原生组件使用SocketTask 调用星火认知大模型

直接上代码 微信开发者工具-调试器-终端-新建终端 进行依赖安装 npm install base-64 npm install crypto-js 然后顶部工具栏依次点击 工具-构建npm // index.js const defaultAvatarUrl https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQ…

4、ChatGPT 无法完成的 5 项编码任务

ChatGPT 无法完成的 5 项编码任务 这是 ChatGPT 不能做的事情的一个清单,但这并非详尽无遗。ChatGPT 可以从头开始生成相当不错的代码,但是它不能取代你的工作。 我喜欢将 ChatGPT 视为 StackOverflow 的更智能版本。非常有帮助,但不会很快取代专业人士。当 ChatGPT 问世时…

鸿蒙开发系列教程(十四)--组件导航:Tabs 导航

Tabs 导航 Tabs组件的页面组成包含两个部分&#xff0c;分别是TabContent和TabBar。TabContent是内容页&#xff0c;TabBar是导航页签栏 每一个TabContent对应的内容需要有一个页签&#xff0c;可以通过TabContent的tabBar属性进行配置 设置多个内容时&#xff0c;需在Tabs…

Maven构建OSGI+HttpServer应用

Maven构建OSGIHttpServer应用 官网&#xff08;https://eclipse.dev/equinox/server/http_in_equinox.php&#xff09;介绍有两种方式&#xff1a; 一种是基于”org.eclipse.equinox.http”包的轻量级实现&#xff0c;另一种是基于”org.eclipse.equinox.http.jetty”包&#…

基于SpringBoot+Vue的实验室管理系统

末尾获取源码作者介绍&#xff1a;大家好&#xff0c;我是墨韵&#xff0c;本人4年开发经验&#xff0c;专注定制项目开发 更多项目&#xff1a;CSDN主页YAML墨韵 学如逆水行舟&#xff0c;不进则退。学习如赶路&#xff0c;不能慢一步。 目录 一、项目简介 二、开发技术与环…

​​​​​​​CleanMyMac X有什么优势?到底好不好用?

当你的Mac开始变得缓慢或者存储空间告急时&#xff0c;这通常是一个清理磁盘空间的信号。无论是工作文件、个人照片、还是各种应用程序&#xff0c;随着时间的推移&#xff0c;它们都可能在你的硬盘上积累了大量数据。有效地管理这些文件不仅可以提高你的工作效率&#xff0c;还…

12.0 Zookeeper 数据同步流程

在 Zookeeper 中&#xff0c;主要依赖 ZAB 协议来实现分布式数据一致性。 ZAB 协议分为两部分&#xff1a; 消息广播崩溃恢复 消息广播 Zookeeper 使用单一的主进程 Leader 来接收和处理客户端所有事务请求&#xff0c;并采用 ZAB 协议的原子广播协议&#xff0c;将事务请求…

Python进阶--下载想要的格言(基于格言网的Python爬虫程序)

注&#xff1a;由于上篇帖子&#xff08;Python进阶--爬取下载人生格言(基于格言网的Python3爬虫)-CSDN博客&#xff09;篇幅长度的限制&#xff0c;此篇帖子对上篇做一个拓展延伸。 目录 一、爬取格言网中想要的内容的url 1、找到想要的内容 2、抓包分析&#xff0c;找到想…

如何更改默认浏览器?

打开设置---应用---默认应用 点击你想要设置为默认浏览器的应用&#xff08;假设为Microsoft Edge&#xff09;&#xff0c;点击设置默认值就可以了。

艺术创作和生活的关系

艺术出现在生产劳作中并体现出人们生活、工作、学习中&#xff0c;使人们在不受限制随意发挥缔造发明能力的体现&#xff0c;独立的精神活动领域在它逐渐演变进步的历程中越来越明显&#xff0c;也是一个人精神思想生活中很重要的一部分。艺术随着社会发展而发展。一件完美的艺…