RedisSession (自定义)

疯狂创客圈 Java 高并发【 亿级流量聊天室实战】实战系列 【博客园总入口

架构师成长+面试必备之 高并发基础书籍 【Netty Zookeeper Redis 高并发实战


前言

Crazy-SpringCloud 微服务脚手架 &视频介绍

Crazy-SpringCloud 微服务脚手架,是为 Java 微服务开发 入门者 准备的 学习和开发脚手架。并配有一系列的使用教程和视频,大致如下:

高并发 环境搭建 图文教程和演示视频,陆续上线:

中间件 链接地址
Linux Redis 安装(带视频) Linux Redis 安装(带视频)
Linux Zookeeper 安装(带视频) Linux Zookeeper 安装, 带视频
Windows Redis 安装(带视频) Windows Redis 安装(带视频)
RabbitMQ 离线安装(带视频) RabbitMQ 离线安装(带视频)
ElasticSearch 安装, 带视频 ElasticSearch 安装, 带视频
Nacos 安装(带视频) Nacos 安装(带视频)

Crazy-SpringCloud 微服务脚手架 图文教程和演示视频,陆续上线:

组件 链接地址
Eureka Eureka 入门,带视频
SpringCloud Config springcloud Config 入门,带视频
spring security spring security 原理+实战
Spring Session SpringSession 独立使用
分布式 session 基础 RedisSession (自定义)
重点: springcloud 开发脚手架 springcloud 开发脚手架
SpingSecurity + SpringSession 死磕 (写作中) SpingSecurity + SpringSession 死磕

小视频以及所需工具的百度网盘链接,请参见 疯狂创客圈 高并发社群 博客

RedisSession 场景和问题

一般,大家获取 Session 的方式: session = request.getSession(), 是通过HttpServletRequest 获取的,因为每次用户请求过来,我们服务端都会获取到请求携带的唯一 SessionId。

如果自定的 HttpSession的,所以我们还要自定义一个 HttpServletRequest 的包装类,使得每次请求获取的都是我们自己的HttpSession。

还有一点 ,如何 使用HttpServletRequest 包装类呢?

还需要自定义一个 Filter,这个Filter不干其它的事情,就负责把HttpServletRquest 换成我们自定义的包装类。

第一步 ,定义一个 RedisHttpSession

RedisHttpSession 实现 HttpSession 接口 ,选择Redis存储属性,达到分布式的目标。

session在 Redis中 选择的 Hash 结构存储,以 sessionId 作为Key,有些方法不需要实现。

//首先我说过,HttpSession是不能注入属性的,所以就需要依赖 上面定义的那个 工具类,获取bean
//如果不加此注解,你的属性就会为空,获取不到
@DependsOn("applicationContextUtil")
@SpringBootConfiguration
public class CustomRedisHttpSession implements HttpSession { private HttpServletRequest httpServletRequest;
private HttpServletResponse httpServletResponse;
private Cookie[] cookies;
//sessionId
private String sessionId;
public CustomRedisHttpSession(){} public CustomRedisHttpSession(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,String sid){ this.httpServletRequest = httpServletRequest;
this.httpServletResponse = httpServletResponse;
this.cookies = cookies;
this.sessionId =sid;
} /**
* 获取指定属性值
* @param key 属性key
* @return key对应的value
*/
@Override
public Object getAttribute(String key) {
if(sessionId != null){
return sessionRedisTemplate.opsForHash().get(sessionId,key);
}
return null;
} /**
* 之前说过了,此类属性不能注入,只能通过手动获取
*/
@SuppressWarnings("unchecked")
private final RedisTemplate<String,Object> sessionRedisTemplate =
=ApplicationContextUtil.getBean("sessionRedisTemplate",RedisTemplate.class); //sessionId 的前缀
private static final String SESSIONID_PRIFIX="yangxiaoguang"; /**
* 设置属性值
* @param key key
* @param value value
*/
@Override
public void setAttribute(String key, Object value) {
if (sessionId != null) {
sessionRedisTemplate.opsForHash().put(sessionId, key, value);
}else{
//如果是第一次登录,那么生成 sessionId,将属性值存入redis,设置过期时间,并设置浏览器cookie
this.sessionId = SESSIONID_PRIFIX + UUID.randomUUID();
setCookieSessionId(sessionId);
sessionRedisTemplate.opsForHash().put(sessionId, key, value);
sessionRedisTemplate.expire(sessionId, sessionTimeout, TimeUnit.SECONDS);
}
}
//session的过期时间,8小时
private final int sessionTimeout=28800; //将sessionId存入浏览器
private void setCookieSessionId(String sessionId){
Cookie cookie = new Cookie(SESSIONID,sessionId);
cookie.setPath("/");
cookie.setMaxAge(sessionTimeout);
this.httpServletResponse.addCookie(cookie);
} /**
* 移除指定的属性
* @param key 属性 key
*/
@Override
public void removeAttribute(String key) {
if(sessionId != null){
sessionRedisTemplate.opsForHash().delete(sessionId,key);
}
} }

