Javaweb登录校验

登录校验

JWT令牌的相关操作需要添加相关依赖

<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version>
</dependency>

一、摘要

场景:当我们想要访问一个网站时,我们一般都需要进行登录验证,这时候就会涉及到一系列的问题

问题:

  • 什么时候进行登录校验?
  • 登录校验你该如何实现?如何设计高效,高健壮性的校验方式?
  • 进行登录校验时,你是以什么方式进行数据传递的?如何实现数据的传递

二、为什么要进行身份校验

试想以下场景,我们在如果我们对一个网站不进行有效的登录校验,而是可以直接通过网址:http://localhost:90/#/system/emp直接进入到网站内部,是不是极其不安全,我们应该在响应每一次请求前都进行一次登录校验,如果用户还没有登录。那么就必须先进行登录校验才能进行后续操作

在这里插入图片描述

三、拦截器

我们已经知道使用登录校验的使用场景,那么我们该如何设计一个高效的登录校验功能?是在每一个请求处理函数里面都添加一次if判断吗?这显然是不合理的,如果我们一个网站功能很复杂,那么面对如此多的请求并在每一个请求中都添加一个if判断就显得十分low,而且健壮性不高,不利于维护。因此,我们就引出了拦截器的概念,我们创建一个工具类,用于处理每一次请求,我们每次向服务器发送请求时,都需要先经过拦截器,经过身份校验后再能传递到对应的处理函数,然后,服务器响应回来的数据同样需要先经过拦截器才能返回给浏览器

在这里插入图片描述

四、传递数据三种方式

我们已经知道拦截器的概念了,接下来就很有必要了解数据传递这样玩意,由于HTTP 协议是无状态的,这意味着每个请求都是独立的,服务器不会保存关于之前请求的任何信息。每次客户端发送请求时,服务器都会独立处理该请求,并在发送响应后忘记关于该请求的任何信息。这就意味着,即使我们已经通过登录页面登录进去了,但是浏览器还是不知道我们已经登录了,因为每一次请求都是独立的,所以后续我们进行的每一次请求,都至少要携带用户登录的信息,如用户名,密码等,这样才能正常通过拦截器,将数据发送给后端继续后续操作,如果不能实现数据传递,这样就很麻烦了,所以这里就引出了以下几种数据传递的方式,会话跟踪技术

在这里插入图片描述

  • 客户端会话跟踪技术:Cookie
  • 服务器会话跟踪技术:Session
  • 令牌技术

这三种方式各有优缺点,以下是三种技术的优缺点对比

在这里插入图片描述

简单解释为什么 session会话无法在集群中使用:

在我们进行登录验证时,如果用户没有进行登录过,就会在服务器那边创建一个session记录,在用户下次登录时会自动检测判断用户有没有登录过,但是集群技术呢,会将我们的数据存储在不同的服务器中,而不是只保存在一个服务器,这就导致了如果我们下次访问资源时访问了另一个没有进行登录校验过的服务器,就得重新进行验证,所以无法在集群中有效使用

由于session的底层还是由cookie,所以无法在移动端中使用,而且用户甚至可以禁用掉cookie,慢慢的,cookie和session技术已不再是主流,目前的主流技术就是令牌技术,这里我们以JWT令牌技术为例

在这里插入图片描述

为什么令牌技术能够解决集群认证,移动端认证等问题?

在我们们的登录请求发送到后端时,后端服务器会检测有没有创建令牌,如果没有,那么就创建令牌并传递回前端,然后前端将令牌保存起来,注意这里的保存可以保存在任意存储空间中,可以保存到cookie,也可以保存在其他地方,在后续的请求中,前端浏览器会自动将令牌发送给后端,这样就解决了cookie技术不能在安卓中使用的问题,同时,因为令牌经Base64编码和数字签名加密,安全性也不需要考虑,解决了cookie安全性不高的问题,由于令牌也不在后端服务器中保存,因此也不会存在集群的问题。在令牌发送给后端后会解析令牌,获取其中的数据,这样就能通过登录校验了。

五、相关会话技术的实现

1、cookie技术
@Slf4j
@RestController
public class SessionController {/*** 设置Cookie* @param response 响应* @return 设置Cookie*/@GetMapping("/setCookie")public Result setCookie(HttpServletResponse response){response.addCookie(new Cookie("name", "zhangsan"));log.info("设置Cookie成功, name=zhangsan");return Result.success();}/*** 获取Cookie* @param request 请求* @return 获取Cookie*/@GetMapping("/getCookie")public Result getCookie(HttpServletRequest request){Cookie[] cookies = request.getCookies();for(Cookie cookit : cookies){if(cookit.getName().equals("name")){log.info("获取Cookie成功, name={}", cookit.getValue());}}return Result.success();}}
2、session技术

