session管理

session超时处理

demo的application中加入超时配置

# 默认30分钟 server.servlet.session.timeout=30m
server.servlet.session.timeout= 10s

但是当我们登录上去发下session的超时时间远超过了10s

原因
看 TomcatEmbeddedServletContainerFactory 类
但找不到这个类

Spring Boot 2中缺少TomcatEmbeddedServletContainerFactory(TomcatEmbeddedServletContainerFactory is missing in Spring Boot 2)

为了支持反应用例,嵌入式容器
包结构已经被非常广泛地重构。 
EmbeddedServletContainer已重命名为WebServer,
org.springframework.boot.context.embedded包已重新定位
到org.springframework.boot.web.server。相应地,
EmbeddedServletContainerCustomizer已重命名为
WebServerFactoryCustomizer。



例如,如果您使用TomcatEmbeddedServletContainerFactory自定义嵌入式Tomcat容器
回调接口,
你现在应该使用TomcatServletWebServerFactory,如果你使用
一个EmbeddedServletContainerCustomizer bean,你现在应该使用
WebServerFactoryCustomizer bean。

看TomcatServletWebServerFactory类

private void configureSession(Context context) {
        long sessionTimeout = this.getSessionTimeoutInMinutes();//以分钟为单位读取超时时间
        context.setSessionTimeout((int)sessionTimeout);//超时时间取整,则说明超时时间最低为1分钟
        Boolean httpOnly = this.getSession().getCookie().getHttpOnly();
        if (httpOnly != null) {
            context.setUseHttpOnly(httpOnly);
        }

        if (this.getSession().isPersistent()) {
            Manager manager = context.getManager();
            if (manager == null) {
                manager = new StandardManager();
                context.setManager((Manager)manager);
            }

            this.configurePersistSession((Manager)manager);
        } else {
            context.addLifecycleListener(new TomcatServletWebServerFactory.DisablePersistSessionListener());
        }

    }

超时页面提醒

BrowserSecurityConfig
在这里插入图片描述
注意 路径前必须有杠

BrowserSecurityController

    @RequestMapping("/session/invalid")
    @ResponseStatus(code = HttpStatus.UNAUTHORIZED)//401未授权状态码
    public SimpleResponse sessionInvalid(HttpServletRequest request, HttpServletResponse response){
        String message = "session失效";
        return  new SimpleResponse(message);
    }

测试
在这里插入图片描述
当跳转到这个页面时,可以由前台决定是给一个美化页面还是重定向到登录

session并发控制

一个用户只能有一个session
当后面再登录时,后面的session替换掉前面的session

BrowserSecurityConfig
在这里插入图片描述

并发策略

public class MyExpiredSessionStrategy implements SessionInformationExpiredStrategy {


	public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException, ServletException {
		event.getResponse().setContentType("application/json;charset=UTF-8");
		event.getResponse().getWriter().write("并发登录!");
	}
}

分别在两个浏览器登录,
然后再回到第一个登录的浏览器访问

在这里插入图片描述

第二种情况 /当session数量达到最大时 阻止后面的登录
在这里插入图片描述
此时 在另一个浏览器重新登录,如下
在这里插入图片描述

重构

见代码

集群session管理

当一款应用让用户使用时都会部署一个集群
这个集群至少部署两台机器
前面再做一个负载均衡,当一台机器挂掉后,应用则还可以正常使用,为用户服务

这种情况下 我们的session如何处理
即,服务器session独立管理
而不是每个机器管理自己的session
在这里插入图片描述
spring专门提供了一个依赖来处理这种情况
在browser模块中我们引入了一个依赖

        <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session</artifactId>
            <version>1.3.5.RELEASE</version>
            <!--这个要写版本号,也不知道为什么Spring IO Platform么有对他进行版本控制-->
        </dependency>

我们只需要告诉它session的存储是什么,即存储的地址和端口

它支持的存储如下

/**
 * Supported Spring Session data store types.
 *
 * @author Tommy Ludwig
 * @author Eddú Meléndez
 * @author Vedran Pavic
 * @since 1.4.0
 */
public enum StoreType {

	/**
	 * Redis backed sessions.
	 */
	REDIS,

	/**
	 * MongoDB backed sessions.
	 */
	MONGODB,

	/**
	 * JDBC backed sessions.
	 */
	JDBC,

	/**
	 * Hazelcast backed sessions.
	 */
	HAZELCAST,

	/**
	 * No session data-store.
	 */
	NONE

}

一般使用redis来做这个存储

1 是session需频繁使用,redis性能高
2 是redis本事就有过期时间与session的过期时间可以对应

下载redis
https://redis.io/download
稳定版
在这里插入图片描述
下载以后解压安装并启动

[root@rain redis-5.0.5]# mv //tmp/VMwareDnD/vzwLhB/redis-5.0.5.tar.gz ./
[root@rain redis-5.0.5]# tar -xvf redis-5.0.5.tar.gz
[root@rain redis-5.0.5]# cd redis-5.0.5/
[root@rain redis-5.0.5]# make
·············
Hint: It’s a good idea to run ‘make test’ ?
make[1]: 离开目录“/opt/work/tools/redis-5.0.5/src”
[root@rain redis-5.0.5]# ./src/redis-server

