【尚筹网项目】 十、 【前台】 会员登录 及使用 SpringSession 实现 Session共享

本文主要是介绍【尚筹网项目】 十、 【前台】 会员登录 及使用 SpringSession 实现 Session共享,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

会员登录

    • 一、思路
    • 二、会员登录代码实现
      • (1) 调整 member-login 页面
      • (2) 创建 MemberLoginVO
      • (3) 增加一些需要用到的 viewController
      • (4) MemberHandler
        • ★ 遇到的问题
      • (5) 退出登录
    • 三、会员登录功能延伸
    • 四、会话控制回顾
      • (1) Cookie 的工作机制
      • (2) Session 的工作机制
    • 五、Session 共享
      • (1) 后端统一存储 Session 数据
      • (2) SpringSession 使用 (测试)
        • ① 引入依赖
        • ② 配置文件
        • ③ handler
        • ④ 测试
        • ⑤ 查看 redis
      • (3) SpringSession 基本原理
        • ① SpringSession 需要完成的任务
        • ② SessionRepositoryFilter
        • ③ HttpSessionStrategy
        • ④ SessionRepository
        • ⑤ RedisOperationsSessionRepository
    • 六、登录检查
      • (1) 思路
      • (2) 设置 Session 共享
        • ① 加入依赖
        • ② 加入配置
      • (3) 准备不需要登录检查的资源
      • (4) 判断当前请求是否为静态资源
      • (5) ZuulFilter
      • (6) 登录页面读取 Session 域
      • (7) Zuul 中的特殊设置


一、思路

在这里插入图片描述

二、会员登录代码实现

(1) 调整 member-login 页面

在这里插入图片描述

在这里插入图片描述

(2) 创建 MemberLoginVO

登录成功后,将MemberLoginVO存入session中

@Data
@NoArgsConstructor
@AllArgsConstructor
public class MemberLoginVO {private Integer id;private String username;private String email;
}

(3) 增加一些需要用到的 viewController

在这里插入图片描述

// 浏览器访问的地址
// ① 注册请求
String toMemberRegPath = "/auth/member/to/reg/page.html";
String toMemberCenterPath = "/auth/member/to/center/page";
String toMemberLoginPath = "/auth/member/to/login/page";// 目标视图的名称
// ① 注册页面
String viewName = "member-reg";
String MemberCenterViewName = "member-center";
String MemberLoginViewName = "member-login";// 添加一个viewController
registry.addViewController(toMemberRegPath).setViewName(viewName);
registry.addViewController(toMemberCenterPath).setViewName(MemberCenterViewName);
registry.addViewController(toMemberLoginPath).setViewName(MemberLoginViewName);

(4) MemberHandler

在这里插入图片描述

// 登录请求
@RequestMapping("/auth/do/member/login")
public String login(@RequestParam("loginacct") String loginacct,@RequestParam("userpswd") String userpswd,ModelMap modelMap,HttpSession session
) {// 1 从mysql中通过loginacct查询对象ResultEntity<MemberPO> memberPO = mySQLRemoteService.getMemberPOByLoginAcctRemote(loginacct);// 2 如果查询失败if (ResultEntity.FAILED.equals(memberPO.getOperationResult())) {// 存储错误信息modelMap.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE,memberPO.getOperationMessage());return "member-login";}// 3 查询成功,比较二者的密码// ① 取出数据库中查询到的对象数据内容MemberPO mysqlData = memberPO.getQueryData();// ② 如果数据不存在if (mysqlData == null) {modelMap.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE,CrowdConstant.MESSAGE_LOGIN_FAILED);return "member-login";}String mysqlDataUserpswd = mysqlData.getUserpswd();// ③ 比较密码,因为盐值加密方式生成盐是随机的,所以不能使用equals等进行判等BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();boolean matchesResult = bCryptPasswordEncoder.matches(userpswd, mysqlDataUserpswd);// ④ 密码不一致 登录失败if(matchesResult == false) {modelMap.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE,CrowdConstant.MESSAGE_LOGIN_FAILED);return "member-login";}// 登录成功,将登录信息存入session中MemberLoginVO memberLoginVO = new MemberLoginVO(mysqlData.getId(),mysqlData.getUsername(),mysqlData.getEmail());session.setAttribute(CrowdConstant.ATTR_NAME_LOGIN_MEMBER, memberLoginVO);// 重定向概到 用户中心页面 (产生问题:session丢失,无法被thymeleaf解析到)return "redirect:/auth/member/to/center/page";// return "member-center";
}

在这里插入图片描述

★ 遇到的问题

