Cookie与Session 实现登录操作

Cookie

Cookie 是网络编程中使用最广泛的一项技术,主要用于辨识用户身份。

客户端(浏览器)与网站服务端通讯的过程如下图所示:

img

从图中看,服务端既要返回 Cookie 给客户端,也要读取客户端提交的 Cookie。所以本节课主要学习服务端 Spring 工程是如何使用 Cookie 的,有两种操作。

浏览器如何使用 Cookie ,在《Java 网络编程》课程中讲解。

读 Cookie

control 类的方法增加一个 HttpServletRequest 参数,通过 request.getCookies() 取得 cookie 数组。然后再循环遍历数组即可。(下列演示代码省略循环代码)

系统会自动传入方法参数所需要的 HttpServletRequest 对象哦

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;@RequestMapping("/songlist")
public Map index(HttpServletRequest request) {Map returnData = new HashMap();returnData.put("result", "this is song list");returnData.put("author", songAuthor);Cookie[] cookies = request.getCookies();returnData.put("cookies", cookies);return returnData;
}

请看代码演示:

从浏览器输出结果可以看到,打印出了所有的 Cookie 数据。

cookie 有很多属性值,各属性值的作用请 点击查看文档

使用注解读取 cookie

如果知道 cookie 的名字,就可以通过注解的方式读取,不需要再遍历 cookie 数组了,更加方便。

control 类的方法增加一个 @CookieValue("xxxx") String xxxx 参数即可,注意使用时要填入正确的 cookie 名字。

系统会自动解析并传入同名的 cookie

import org.springframework.web.bind.annotation.CookieValue;@RequestMapping("/songlist")
public Map index(@CookieValue("JSESSIONID") String jSessionId) {Map returnData = new HashMap();returnData.put("result", "this is song list");returnData.put("author", songAuthor);returnData.put("JSESSIONID", jSessionId);return returnData;
}

请看代码演示:

注意:如果系统解析不到指定名字的 cookie,使用此注解就会报错。必须谨慎使用。

写 Cookie

同样,也很简单。为 control 类的方法增加一个 HttpServletResponse 参数,调用 response.addCookie() 方法添加 Cookie 实例对象即可。

系统会自动传入方法参数所需要的 HttpServletResponse 对象哦

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;@RequestMapping("/songlist")
public Map index(HttpServletResponse response) {Map returnData = new HashMap();returnData.put("result", "this is song list");returnData.put("name", songName);Cookie cookie = new Cookie("sessionId","CookieTestInfo");// 设置的是 cookie 的域名,就是会在哪个域名下生成 cookie 值cookie.setDomain("youkeda.com");// 是 cookie 的路径,一般就是写到 / ,不会写其他路径的cookie.setPath("/");// 设置cookie 的最大存活时间,-1 代表随浏览器的有效期,也就是浏览器关闭掉,这个 cookie 就失效了。cookie.setMaxAge(-1);// 设置是否只能服务器修改,浏览器端不能修改,安全有保障cookie.setHttpOnly(false);response.addCookie(cookie);returnData.put("message", "add cookie successful");return returnData;
}

Cookie 的各个属性的作用,注意看代码注释哦

注意,Cookie 类的构造函数,第一个参数是 cookie 名称,第二个参数是 cookie 值。而且其他的属性,需要根据实际情况和具体的业务需求决定。

Spring Session API

Cookie 放在客户端,可以存储用户登录信息,主要用于辨识用户身份。

但如果真的把用户ID登录状态等重要信息放入 cookie,会带来安全隐患,因为网络上很不安全,cookie可能会拦截、甚至伪造。

采用 Session 会话机制可以解决这个问题,用户ID登录状态等重要信息不存放在客户端,而是存放在服务端,从而避免安全隐患。

使用会话机制时,Cookie 作为 session id 的载体与客户端通信。上一节课演示代码中,Cookie 中的 JSESSIONID 就是这个作用。

