集群现在越来越常见,当我们项目搭建了集群,就会产生session共享问题。因为session是保存在服务器上面的。那么解决这一问题,大致有三个方案,1.通过nginx的负载均衡其中一种ip绑定来实现(通过ip绑定服务器其中一台,就没有集群概念了);2.通过cookie备份session实现(因为cookie数据保存在客户端,不推荐;3.通过redis备份session实现(推荐);
学习本章节之前,建议依次阅读以下文章,更好的串联全文内容,如已掌握以下列出知识点,请跳过:
vSpring Session概念
Spring Session 提供了一套创建和管理 Servlet HttpSession 的方案。Spring Session 提供了集群 Session(Clustered Sessions)功能,默认采用外置的 Redis 来存储 Session 数据(不用手动存储到redis中),以此来解决 Session 共享的问题。
Spring Session 为企业级 Java 应用的 session 管理带来了革新,使得以下的功能更加容易实现:
API 和用于管理用户会话的实现。
支持每个浏览器上使用多个 session,从而能够很容易地构建更加丰富的终端用户体验。
当用户使用 WebSocket 发送请求的时候,能够保持 HttpSession 处于活跃状态。
控制 session id 如何在客户端和服务器之间进行交换,这样的话就能很容易地编写 Restful API,因为它可以从 HTTP头信息中获取 session id,而不必再依赖于 cookie。
将 session 所保存的状态卸载到特定的外部 session 存储中,如 Redis 或 Apache Geode 中,它们能够以独立于应用服务器的方式提供高质量的集群。
HttpSession - 允许以应用程序容器(即 Tomcat)中性的方式替换 HttpSession。
关于更多Spring Session概念的介绍,可以看看官网Spring Session
vSpring Session整合
1.1 引入pmo.xml
<dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency>
1.2 创建useraccount表
CREATE TABLE `useraccount` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `username` varchar(255) NOT NULL, `age` int(10) NOT NULL, `phone` bigint NOT NULL, `email` varchar(255) NOT NULL, `account` varchar(100) NOT NULL UNIQUE, `pwd` varchar(255) NOT NULL, PRIMARY KEY (`id`) )ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; insert into `useraccount` values(1,‘赵(dev)‘,23,158,‘[email protected]‘,‘test001‘,‘test001‘); insert into `useraccount` values(2,‘钱(dev)‘,27,136,‘[email protected]‘,‘test002‘,‘test002‘); insert into `useraccount` values(3,‘孙(dev)‘,31,159,‘[email protected]‘,‘test003‘,‘test003‘); insert into `useraccount` values(4,‘李(dev)‘,35,130,‘[email protected]‘,‘test004‘,‘test004‘); select * from `useraccount`;
1.3 mapper
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.demo.dao.UserAccountMapper"> <resultMap id="BaseResultMap" type="com.demo.pojo.UserAccount"> <id column="id" jdbcType="INTEGER" property="id" /> <result column="username" jdbcType="VARCHAR" property="username" /> <result column="age" jdbcType="INTEGER" property="age" /> <result column="phone" jdbcType="BIGINT" property="phone" /> <result column="email" jdbcType="VARCHAR" property="email" /> <result column="account" jdbcType="VARCHAR" property="account" /> <result column="pwd" jdbcType="VARCHAR" property="pwd" /> </resultMap> <sql id="Base_Column_List"> id, username, age, phone, email, account, pwd </sql> <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap"> select <include refid="Base_Column_List" /> from useraccount where id = #{id,jdbcType=INTEGER} </select> <select id="getUserByAccount" parameterType="java.lang.String" resultMap="BaseResultMap"> select <include refid="Base_Column_List" /> from useraccount where account = #{account} </select> <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer"> delete from useraccount where id = #{id,jdbcType=INTEGER} </delete> <insert id="insert" parameterType="com.demo.pojo.UserAccount"> insert into useraccount (id, username, age, phone, email, account, pwd) values (#{id,jdbcType=INTEGER}, #{username,jdbcType=VARCHAR}, #{age,jdbcType=INTEGER}, #{phone,jdbcType=BIGINT}, #{email,jdbcType=VARCHAR}, #{account,jdbcType=VARCHAR}, #{pwd,jdbcType=VARCHAR}) </insert> <insert id="insertSelective" parameterType="com.demo.pojo.UserAccount"> insert into useraccount <trim prefix="(" suffix=")" suffixOverrides=","> <if test="id != null"> id, </if> <if test="username != null"> username, </if> <if test="age != null"> age, </if> <if test="phone != null"> phone, </if> <if test="email != null"> email, </if> <if test="account != null"> account, </if> <if test="pwd != null"> pwd, </if> </trim> <trim prefix="values (" suffix=")" suffixOverrides=","> <if test="id != null"> #{id,jdbcType=INTEGER}, </if> <if test="username != null"> #{username,jdbcType=VARCHAR}, </if> <if test="age != null"> #{age,jdbcType=INTEGER}, </if> <if test="phone != null"> #{phone,jdbcType=BIGINT}, </if> <if test="email != null"> #{email,jdbcType=VARCHAR}, </if> <if test="account != null"> #{account,jdbcType=VARCHAR}, </if> <if test="pwd != null"> #{pwd,jdbcType=VARCHAR}, </if> </trim> </insert> <update id="updateByPrimaryKeySelective" parameterType="com.demo.pojo.UserAccount"> update useraccount <set> <if test="username != null"> username = #{username,jdbcType=VARCHAR}, </if> <if test="age != null"> age = #{age,jdbcType=INTEGER}, </if> <if test="phone != null"> phone = #{phone,jdbcType=BIGINT}, </if> <if test="email != null"> email = #{email,jdbcType=VARCHAR}, </if> <if test="account != null"> account = #{account,jdbcType=VARCHAR}, </if> <if test="pwd != null"> pwd = #{pwd,jdbcType=VARCHAR}, </if> </set> where id = #{id,jdbcType=INTEGER} </update> <update id="updateByPrimaryKey" parameterType="com.demo.pojo.UserAccount"> update useraccount set username = #{username,jdbcType=VARCHAR}, age = #{age,jdbcType=INTEGER}, phone = #{phone,jdbcType=BIGINT}, email = #{email,jdbcType=VARCHAR}, account = #{account,jdbcType=VARCHAR}, pwd = #{pwd,jdbcType=VARCHAR} where id = #{id,jdbcType=INTEGER} </update> </mapper>
package com.demo.dao; import com.demo.pojo.UserAccount; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; @Mapper @Repository public interface UserAccountMapper { int deleteByPrimaryKey(Integer id); int insert(UserAccount record); int insertSelective(UserAccount record); UserAccount selectByPrimaryKey(Integer id); UserAccount getUserByAccount(@Param("account") String account); int updateByPrimaryKeySelective(UserAccount record); int updateByPrimaryKey(UserAccount record); }
1.4 实体类
package com.demo.pojo; import java.io.Serializable; public class UserAccount implements Serializable { private static final long serialVersionUID = 1L; private Integer id; private String username; private Integer age; private Long phone; private String email; private String account; private String pwd; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username == null ? null : username.trim(); } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Long getPhone() { return phone; } public void setPhone(Long phone) { this.phone = phone; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email == null ? null : email.trim(); } public String getAccount() { return account; } public void setAccount(String account) { this.account = account == null ? null : account.trim(); } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd == null ? null : pwd.trim(); } }
1.5 service层
Service
UserAccount getUserByAccount(String account);
ServiceImpl
@Override public UserAccount getUserByAccount(String account){ return userAccountMapper.getUserByAccount(account); }
1.6 添加session配置类
package com.demo.common; import org.springframework.context.annotation.Configuration; import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession; /** * Created by toutou on 2019/1/22. */ @Configuration @EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1800) /** * maxInactiveIntervalInSeconds: 设置 Session 失效时间,使用 Redis Session 之后,原 Boot 的 server.session.timeout 属性不再生效。 */ public class SessionRedisConfig { }
1.7 设置拦截器
关于拦截器不懂的话,可以看我之前的一篇文章《SpringBoot(十一)过滤器和拦截器》
/* * 进入controller层之前拦截请求 */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object obj) throws Exception { System.out.println("MyTestInterceptor 1111111111"); Object user= request.getSession().getAttribute("useraccount"); if (user==null){ // response.sendRedirect(request.getContextPath()+"/error"); response.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=utf-8"); response.getWriter().write("请先登录"); return false; } return true; }
@Override public void addInterceptors(InterceptorRegistry registry) { // 自定义拦截器,添加拦截路径和排除拦截路径 registry.addInterceptor(myTestInterceptor).addPathPatterns("/**").excludePathPatterns("/userlogin"); }
设置全局拦截
1.8 登录/注销
package com.demo.controller; import com.demo.pojo.UserAccount; import com.demo.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; /** * Created by toutou on 2019/1/22. */ @RestController public class LoginController { @Autowired UserService userService; @RequestMapping(value = "/userlogin") public String Login(HttpServletRequest request, String account, String pwd) { UserAccount user= userService.getUserByAccount(account); if (user!=null && user.getPwd().equals(pwd)){ request.getSession().setAttribute("useraccount",user); return "登录成功"; } return "登录失败"; } @RequestMapping(value = "/userlogout") public String logout (HttpServletRequest request){ HttpSession session=request.getSession(); session.removeAttribute("useraccount"); return "退出登录"; } }
原文地址:https://www.cnblogs.com/toutou/p/redis_session.html