无法解析session,推测 是由于zuul代理后,重定向后请求地址如下图所示,地址栏改变,且不保存第一次请求的数据,所以session失效了

在这里插入图片描述

此时访问的地址
在这里插入图片描述
在这里插入图片描述


解决办法:在zuul的配置文件中加入以下内容
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

(5) 退出登录

在这里插入图片描述

// 退出登录
@RequestMapping("/auth/do/logout")
public String logout(HttpSession session) {// 使session失效session.invalidate();return "redirect:/";
}

三、会员登录功能延伸

在这里插入图片描述

四、会话控制回顾

回顾以前学的 cookie 和 session 都有笔记

(1) Cookie 的工作机制

服务器端返回 Cookie 信息给浏览器
       Java 代码:response.addCookie(cookie 对象);
       HTTP 响应消息头:Set-Cookie: Cookie 的名字=Cookie 的值

浏览器接收到服务器端返回的 Cookie,以后的每一次请求都会把 Cookie 带上
      HTTP 请求消息头:Cookie: Cookie

(2) Session 的工作机制

获取 Session 对象:request.getSession()
     检查当前请求是否携带了 JSESSIONID 这个 Cookie
          带了:根据这个 JSESSIONID 在服务器端查找对应的 Session 对象
                能找到:就把找到的 Session 对象返回
                没找到:新建Session 对象返回,同时返回 JSESSIONID 的 Cookie
         没带:新建 Session 对象返回,同时返回JSESSIONID 的 Cookie


五、Session 共享

        在分布式和集群环境下,每个具体模块运行在单独的 Tomcat 上,而 Session 是被不同Tomcat 所“区隔”的,所以不能互通,会导致程序运行时,用户会话数据发生错误。有的服务器上有,有的服务器上没有。

(1) 后端统一存储 Session 数据

      后端存储 Session 数据时,一般需要使用 Redis 这样的内存数据库,而一 般不采用 MySQL 这样的关系型数据库。原因如下:
            Session 数据存取比较频繁。内存访问速度快。
            Session 有过期时间,Redis 这样的内存数据库能够比较方便实现过期释放。

在这里插入图片描述

Redis 可以配置主从复制集群,不担心单点故障。


(2) SpringSession 使用 (测试)

① 引入依赖

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

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.0.0.RELEASE</version>
</parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- 引入 springboot&redis 整合场景 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!-- 引入 springboot&springsession 整合场景 --><dependency><groupId>org.springframework.session</groupId><artifactId>spring-session-data-redis</artifactId></dependency>
</dependencies>
② 配置文件

ps:端口号要不同

注意:存入 Session 域的实体类对象需要支持序列化!!!
在这里插入图片描述
在这里插入图片描述

# redis 配置
spring.redis.host=192.168.44.129spring.redis.jedis.pool.max-idle=100# springsession 配置
spring.session.store-type=redis
③ handler

spring_session_a

@RestController
public class setSession {@RequestMapping("/set/session")public String setSession(HttpSession session) {session.setAttribute("springSession","hello springSession");return "添加session成功";}}

spring_session_b

@RestController
public class getSession {@RequestMapping("/get/session")public String getSession(HttpSession session) {return (String)session.getAttribute( "springSession");}
}
④ 测试

在这里插入图片描述

在这里插入图片描述

⑤ 查看 redis

在这里插入图片描述

(3) SpringSession 基本原理

① SpringSession 需要完成的任务

在这里插入图片描述

② SessionRepositoryFilter

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

③ HttpSessionStrategy

封装 Session 的存取策略;cookie 还是 http headers 等方式

在这里插入图片描述

④ SessionRepository

指定存取/删除/过期 session 操作的 repository

在这里插入图片描述

⑤ RedisOperationsSessionRepository

使用 Redis 将 session

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


六、登录检查

把项目中必须登录才能访问的功能保护起来,如果没有登录就访问则跳转到登录页面。

(1) 思路

在这里插入图片描述


(2) 设置 Session 共享

① 加入依赖

zuul 工程auth-consumer 工程 都加入以下依赖

在这里插入图片描述

<!-- 引入 springboot&redis 整合场景 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 引入 springboot&springsession 整合场景 -->
<dependency><groupId>org.springframework.session</groupId><artifactId>spring-session-data-redis</artifactId>
</dependency>
② 加入配置

zuul 工程auth-consumer 工程 都加入以下配置
在这里插入图片描述

spring:redis:host: 192.168.44.129 # 连接redis时使用的localhostsession:store-type: redis

(3) 准备不需要登录检查的资源

在这里插入图片描述