名字为 JSESSIONID 的 cookie,是专门用来记录用户session的。JSESSIONID 是标准的、通用的名字。

在了解 SessionCookie 之间的关系后,我们来学习如何使用 Session,也分为读、写两种操作。

读操作

cookie 相似,从 HttpServletRequest 对象中取得 HttpSession 对象,使用的语句是 request.getSession()

但不同的是,返回结果不是数组,是对象。在 attribute 属性中用 key -> value 的形式存储多个数据。

假设存储登录信息的数据 keyuserLoginInfo,那么语句就是 session.getAttribute("userLoginInfo")

登录信息类

登录信息实例对象因为要在网络上传输,就必须实现序列化接口 Serializable ,否则不实现的话会报错。

登录信息类需要根据具体的需要设计属性字段。下列代码的两个属性仅供演示。

import java.io.Serializable;public class UserLoginInfo implements Serializable {private String userId;private String userName;
}

操作代码

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;@RequestMapping("/songlist")
public Map index(HttpServletRequest request, HttpServletResponse response) {Map returnData = new HashMap();returnData.put("result", "this is song list");// 取得 HttpSession 对象HttpSession session = request.getSession();// 读取登录信息UserLoginInfo userLoginInfo = (UserLoginInfo)session.getAttribute("userLoginInfo");if (userLoginInfo == null) {// 未登录returnData.put("loginInfo", "not login");} else {// 已登录returnData.put("loginInfo", "already login");}return returnData;
}

这里实际上取不到数据,因为还没有写入。

写操作

假设登录成功,怎么记录登录信息到 Session 呢?

既然从 HttpSession 对象中读取登录信息用的是 getAttribute() 方法,那么写入登录信息就用 setAttribute() 方法。

下列代码演示了使用 Session 完成登录的过程,略去了校验用户名和密码的步骤(实际项目中需要):

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;@RequestMapping("/loginmock")
public Map loginMock(HttpServletRequest request, HttpServletResponse response) {Map returnData = new HashMap();// 假设对比用户名和密码成功// 仅演示的登录信息对象UserLoginInfo userLoginInfo = new UserLoginInfo();userLoginInfo.setUserId("12334445576788");userLoginInfo.setUserName("ZhangSan");// 取得 HttpSession 对象HttpSession session = request.getSession();// 写入登录信息session.setAttribute("userLoginInfo", userLoginInfo);returnData.put("message", "login successful");return returnData;
}

额外知识点

Cookie 存放在客户端,一般不能超过 4kb ,要特别注意,放太多的内容会导致出错;而 Session 存放在服务端,没有限制,不过基于服务端的性能考虑也不能放太多的内容。

Spring Session 配置

上面学了Session 的操作,在操作中,没有涉及到 cookie。系统会自动把默认的 JSESSIONID 放在默认的 cookie 中。

Cookie 作为 session id 的载体,也可以修改属性。

前置知识点:配置

第 6 章我们讲了 application.propertiesSpringBoot 的标准配置文件,配置一些简单的属性。同时,SpringBoot 也提供了编程式的配置方式,主要用于配置 Bean

基本格式:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class SpringHttpSessionConfig {@Beanpublic TestBean testBean() {return new TestBean();}
}

在类上添加 @Configuration 注解,就表示这是一个配置类,系统会自动扫描并处理。

在方法上添加 @Bean 注解,表示把此方法返回对象实例注册成 Bean

@Service 等写在类上的注解一样,都表示注册 Bean

Session 配置

依赖库

先在 pom.xml 文件中增加依赖库:

<!-- spring session 支持 -->
<dependency><groupId>org.springframework.session</groupId><artifactId>spring-session-core</artifactId>
</dependency>

配置类

在类上额外添加一个注解:@EnableSpringHttpSession ,开启 session 。然后,注册两个 bean

  • CookieSerializer:读写 Cookies 中的 SessionId 信息
  • MapSessionRepository:Session 信息在服务器上的存储仓库。