第2步 ,定义一个 ServletRequestWrapper

如果自定的 HttpSession的,所以我们还要自定义一个 HttpServletRequest 的包装类,使得每次请求获取的都是我们自己的HttpSession。


public class CustomSessionHttpServletRequestWrapper extends HttpServletRequestWrapper{
private HttpServletRequest httpServletRequest;
private HttpServletResponse httpServletResponse;
//自定义Session
private CustomRedisHttpSession customRedisHttpSession; public CustomSessionHttpServletRequestWrapper(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse){
super(httpServletRequest);
this.httpServletRequest = httpServletRequest;
this.httpServletResponse = httpServletResponse;
Cookie[] cookies = httpServletRequest.getCookies();
String sid= getCookieSessionId(cookies);
this.customRedisHttpSession = new
CustomRedisHttpSession(httpServletRequest,httpServletResponse,sid);
} //这个方法就是最重要的,通过它获取自定义的 HttpSession
@Override
public HttpSession getSession() {
return this.customRedisHttpSession;
} //浏览器的cookie key
private static final String SESSIONID="xyzlycimanage"; //从浏览器获取SessionId
private String getCookieSessionId(Cookie[] cookies){
if(cookies != null){
for(Cookie cookie : cookies){
if(SESSIONID.equals(cookie.getName())){
return cookie.getValue();
}
}
}
return null;
}
}

如果从header 中获取 sessiondi,也是类似的:

public class CustomSessionHttpServletRequestWrapper extends HttpServletRequestWrapper{
private HttpServletRequest httpServletRequest;
private HttpServletResponse httpServletResponse;
//自定义Session
private CustomRedisHttpSession customRedisHttpSession; public CustomSessionHttpServletRequestWrapper(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse){
super(httpServletRequest);
this.httpServletRequest = httpServletRequest;
this.httpServletResponse = httpServletResponse;
String sid= request.getHeader("SESSION_ID");
this.customRedisHttpSession = new
CustomRedisHttpSession(httpServletRequest,httpServletResponse,sid);
} //这个方法就是最重要的,通过它获取自定义的 HttpSession
@Override
public HttpSession getSession() {
return this.customRedisHttpSession;
} //浏览器的cookie key
private static final String SESSIONID="xyzlycimanage"; //从浏览器获取SessionId
private String getCookieSessionId(Cookie[] cookies){
if(cookies != null){
for(Cookie cookie : cookies){
if(SESSIONID.equals(cookie.getName())){
return cookie.getValue();
}
}
}
return null;
}
}

第三步: 定义一个 Filter 类

还有一点 ,如何 使用HttpServletRequest 包装类呢?