// 允许通过的请求路径集合
public static final Set<String> PASS_RES_SET = new HashSet<>();static {PASS_RES_SET.add("/");PASS_RES_SET.add("/auth/member/send/short/message.json"); // 发送验证码PASS_RES_SET.add("/auth/do/member/register");             // 执行注册PASS_RES_SET.add("/auth/do/member/login");                // 执行登录PASS_RES_SET.add("/auth/do/logout");                      // 退出登录PASS_RES_SET.add("/auth/member/to/reg/page.html");        // 到用户注册页面PASS_RES_SET.add("/auth/member/to/login/page");           // 到用户登录页面
}// 允许通过的静态资源集合
public static final Set<String> STATIC_RES_SET = new HashSet<>();static {STATIC_RES_SET.add("bootstrap");STATIC_RES_SET.add("css");STATIC_RES_SET.add("fonts");STATIC_RES_SET.add("img");STATIC_RES_SET.add("jquery");STATIC_RES_SET.add("layer");STATIC_RES_SET.add("script");STATIC_RES_SET.add("ztree");
}

(4) 判断当前请求是否为静态资源

在这里插入图片描述

// 用于判断某个 ServletPath 值是否对应一个静态资源
public static boolean judgeCurrentServletPathWetherStaticResource(String servletPath) {// 假如字符串无效if (servletPath == null || servletPath.length() == 0) {throw new RuntimeException(CrowdConstant.MESSAGE_ACCESS_FORBIDEN);}// 拆分字符串,分割路径判断是否为静态资源String[] strings = servletPath.split("/");// 考虑到第一个斜杠左边经过拆分后得到一个空字符串是数组的第一个元素,所以需要使用下标 1 取第二个元素String firstLevelPath = strings[1];// 判断是否在静态资源中return STATIC_RES_SET.contains(firstLevelPath);
}

(5) ZuulFilter

在这里插入图片描述

@Slf4j
@Component
public class CrowdAccessFilter extends ZuulFilter {// 过滤器类型,可选值有 pre、route、post、error。@Overridepublic String filterType() {// 这里返回“pre”意思是在目标微服务前执行过滤return "pre";}// 过滤器的执行顺序,数值越小,优先级越高。@Overridepublic int filterOrder() {return 0;}// 如果应调用run()方法,则为true[不放行]。false不会调用run()方法 [放行]@Overridepublic boolean shouldFilter() {log.info("到达shouldFilter");// 获取 RequestContext 对象RequestContext currentContext = RequestContext.getCurrentContext();// 通过 RequestContext 对象获取当前请求对象(框架底层是借助 ThreadLocal 从当前线程上获取事先绑定的 Request 对象)HttpServletRequest request = currentContext.getRequest();// 3.获取 servletPath 值String servletPath = request.getServletPath();// 根据 servletPath 判断当前请求是否对应可以直接放行的特定功能if (AccessPassResources.PASS_RES_SET.contains(servletPath)) {log.info("放行" + servletPath);// 在允许通过的请求路径集合中,返回false,不经过run()方法,即为放行return false;}// 5.判断当前请求是否为静态资源// 工具方法返回 true:说明当前请求是静态资源请求,取反为 false 表示放行不做登录检查// 工具方法返回 false:说明当前请求不是可以放行的特定请求也不是静态资源,取反为 truereturn !AccessPassResources.judgeCurrentServletPathWetherStaticResource(servletPath);}@Overridepublic Object run() throws ZuulException {// 不放行某个资源后,来到这个run()方法中// 目标: 判断当前用户是否处于登录状态// 获取 RequestContext 对象RequestContext currentContext = RequestContext.getCurrentContext();// 通过 RequestContext 对象获取当前请求对象(框架底层是借助 ThreadLocal 从当前线程上获取事先绑定的 Request 对象)HttpServletRequest request = currentContext.getRequest();log.info("被拦截: " + request.getServletPath());// 获取当前 Session 对象HttpSession session = request.getSession();// 从sesison中取出当前登录的用户信息MemberLoginVO loginMember = (MemberLoginVO) session.getAttribute(CrowdConstant.ATTR_NAME_LOGIN_MEMBER);// 若当前登录的用户信息为空,即未登录状态if (loginMember == null) {//log.info("当前无人登录");// 往session保存错误信息session.setAttribute(CrowdConstant.ATTR_NAME_MESSAGE,CrowdConstant.MESSAGE_No_LOGIN_MEMBER);// 获取 Response 对象,用来进行重定向HttpServletResponse response = currentContext.getResponse();try {// 重定向到 用户登录页面response.sendRedirect("/auth/member/to/login/page");} catch (IOException e) {e.printStackTrace();}}return null;}
}