在这里插入图片描述
他会在6379这个端口提供redis服务

demo的application配置session存储类型和redis连接

spring.session.store-type=redis
#spring.session.store-type=none

## Redis服务器地址
spring.redis.host=192.168.85.134
## Redis服务器连接端口
spring.redis.port=6379
## Redis服务器连接密码(默认为空)
spring.redis.password=123456

redis我是在虚拟机里配的,宿主连接

启动
报错

 ERROR 20224 --- [ost-startStop-1] o.s.b.web.embedded.tomcat.TomcatStarter  : Error starting Tomcat context. Exception: org.springframework.beans.factory.UnsatisfiedDependencyException. Message: Error creating bean with name 'sessionRepositoryFilterRegistration' defined in class path resource [org/springframework/boot/autoconfigure/session/SessionRepositoryFilterConfiguration.class]: Unsatisfied dependency expressed through method 'sessionRepositoryFilterRegistration' parameter 1; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.boot.autoconfigure.session.RedisSessionConfiguration$SpringBootRedisHttpSessionConfiguration': Injection of autowired dependencies failed; nested exception is java.lang.NoSuchMethodError: org.springframework.boot.autoconfigure.session.RedisSessionConfiguration$SpringBootRedisHttpSessionConfiguration.setCleanupCron(Ljava/lang/String;)V

原因
依赖错了
我用的是spring-session
实际应该用spring-session-data-redis
依赖替换后启动成功

访问,但是图片验证码没出来
在这里插入图片描述
报错

org.springframework.data.redis.serializer.SerializationException: Cannot serialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to serialize object using DefaultSerializer; nested exception is java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [com.whale.security.core.validate.image.ImageCode]
	at org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.serialize(JdkSerializationRedisSerializer.java:96) ~[spring-data-redis-2.0.12.RELEASE.jar:2.0.12.RELEASE]

意思是ImageCode序列化出现了问题

因为我们的session是用redis管理了,而我们是把ImageCode放在了session里面
放在redis里的东西都需要可序列化
一个类实现序列化的时候
类中的属性都需要实现序列化
但是在ImageCode中有一个BufferedImage属性,而BufferedImage是jdk提供的,它没有实现序列化接口

所有我们session中不放图片,而是直接放验证码
AbstractValidateCodeProcessor


	/**
	 * 保存校验码
	 *
	 * @param request
	 * @param validateCode
	 */
	private void save(ServletWebRequest request, C validateCode) {
//		sessionStrategy.setAttribute(request, getSessionKey(request).toUpperCase(), validateCode);


		ValidateCode code = new ValidateCode(validateCode.getCode(),validateCode.getExpireTime());
//        sessionStrategy.setAttribute(request, getSessionKey(request), validateCode);
        sessionStrategy.setAttribute(request, getSessionKey(request), code);

    }

重新启动
验证码可以正常显示

再再8081端口启动一个实例

测试

同一个浏览器中
8080端口登录后
8081端口共享这个session

退出处理

退出需要访问一个特定的路径
##

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	index
<a href="/logout"> 退出</a>
</body>
</html>

在这里插入图片描述
退出步骤

使当前session失效
清除与当前用户相关的remember-me记录
清空当前的securityContext
重定向到登录页

更改默认退出配置
自定义退出路径
index

<a href="/logout"> 退出</a>
<a href="/signOut"> 我的退出</a>

BrowserSecurityConfig
在这里插入图片描述
测试 ok

退出成功处理
在这里插入图片描述

<body>
	<h2>退出成功</h2>

</body>

BrowserSecurityConfig
在这里插入图片描述

test
在这里插入图片描述

自定义退出成功处理器
LogoutSuccessHandler

public class WhaleLogoutSuccessHandler implements LogoutSuccessHandler {


//    private SecurityProperties securityProperties;

    private String signOutUrl;

    private ObjectMapper objectMapper = new ObjectMapper();

    public WhaleLogoutSuccessHandler(String signOutUrl){
        this.signOutUrl = signOutUrl;

    }

    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        System.out.println("退出成功");

//        String signOutUrl = securityProperties.getBrowser().getSignOutUrl();
        //如果没有配置退出页面
        if(StringUtils.isBlank(signOutUrl)){

            response.setContentType("application/json;charset=utf-8");
            response.getWriter().write(objectMapper.writeValueAsString("LogoutSuccessHandler退出成功"));

        }else {
            response.sendRedirect(signOutUrl);
        }

    }

BrowserSecurityBeanConfig

	@Bean
	@ConditionalOnMissingBean(LogoutSuccessHandler.class)
	public LogoutSuccessHandler logoutSuccessHandler(){
		return new WhaleLogoutSuccessHandler(securityProperties.getBrowser().getSignOutUrl());
	}

BrowserSecurityConfig
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值