import org.springframework.session.MapSessionRepository;
import org.springframework.session.config.annotation.web.http.EnableSpringHttpSession;
import org.springframework.session.web.http.CookieSerializer;
import org.springframework.session.web.http.DefaultCookieSerializer;import java.util.concurrent.ConcurrentHashMap;@Configuration
@EnableSpringHttpSession
public class SpringHttpSessionConfig {@Beanpublic CookieSerializer cookieSerializer() {DefaultCookieSerializer serializer = new DefaultCookieSerializer();serializer.setCookieName("JSESSIONID");// 用正则表达式配置匹配的域名,可以兼容 localhost、127.0.0.1 等各种场景serializer.setDomainNamePattern("^.+?\\.(\\w+\\.[a-z]+)$");serializer.setCookiePath("/");serializer.setUseHttpOnlyCookie(false);// 最大生命周期的单位是秒serializer.setCookieMaxAge(24 * 60 * 60);return serializer;}// 当前存在内存中@Beanpublic MapSessionRepository sessionRepository() {return new MapSessionRepository(new ConcurrentHashMap<>());}
}

想必大家已经了解了 Cookie 各属性值的作用,这里就不赘述了。

这段代码有些长,是 Spring 官方推荐的比较标准的写法:点此阅读官方文档。当前的重点是学习如何使用。

Spring Request 拦截器

在上节课的练习中我们模拟了用户登录以及提供了查询登录状态的方法。
在实际的项目中,会有大量的页面功能是需要判断用户是否登录的。例如电商的网站,订单、购物车、管理收货地址等等,都需要登录。那么让每一个页面都判断是否登录、未登录跳转到登录页,就太繁琐了,也不利于维护。
所以需要一种统一处理相同逻辑的机制,Spring 提供了 HandlerInterceptor(拦截器)满足这种场景的需求。

实现拦截器也不同负责,有三个步骤:

一、创建拦截器

拦截器必须实现 HandlerInterceptor 接口。可以在三个点进行拦截:

  1. Controller方法执行之前。这是最常用的拦截点。例如是否登录的验证就要在 preHandle() 方法中处理。
  2. Controller方法执行之后。例如记录日志、统计方法执行时间等,就要在 postHandle() 方法中处理。
  3. 整个请求完成后。不常用的拦截点。例如统计整个请求的执行时间的时候用,在 afterCompletion 方法中处理。

请看下列示例代码:

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;public class InterceptorDemo implements HandlerInterceptor {// Controller方法执行之前@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 只有返回true才会继续向下执行,返回false取消当前请求return true;}//Controller方法执行之后@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,ModelAndView modelAndView) throws Exception {}// 整个请求完成后(包括Thymeleaf渲染完毕)@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}
}
preHandle()` 方法的参数中有 `HttpServletRequest` 和 `HttpServletResponse`,可以像 `control` 中一样使用 `Session

二、实现 WebMvcConfigurer

创建一个类实现 WebMvcConfigurer,并实现 addInterceptors() 方法。这个步骤用于管理拦截器。

注意:实现类要加上 @Configuration 注解,让框架能自动扫描并处理。

管理拦截器,比较重要的是为拦截器设置拦截范围。常用 addPathPatterns("/**") 表示拦截所有的 URL

当然也可以调用 excludePathPatterns() 方法排除某些 URL,例如登录页本身就不需要登录,需要排除。

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class WebAppConfigurerDemo implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 多个拦截器组成一个拦截器链// 仅演示,设置所有 url 都拦截registry.addInterceptor(new UserInterceptor()).addPathPatterns("/**");}
}

学习拦截器,要注意理解和体会 拦截器管理拦截器 分开的思想。思考一下:如果不分开处理,由拦截器本身决定在什么情况下进行拦截,是否更好?

通常拦截器,会放在一个包(例如interceptor)里。而用于管理拦截器的配置类,会放在另一个包(例如config)里。

这种按功能划分子包的方式,可以让阅读者比较直观、清晰的了解各个类的作用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/48794.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Domainim:一款高效的企业级网络安全扫描工具