如果是第一次请求,那么session对象是不存在的,后端服务器会自动创建一个session对象,每个对象都有一个对应的ID,然后服务器响应数据时,会以cookie的方式将sessionID返回给浏览器,即在响应头中添加了一个Set-Cookie:JSESSIONID=session_ID,然后浏览器会自动将这个cookie存储在浏览器,然后在后续的每一次请求当中都会将cookie中的数据获取出来并携带到服务端,然后服务器通过请求中的session_ID查看本地有没有这个会话对象,这样就能在同一个会话的多次请求中共享数据了

   /*** 设置Session* @param session 会话* @return 设置Session*/@GetMapping("/session1")// 如果session对象不存在,会自动创建一个,如果存在,会直接返回session对象public Result session1(HttpSession session){log.info("HttpSession-s1: {}", session.hashCode());session.setAttribute("loginUser", "tom"); //往session中存储数据log.info("session.getAttribute: {}", session.getAttribute("loginUser"));return Result.success();}/*** 设置Session* @param request 请求* @return 设置Session*/@GetMapping("/session2")public Result session2(HttpServletRequest request){// 如果session对象不存在,会自动创建一个,如果存在,会直接返回session对象HttpSession session = request.getSession();log.info("HttpSession-s2: {}", session.hashCode());Object loginUser = session.getAttribute("loginUser"); //从session中获取数据log.info("loginUser: {}", loginUser);return Result.success(loginUser);}
3、JWT令牌

同样的,第一次请求时没有JWT令牌会创建令牌并返回给前端,前端会将这个令牌保存起来,在以后的每一次请求中,都会在请求头中携带这个令牌(token),然后通过后端拦截器或过滤器的解析验证,进行后续操作

 /*** 生成JWT令牌*/@Testpublic void generateJwt() {String signKey = "terminal";Long expire = 43200000L;Map<String, Object> claims = new HashMap<>();claims.put("username", "admin");String jwt = Jwts.builder().addClaims(claims).signWith(SignatureAlgorithm.HS256, signKey).setExpiration(new Date(System.currentTimeMillis() + expire)).compact();System.out.println("jwt = " + jwt);}/*** 解析JWT令牌*/@Testpublic void parseJWT() {String signKey = "terminal";String token = "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNzE3MDQxMjk2fQ.p-UYxhJQY30cYELv8hETYHWMs74D-lo1a0tdi3xXXBU";String jwt = Jwts.parser().setSigningKey(signKey).parseClaimsJws(token).getBody().toString();System.out.println("jwt = " + jwt);}

六、使用过滤器和拦截器进行身份验证

1、过滤器(Filter)
package com.example.filter;import com.alibaba.fastjson.JSONObject;
import com.example.pojo.Result;
import com.example.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;// @WebFilter(urlPatterns = "/*") 表示拦截所有请求
@Slf4j
@WebFilter(urlPatterns = "/*")
public class LoginCheckFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {log.info("demoFilter init");Filter.super.init(filterConfig);}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest) servletRequest;HttpServletResponse response = (HttpServletResponse) servletResponse;// 获取URLString url = request.getRequestURI().toString();if (url.contains("login")) {filterChain.doFilter(servletRequest, servletResponse);return;}// 获取tokenString jwt = request.getHeader("token");log.info("token = {}", jwt);// 如果token为空,但会未登录信号if (!StringUtils.hasLength(jwt)) {Result result = Result.error("NOT_LOGIN");String notLogin = JSONObject.toJSONString(result);response.getWriter().write(notLogin);return;}try {JwtUtils.parseJWT(jwt);} catch (Exception e) {e.printStackTrace();log.info("jwt令牌无效");Result result = Result.error("NOT_LOGIN");String notLogin = JSONObject.toJSONString(result);response.getWriter().write(notLogin);return;}//放行filterChain.doFilter(servletRequest, servletResponse);}@Overridepublic void destroy() {log.info("demoFilter destroy");Filter.super.destroy();}
}
2、拦截器(Interceptor)
  • 拦截器配置
