RedisSession (自定义)
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) 聊天程序【 亿级流量】实战 开源项目实战
- Netty 源码、原理、JAVA NIO 原理
- Java 面试题 一网打尽
- 疯狂创客圈 【 博客园 总入口 】
RedisSession (自定义)的更多相关文章
- tornado--之cookie自定义(还有session)
aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAhAAAAHzCAIAAAD+WrNvAAAgAElEQVR4nOy993cTV7/vf/6qu865ob
- 自定义Web组件
一.Session 1.面向对象基础 面向对象中通过索引的方式访问对象,需要内部实现 __getitem__ .__delitem__.__setitem__方法 +? 1 2 3 4 5 6 7 8 ...
- Tornado 自定义Form,session实现方法
一. 自定义Tornado 验证模块 我们知道,平时在登陆某个网站或软件时,网站对于你输入的内容是有要求的,并且会对你输入的错误内容有提示,对于Django这种大而全的web框架,是提供了form表单 ...
- Python web框架 Tornado(三)自定义session组件
我们在学习Django框架的过程中,内部封装了session组件,以方便于我们使用进行验证.但是Tornado框架是没有session的,所以如果想使用session的话,就需要我们自己定制相对应的组 ...
- Tornado之自定义session
面向对象基础 面向对象中通过索引的方式访问对象,需要内部实现 __getitem__ .__delitem__.__setitem__方法 #!/usr/bin/env python # -*- ...
- spring-redis-session 自定义 key 和过期时间
对于分布式应用来说,最开始遇到的问题就是 session 的存储了,解决方案大致有如下几种 使用 spring-session 它可以把 session 存储到你想存储的位置,如 redis,mysq ...
- 关于Unity3D自定义编辑器的学习
被人物编辑器折腾了一个月,最终还是交了点成品上去(还要很多优化都还么做). 刚接手这项工作时觉得没概念,没想法,不知道.后来就去看<<Unity5.X从入门到精通>>中有关于 ...
- 一起学微软Power BI系列-使用技巧(5)自定义PowerBI时间日期表
1.日期函数表作用 经常使用Excel或者PowerBI,Power Pivot做报表,时间日期是一个重要的纬度,加上做一些钻取,时间日期函数表不可避免.所以今天就给大家分享一个自定义的做日期表的方法 ...
- JavaScript自定义浏览器滚动条兼容IE、 火狐和chrome
今天为大家分享一下我自己制作的浏览器滚动条,我们知道用css来自定义滚动条也是挺好的方式,css虽然能够改变chrome浏览器的滚动条样式可以自定义,css也能够改变IE浏览器滚动条的颜色.但是css ...
随机推荐
- git本地项目连接私人远程仓库以及遇到的问题
一.引言 1.最开始的时候,我本地项目连接的是github远程仓库,现在要转到公司的私人远程仓库. 2.我和大家说两个事: (1)本地项目连接github远程仓库, (2)本地项目连接私人远程仓库, ...
- 深入了解CSS中盒子模型
CSS中盒子模型介绍 什么是盒子? 盒子是用来存储物品,我们可以将盒子理解为酒盒,酒盒有什么组成的呢? 有酒可以喝.有填充物保护酒防止酒被摔坏.纸盒子. 我们怎么理解CSS中的盒子呢,CSS中盒子有什 ...
- 使用 Flask 和 Vue.js 来构建全栈单页应用
在这个教程中,我将向你展示如何将 Vue 的单页面应用和 Flask 后端连接起来. 简单的来说,如果想在 Flask 中使用 Vue 框架是没有什么问题的. 但在实际中存在一个明显的问题就是 Fla ...
- JDK官方下载
平时进行java开发时避免不了使用jdk,而现在jdk版本已经到1.9了,但是之前版本下载在官方网站就不好找了(主要还是因为网站是英文的): 进入官网下载jdk的前提是进入官网,直接百度搜jdk下载也 ...
- ThreadLocal快速了解一下
欢迎点赞阅读,一同学习交流,有疑问请留言 . GitHub上也有开源 JavaHouse 欢迎star 1 引入 在Java8里面,ThreadLocal 是一个泛型类.这个类可以提供线程变量.每个线 ...
- react之高阶组件(一)
当两个或多个组件有相同的地方,可以将相同的部分抽离出来 先创建三个组件A.B.C A.js import React, { Component } from 'react' class A exten ...
- RabbitMQ的三大交换器
pom文件都是相同的 <?xml version="1.0" encoding="UTF-8"?> <project xmlns=" ...
- Selenium 4 Python的最佳测试框架
随着Python语言的使用越来越流行,基于Python的测试自动化框架也越来越流行.在项目选择最佳框架时,开发人员和测试人员会有些无法下手.做出选择是应该判断很多事情,框架的脚本质量,测试用例的简单性 ...
- 洛谷 题解 P2117 【小Z的矩阵】
这题这么无聊,亏我还用了读入输出优化... 关键在于,这还是道黄题QWQ 掀桌而起 (╯‵□′)╯︵┻━┻ 显而易见,在i != j的情况下,a[i][j] + a[j][i]和a[j][i] + a ...
- 小程序 - 简单实现mixin功能
前言 在业务中有没有一个场景:多个页面需要用到一样的 data 和 method,或者生命周期都需要执行同样的操作.我们在每个页面都写上重复的代码,一但功能修改就要更新多个页面,在后期维护起来会很麻烦 ...