关于Domainim Domainim是一款功能强大的企业级网络安全扫描工具&#xff0c;该工具运行效率高&#xff0c;功能完善&#xff0c;可以帮助广大研究人员针对企业或组织网络执行大规模安全扫描任务。 该工具可以快速执行网络安全扫描和域名/子域名网络侦查任务&#xff0c;旨在使…

python毕业设计选题协同过滤算法在音乐推荐系统

✌网站介绍&#xff1a;✌10年项目辅导经验、专注于计算机技术领域学生项目实战辅导。 ✌服务范围&#xff1a;Java(SpringBoo/SSM)、Python、PHP、Nodejs、爬虫、数据可视化、小程序、安卓app、大数据等设计与开发。 ✌服务内容&#xff1a;免费功能设计、免费提供开题答辩P…

暑期C++ 缺省参数

有任何不懂的问题可以评论区留言&#xff0c;能力范围内都会一一回答 1.缺省参数的概念 缺省参数是是声明或定义参数时为函数的参数指定一个缺省值。在调用该函数值时&#xff0c;如果没有指定实参则采用该形参的缺省值&#xff0c;否则使用指定的实参 看了上面定义后&#…

【零基础必看的前端教程】——JavaScript(七)数组

欢迎大家打开前端的新篇章——JavaScript&#xff0c;JavaScript与HTML、CSS合称为前端三大件&#xff0c;JavaScript是前端的重中之重&#xff0c;小洪将继续以零基础视角&#xff0c;带你循序渐进学习前端知识&#xff0c;一看就懂&#xff0c;小白也能转行做前端&#xff01…

vue3实现在新标签中打开指定的网址

有一个文件列表&#xff0c;如下图&#xff1a; 我希望点击查看按钮的时候&#xff0c;能够在新的标签页面打开这个文件的地址进行预览&#xff0c;该如何实现呢&#xff1f; 比如&#xff1a; 实际上要实现这个并不难&#xff0c;参考demo如下&#xff1a; 首先&#x…

渗透测试——利用公网反弹shell到本地的两种方式,vmware虚拟机与主机的端口转发,本地ssh无法上线的问题解决

解决问题&#xff1a; 因长期使用本地模拟靶场&#xff0c;实战护网时并非模拟靶场&#xff0c;shell反弹需要利用公网测试。解决目标站无法反弹到本地的情况。解决本地是windows&#xff0c;虚拟机是kail、linux&#xff0c;无法相互转换流量的情况。 环境搭建 靶机 centOS7 …

VScode 批量操作

VScode 批量操作 批量修改 按住 alt/option 键&#xff0c; 选择需要批量操作的位置 如果是多行&#xff0c;则按住 altshift 键 可以直接操作 但是有时候比如变量命名&#xff0c;可能需要递增操作的命名 需要下载插件 Increment Selection 按照1的方法多选光标之后&am…

html+css+js前端作业 王者荣耀官网5个页面带js

htmlcssjs前端作业 王者荣耀官网5个页面带js 下载地址 https://download.csdn.net/download/qq_42431718/89574989 目录1 目录2 目录3 项目视频 王者荣耀5个页面&#xff08;带js&#xff09; 页面1 页面2 页面3 页面4 页面5

php接口返回的json字符串,json_decode()失败,原来是多了红点

问题&#xff1a; 调用某个接口返回的json&#xff0c;json_decode()失败&#xff0c;返回数据为null&#xff0c; echo json_last_error();返回错误码 4 经过多次调试发现&#xff1a;多出来一个红点&#xff0c;预览是看不到的。 解决&#xff1a;要去除BOM头部 $resul…

vue 搜索框

效果 创建搜索组件&#xff1a; 在Vue项目中&#xff0c;首先需要创建一个搜索组件。这个组件通常包含一个输入框和一个搜索按钮。使用v-model指令将输入框与组件的数据属性&#xff08;如searchKeyword&#xff09;进行双向绑定&#xff0c;以便获取用户输入的关键词。处理搜索…