package com.example.config;import com.example.interceptor.LoginCheckInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/*** 拦截器配置类*/
// Configuration 设置该类为一个配置类
@Slf4j
@Configuration
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate LoginCheckInterceptor loginCheckInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 注册一个拦截器,拦截除了/login之外的所有请求registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**").excludePathPatterns("/login");}
}
  • 拦截器处理逻辑
package com.example.interceptor;import com.alibaba.fastjson.JSONObject;
import com.example.pojo.Result;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.util.StringUtils;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/*** 拦截器处理逻辑*/
@Component
public class LoginCheckInterceptor implements HandlerInterceptor {@Override// 目标资源方法前执行,true为方行,false为拦截// 与doFilter的fileChain前执行的方法类似public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 获取tokenString token = request.getHeader("token");// 如果token为空,返回未登录信号if(!StringUtils.hasLength(token)){Result error = Result.error("NOT_LOGIN");String notLogin = JSONObject.toJSONString(error);response.getWriter().write(notLogin);return false;}// 放行return true;}@Override// 目标资源方法后执行后执行// 与doFilter的fileChain后执行的方法类似public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("postHandle");}@Override// 试图渲染完毕后执行,最后执行public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("afterCompletion");}
}

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

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

相关文章

Vue开发中Element UI/Plus使用指南:常见问题(如Missing required prop: “value“)及中文全局组件配置解决方案

文章目录 一、vue中使用el-table的typeindex有时不显示序号Table 表格显示索引自定义索引报错信息解决方案 二、vue中Missing required prop: “value” 报错报错原因解决方案 三、el-table的索引值index在翻页的时候可以连续显示方法一方法二 四、vue3中Element Plus全局组件配…

VMware RedHat虚拟机磁盘扩容(添加磁盘和扩展磁盘)

前言 自己的电脑上配一个虚拟机还是很有必要的&#xff0c;用起来比双系统方便一点&#xff0c;之前搞了100g的ubuntu没用到&#xff0c;后面重装redhat觉得随便搞个20g就够用了&#xff0c;后面用到之后就遇到磁盘不够用的情况&#xff0c;只能说情况允许的话&#xff0c;磁盘…

CityEngine记录1:工程目录

CityEngine的工程目录结构对于理解和组织3D城市建模项目至关重要。以下是对CityEngine工程目录结构的详细解析&#xff1a; Assets&#xff1a; 存放模型的零件与纹理图片。这些资产通常用于在建模过程中为建筑物、道路、植被等元素添加详细的纹理和细节。 Data&#xff1a; …

自学鸿蒙HarmonyOS的ArkTS语言<一>基本语法

一、一个ArkTs的目录结构 二、一个页面的结构 A、装饰器 Entry 装饰器 : 标记组件为入口组件&#xff0c;一个页面由多个自定义组件组成&#xff0c;但是只能有一个组件被标记 Component : 自定义组件, 仅能装饰struct关键字声明的数据结构 State&#xff1a;组件中的状态变量…

图像处理:Python使用OpenCV进行图像锐化 (非锐化掩模、拉普拉斯滤波器)

文章目录 非锐化掩模 (Unsharp Masking)拉普拉斯滤波器 (Laplacian Filter)效果对比总结 在图像处理中&#xff0c;锐化操作用于增强图像的边缘和细节&#xff0c;使图像看起来更清晰。常见的图像锐化方法包括非锐化掩模&#xff08;Unsharp Masking&#xff09;和拉普拉斯滤波…

AI 音乐大模型:创新的曙光还是创意产业的阴影?

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

[Kubernetes] etcd 单机和集群部署

文章目录 1.etcd基本概念2.etcd的基本知识3.etcd优势4.etcd单机部署4.1 linux部署4.2 windows部署4.3 docker安装etcd 5.etcd集群部署 1.etcd基本概念 etcd是一个高可用的分布式键值存储系统&#xff0c;是CoreOS&#xff08;现在隶属于Red Hat&#xff09;公司开发的一个开源…

人工智能这么厉害,比如GPT-4,为什么没有看到程序员大量失业?

从ChatGPT第一版发布到现在&#xff0c;还不到一年的时间中&#xff0c;可是它使用的GPT架构已经从3.5版本进化到现在的4.0版本&#xff0c;随之而来的是其能力的极大提升。下面是GPT-4在其官网的介绍中的一句话&#xff1a; GPT-4是OpenAI最先进的系统&#xff0c;可以产生更安…

数学建模基础:数学建模概述