(6) 登录页面读取 Session 域

在这里插入图片描述

在这里插入图片描述

<p th:text="${session.message}">这里登录检查后发现不允许访问时的提示消息</p>

(7) Zuul 中的特殊设置

在这里插入图片描述

在这里插入图片描述

这篇关于【尚筹网项目】 十、 【前台】 会员登录 及使用 SpringSession 实现 Session共享的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/526789

相关文章

详解Vue如何使用xlsx库导出Excel文件

《详解Vue如何使用xlsx库导出Excel文件》第三方库xlsx提供了强大的功能来处理Excel文件,它可以简化导出Excel文件这个过程,本文将为大家详细介绍一下它的具体使用,需要的小伙伴可以了解... 目录1. 安装依赖2. 创建vue组件3. 解释代码在Vue.js项目中导出Excel文件,使用第三

Linux alias的三种使用场景方式

《Linuxalias的三种使用场景方式》文章介绍了Linux中`alias`命令的三种使用场景:临时别名、用户级别别名和系统级别别名,临时别名仅在当前终端有效,用户级别别名在当前用户下所有终端有效... 目录linux alias三种使用场景一次性适用于当前用户全局生效,所有用户都可调用删除总结Linux

Oracle查询优化之高效实现仅查询前10条记录的方法与实践

《Oracle查询优化之高效实现仅查询前10条记录的方法与实践》:本文主要介绍Oracle查询优化之高效实现仅查询前10条记录的相关资料,包括使用ROWNUM、ROW_NUMBER()函数、FET... 目录1. 使用 ROWNUM 查询2. 使用 ROW_NUMBER() 函数3. 使用 FETCH FI

Python脚本实现自动删除C盘临时文件夹

《Python脚本实现自动删除C盘临时文件夹》在日常使用电脑的过程中,临时文件夹往往会积累大量的无用数据,占用宝贵的磁盘空间,下面我们就来看看Python如何通过脚本实现自动删除C盘临时文件夹吧... 目录一、准备工作二、python脚本编写三、脚本解析四、运行脚本五、案例演示六、注意事项七、总结在日常使用

Java实现Excel与HTML互转

《Java实现Excel与HTML互转》Excel是一种电子表格格式,而HTM则是一种用于创建网页的标记语言,虽然两者在用途上存在差异,但有时我们需要将数据从一种格式转换为另一种格式,下面我们就来看看... Excel是一种电子表格格式,广泛用于数据处理和分析,而HTM则是一种用于创建网页的标记语言。虽然两

java图像识别工具类(ImageRecognitionUtils)使用实例详解

《java图像识别工具类(ImageRecognitionUtils)使用实例详解》:本文主要介绍如何在Java中使用OpenCV进行图像识别,包括图像加载、预处理、分类、人脸检测和特征提取等步骤... 目录前言1. 图像识别的背景与作用2. 设计目标3. 项目依赖4. 设计与实现 ImageRecogni

Java中Springboot集成Kafka实现消息发送和接收功能

《Java中Springboot集成Kafka实现消息发送和接收功能》Kafka是一个高吞吐量的分布式发布-订阅消息系统,主要用于处理大规模数据流,它由生产者、消费者、主题、分区和代理等组件构成,Ka... 目录一、Kafka 简介二、Kafka 功能三、POM依赖四、配置文件五、生产者六、消费者一、Kaf

python管理工具之conda安装部署及使用详解

《python管理工具之conda安装部署及使用详解》这篇文章详细介绍了如何安装和使用conda来管理Python环境,它涵盖了从安装部署、镜像源配置到具体的conda使用方法,包括创建、激活、安装包... 目录pytpshheraerUhon管理工具:conda部署+使用一、安装部署1、 下载2、 安装3

Mysql虚拟列的使用场景

《Mysql虚拟列的使用场景》MySQL虚拟列是一种在查询时动态生成的特殊列,它不占用存储空间,可以提高查询效率和数据处理便利性,本文给大家介绍Mysql虚拟列的相关知识,感兴趣的朋友一起看看吧... 目录1. 介绍mysql虚拟列1.1 定义和作用1.2 虚拟列与普通列的区别2. MySQL虚拟列的类型2

使用MongoDB进行数据存储的操作流程

《使用MongoDB进行数据存储的操作流程》在现代应用开发中,数据存储是一个至关重要的部分,随着数据量的增大和复杂性的增加,传统的关系型数据库有时难以应对高并发和大数据量的处理需求,MongoDB作为... 目录什么是MongoDB?MongoDB的优势使用MongoDB进行数据存储1. 安装MongoDB