Linux网络:传输层协议TCP(二)三次挥手四次握手详解

目录 一、TCP的连接管理机制 1.1三次握手 1.2四次挥手 二、理解 TIME_WAIT 状态 2.1解决TIME_WAIT 状态引起的 bind 失败的方法 三、理解CLOSE_WAIT状态 一、TCP的连接管理机制 在正常情况下, TCP 要经过三次握手建立连接, 四次挥手断开连接 1.1三次握手 三次握手顾名思…

Docker从零开始:安装、部署到卸载,一文搞定全流程

Docker是一种开源容器化平台&#xff0c;它允许开发者将应用程序及其依赖打包成轻量级、可移植的容器。这些容器能确保软件在任何环境中稳定运行&#xff0c;无论是开发者的笔记本电脑还是生产服务器。Docker流行的原因在于其提供的隔离性、可移植性和可扩展性&#xff0c;它简…

2024年展望:人工智能领域将呈现怎样的发展趋势?

2024年&#xff0c;人工智能&#xff08;AI&#xff09;领域将继续保持强劲的发展势头&#xff0c;并呈现出多个重要的发展趋势。以下是对该领域未来发展趋势的详细展望&#xff1a; 一、技术创新与融合 多模态生成式AI的崛起&#xff1a; 多模态生成式AI系统能够处理文本、声…

C# 将字符串数组以树型结构化

例如字符串数组&#xff1a; string[] arr { "1","3-4-5-6-7", "2","3-4","3-4-5","3-4-5-6", "3", "6", "4", "6-1", "6-2", "5", "6-1-1&…

李艳波医生怎么挂号?

对于想要预约李艳波医生的患者来说&#xff0c;北京仁爱堂提供了两种便捷的预约方式&#xff1a;来院面诊和视频会诊。来院面诊是传统的就诊方式&#xff0c;患者可以直接前往仁爱堂&#xff0c;与李艳波医生面对面交流&#xff0c;详细了解自己的病情并接受专业的治疗建议。这…

解决Github Copilot无效,无法使用的问题

如果是在Copilot的终端报错 Invalid copilot token: missing token: 403 原因有三种 1&#xff0c;你的账号没有订阅正版的服务&#xff0c;解决办法是购买正版服务 2&#xff0c;你在购买服务的时候&#xff0c;Github上 billing information 地址信息和支付卡片的地址信息不…

关卡1-3:Git

关卡1-3&#xff1a;Git Git基础fork并拉取本次课程的源创建一个gitee自己的仓库 这个是internLM的3期训练营的通关笔记。 任务&#xff1a; 熟悉git熟悉使用git托管平台&#xff0c;常见有github、giteefork官方的训练营的教程项目&#xff0c;提交文件到自己的项目&#xf…

openGauss触发器详解

openGauss 是一款开源关系型数据库管理系统&#xff0c;广泛应用于企业级应用中。随着数据量的增长和业务逻辑的复杂化&#xff0c;数据库管理和操作的自动化需求越来越高。触发器&#xff08;Triggers&#xff09;作为数据库中重要的编程工具&#xff0c;能够极大地简化复杂操…

【python】OpenCV—Point Polygon Test

文章目录 1、完整代码2、涉及到的库cv2.pointPolygonTestcv2.minMaxLoc 1、完整代码 from __future__ import print_function from __future__ import division import cv2 as cv import numpy as np # Create an image r 100 src np.zeros((4*r, 4*r), dtypenp.uint8) # 创…

前端学习3——自学习梳理

1.学习一下盒子模型(盒子就是元素&#xff0c;标签) 盒子模型又分为4种&#xff1a;块级&#xff0c;内联级&#xff0c;内联块级&#xff0c;弹性盒子 (弹性盒子续在下一节) 2.元素的结构 1.盒子模型 <!DOCTYPE html> <html lang"en"> <head>&l…