目录 前言 一、数学建模的步骤 二、模型的分类 三、模型评价指标 四、常见的数学建模方法 实际案例&#xff1a;线性回归建模 步骤 1&#xff1a;导入数据 步骤 2&#xff1a;数据预处理 步骤 3&#xff1a;建立线性回归模型 步骤 4&#xff1a;模型验证 步骤 5&…

python-基础篇-文件和异常

文章目录 文件和异常读写文本文件读写二进制文件读写JSON文件 文件和异常 实际开发中常常会遇到对数据进行持久化操作的场景&#xff0c;而实现数据持久化最直接简单的方式就是将数据保存到文件中。说到“文件”这个词&#xff0c;可能需要先科普一下关于文件系统的知识&#…

渗透测试基础(四) MS08-067 漏洞攻击

1. 漏洞介绍 漏洞描述 Microsoft Windows Server服务RPC请求缓冲区溢出漏洞Windows的Server服务在处理特质RPC请求时存在缓冲区溢出漏洞&#xff0c;远程攻击者可以通过发送恶意的RPC请求触发这个溢出&#xff0c;导致完全入侵用户系统&#xff0c;以SYSTEM权限执行任意指令。…

C#使用Scoket实现服务器和客户端互发信息

20240616 By wdhuag 目录 前言&#xff1a; 参考&#xff1a; 一、服务器端&#xff1a; 1、服务器端口绑定&#xff1a; 2、服务器关闭&#xff1a; 二、客户端&#xff1a; 1、客户端连接&#xff1a; 2、客户端断开&#xff1a; 三、通讯&#xff1a; 1、接收信…

Python4 操作MySQL数据库

通过python的pymysql库连接到本地的MySQL数据库&#xff0c;并执行查询操作来获取数据&#xff0c;然后打印出每一行的数据&#xff0c;这里以一个简单的学生表为例进行介绍。 1. MySQL的安装与数据准备 首先需要安装MySQL&#xff0c;在安装完成之后使用Navicat与本地数据库…

1-函数极限与连续

1 2 平方项没有考虑到&#xff08;其正负&#xff09;

gitlab-cicd-k8s

k8s已经准备好 kubectl get node 创建cicdYaml文件 kubectl create namespace gitlab-cicd --dry-runclient --outputyaml >> gitlab-cicd.yaml kubectl apply -f gitlab-cicd.yaml 服务器和仓库在一起可用专有地址 使用 GitLab Runner 可以自动执行 GitLab CI/CD 管道…

【WEB前端2024】3D智体编程:乔布斯3D纪念馆-第45课-使用头像

【WEB前端2024】3D智体编程&#xff1a;乔布斯3D纪念馆-第45课-使用头像 使用dtns.network德塔世界&#xff08;开源的智体世界引擎&#xff09;&#xff0c;策划和设计《乔布斯超大型的开源3D纪念馆》的系列教程。dtns.network是一款主要由JavaScript编写的智体世界引擎&…

zip文件上传到linux服务器文件大小发生变化

在传一个文件到服务器的时候&#xff0c;第一次传完看见大小不一样&#xff08;服务器中du命令查看大小796596MB&#xff09;就重传了一下&#xff0c;还是大小不一样&#xff0c;就查了下。 查了下有以下原因&#xff1a; 文件系统的不同&#xff1a; 原因&#xff1a;不同的…

boost asio异步服务器(3)增加发送队列实现全双工通信

增加发送节点 构造发送节点&#xff0c;管理发送数据。发送节点的类如下。 这个发送节点用于保证发送和接收数据的有效性。 增加发送队列 前边实现的是一个简单的echo服务器&#xff0c;也就是服务器将收到的内容发送给对应的客户端。但是在实际的服务器设计中&#xff0c;服务…

Java共享台球室无人系统支持微信小程序+微信公众号

共享台球室无人系统 &#x1f3b1; 创新台球体验 近年来&#xff0c;共享经济如火如荼&#xff0c;从共享单车到共享汽车&#xff0c;无一不改变着我们的生活方式。而如今&#xff0c;这一模式已经渗透到了更多领域&#xff0c;共享台球室便是其中之一。不同于传统的台球室&a…

驱动开发(三):内核层控制硬件层

驱动开发系列文章&#xff1a; 驱动开发&#xff08;一&#xff09;&#xff1a;驱动代码的基本框架 驱动开发&#xff08;二&#xff09;&#xff1a;创建字符设备驱动 驱动开发&#xff08;三&#xff09;&#xff1a;内核层控制硬件层​​​​​​​ ←本文 目录…