还需要自定义一个 Filter,这个Filter不干其它的事情,就负责把HttpServletRquest 换成我们自定义的包装类。

	/**
* 此过滤器拦截所有请求,也放行所有请求,但是只要与Session操作的有关的请求都换被
* 替换成:CustomSessionHttpServletRequestWrapper包装请求,
* 这个请求会获取自定义的HttpSession
*/
public class CustomSessionRequestFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest,
ServletResponse servletResponse,
FilterChain filterChain)
throws IOException, ServletException { //将请求替换成自定义的 CustomSessionHttpServletRequestWrapper 包装请求
HttpServletRequest customSessionHttpServletRequestWrapper =
new CustomSessionHttpServletRequestWrapper
((HttpServletRequest)servletRequest,(HttpServletResponse)servletResponse); filterChain.doFilter(customSessionHttpServletRequestWrapper,
httpServletResponse); //替换了 请求哦
filterChain.doFilter(customSessionHttpServletRequestWrapper,servletResponse);
} /**
* init 和 destroy 是管理 Filter的生命周期的,与逻辑无关,所以无需实现
*/
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void destroy() {}
}

四步 装载 Filter 类

可以独立加载,也可以放在springsecurity 的配置类中。

如果放在springsecurity 的配置类,具体如下:

package com.gsafety.pushserver.message.config;

//...

@EnableWebSecurity()
public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers(
//...
"/actuator/hystrix",
"/actuator/hystrix.stream",
"/v2/api-docs",
"/swagger-resources/configuration/ui",
"/swagger-resources",
"/swagger-resources/configuration/security",
"/swagger-ui.html")
.permitAll()
// .antMatchers("/image/**").permitAll()
// .antMatchers("/admin/**").hasAnyRole("ADMIN")
.and()
.authorizeRequests().anyRequest().authenticated() .and() .formLogin().disable()
.sessionManagement().disable()
.and()
.logout().disable()
.addFilterBefore(new CustomSessionRequestFilter(), SessionManagementFilter.class)
.sessionManagement().disable(); } @Override
public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers(
//....
"/actuator/hystrix.stream",
"/actuator/hystrix",
"/api/mock/**",
"/v2/api-docs",
"/swagger-resources/configuration/ui",
"/swagger-resources",
"/swagger-resources/configuration/security",
"/api/gemp/duty/info/user/login",
"/swagger-ui.html",
"/css/**",
"/js/**",
"/images/**",
"/webjars/**",
"**/favicon.ico"
);
} }

具体,请关注 Java 高并发研习社群博客园 总入口


最后,介绍一下疯狂创客圈:疯狂创客圈,一个Java 高并发研习社群博客园 总入口

疯狂创客圈,倾力推出:面试必备 + 面试必备 + 面试必备 的基础原理+实战 书籍 《Netty Zookeeper Redis 高并发实战


疯狂创客圈 Java 死磕系列

  • Java (Netty) 聊天程序【 亿级流量】实战 开源项目实战

RedisSession (自定义)的更多相关文章

  1. tornado--之cookie自定义(还有session)

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAhAAAAHzCAIAAAD+WrNvAAAgAElEQVR4nOy993cTV7/vf/6qu865ob

  2. 自定义Web组件

    一.Session 1.面向对象基础 面向对象中通过索引的方式访问对象,需要内部实现 __getitem__ .__delitem__.__setitem__方法 +? 1 2 3 4 5 6 7 8 ...

  3. Tornado 自定义Form,session实现方法

    一. 自定义Tornado 验证模块 我们知道,平时在登陆某个网站或软件时,网站对于你输入的内容是有要求的,并且会对你输入的错误内容有提示,对于Django这种大而全的web框架,是提供了form表单 ...

  4. Python web框架 Tornado(三)自定义session组件

    我们在学习Django框架的过程中,内部封装了session组件,以方便于我们使用进行验证.但是Tornado框架是没有session的,所以如果想使用session的话,就需要我们自己定制相对应的组 ...

  5. Tornado之自定义session

      面向对象基础 面向对象中通过索引的方式访问对象,需要内部实现 __getitem__ .__delitem__.__setitem__方法 #!/usr/bin/env python # -*- ...

  6. spring-redis-session 自定义 key 和过期时间

    对于分布式应用来说,最开始遇到的问题就是 session 的存储了,解决方案大致有如下几种 使用 spring-session 它可以把 session 存储到你想存储的位置,如 redis,mysq ...

  7. 关于Unity3D自定义编辑器的学习

    被人物编辑器折腾了一个月,最终还是交了点成品上去(还要很多优化都还么做).  刚接手这项工作时觉得没概念,没想法,不知道.后来就去看<<Unity5.X从入门到精通>>中有关于 ...

  8. 一起学微软Power BI系列-使用技巧(5)自定义PowerBI时间日期表

    1.日期函数表作用 经常使用Excel或者PowerBI,Power Pivot做报表,时间日期是一个重要的纬度,加上做一些钻取,时间日期函数表不可避免.所以今天就给大家分享一个自定义的做日期表的方法 ...

  9. JavaScript自定义浏览器滚动条兼容IE、 火狐和chrome

    今天为大家分享一下我自己制作的浏览器滚动条,我们知道用css来自定义滚动条也是挺好的方式,css虽然能够改变chrome浏览器的滚动条样式可以自定义,css也能够改变IE浏览器滚动条的颜色.但是css ...

随机推荐

  1. git本地项目连接私人远程仓库以及遇到的问题

    一.引言 1.最开始的时候,我本地项目连接的是github远程仓库,现在要转到公司的私人远程仓库. 2.我和大家说两个事: (1)本地项目连接github远程仓库, (2)本地项目连接私人远程仓库, ...

  2. 深入了解CSS中盒子模型

    CSS中盒子模型介绍 什么是盒子? 盒子是用来存储物品,我们可以将盒子理解为酒盒,酒盒有什么组成的呢? 有酒可以喝.有填充物保护酒防止酒被摔坏.纸盒子. 我们怎么理解CSS中的盒子呢,CSS中盒子有什 ...

  3. 使用 Flask 和 Vue.js 来构建全栈单页应用

    在这个教程中,我将向你展示如何将 Vue 的单页面应用和 Flask 后端连接起来. 简单的来说,如果想在 Flask 中使用 Vue 框架是没有什么问题的. 但在实际中存在一个明显的问题就是 Fla ...

  4. JDK官方下载

    平时进行java开发时避免不了使用jdk,而现在jdk版本已经到1.9了,但是之前版本下载在官方网站就不好找了(主要还是因为网站是英文的): 进入官网下载jdk的前提是进入官网,直接百度搜jdk下载也 ...

  5. ThreadLocal快速了解一下

    欢迎点赞阅读,一同学习交流,有疑问请留言 . GitHub上也有开源 JavaHouse 欢迎star 1 引入 在Java8里面,ThreadLocal 是一个泛型类.这个类可以提供线程变量.每个线 ...

  6. react之高阶组件(一)

    当两个或多个组件有相同的地方,可以将相同的部分抽离出来 先创建三个组件A.B.C A.js import React, { Component } from 'react' class A exten ...

  7. RabbitMQ的三大交换器

    pom文件都是相同的 <?xml version="1.0" encoding="UTF-8"?> <project xmlns=" ...

  8. Selenium 4 Python的最佳测试框架

    随着Python语言的使用越来越流行,基于Python的测试自动化框架也越来越流行.在项目选择最佳框架时,开发人员和测试人员会有些无法下手.做出选择是应该判断很多事情,框架的脚本质量,测试用例的简单性 ...

  9. 洛谷 题解 P2117 【小Z的矩阵】

    这题这么无聊,亏我还用了读入输出优化... 关键在于,这还是道黄题QWQ 掀桌而起 (╯‵□′)╯︵┻━┻ 显而易见,在i != j的情况下,a[i][j] + a[j][i]和a[j][i] + a ...

  10. 小程序 - 简单实现mixin功能

    前言 在业务中有没有一个场景:多个页面需要用到一样的 data 和 method,或者生命周期都需要执行同样的操作.我们在每个页面都写上重复的代码,一但功能修改就要更新多个页面,在后期维护起来会很麻烦 ...