Javaweb登录校验

news2024/11/23 15:17:18

登录校验

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令牌
     */
    @Test
    public 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令牌
     */
    @Test
    public 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 {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("demoFilter init");
        Filter.super.init(filterConfig);
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        // 获取URL
        String url = request.getRequestURI().toString();
        if (url.contains("login")) {
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        }

        // 获取token
        String 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);
    }

    @Override
    public 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 {
    @Autowired
    private LoginCheckInterceptor loginCheckInterceptor;

    @Override
    public 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 {
        // 获取token
        String 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.coloradmin.cn/o/1827127.html

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

相关文章

如何用AI提高产品经理的工作效率

最近我跟几个产品经理聊天&#xff0c;发现有些人居然还没有使用过ChatGPT、MidJourney、NotionAI 等AI工具。 产品经理有个重要的素质是好奇心&#xff0c;好奇心能够帮助产品经理发现新机会、了解用户需求、学习新知识和探索竞争对手&#xff0c;从而更好地完成产品开发和管…

mediamtx流媒体服务器测试

MediaMTX简介 在web页面中直接播放rtsp视频流&#xff0c;重点推荐&#xff1a;mediamtx&#xff0c;不仅仅是rtsp-CSDN博客 mediamtx github MediaMTX(以前的rtsp-simple-server)是一个现成的和零依赖的实时媒体服务器和媒体代理&#xff0c;允许发布&#xff0c;读取&…

android13 应用冷启动

1 概述 launcher 通过binder到systemserver中atms中发送startActivity请求 startProcess向zygote发送启动新进程请求 zygote收到请求&#xff0c;fork新进程并调用ActivityThread的main初始化 新进程启动&#xff0c;发送attachApplication给ams&#xff0c;告诉他新进程启动…

C++ 44 之 指针运算符的重载

#include <iostream> #include <string> using namespace std;class Students04{ public:int m_age;Students04(int age){this->m_age age;}void showAge(){cout << "年龄是&#xff1a; " << this->m_age << endl;}~Students0…

155. 最小栈 力扣 python 空间换时间 o(1) 腾讯面试题

设计一个支持 push &#xff0c;pop &#xff0c;top 操作&#xff0c;并能在常数时间内检索到最小元素的栈。 实现 MinStack 类: MinStack() 初始化堆栈对象。void push(int val) 将元素val推入堆栈。void pop() 删除堆栈顶部的元素。int top() 获取堆栈顶部的元素。int get…

RestTemplate远程请求的艺术

1 简说 编程是一门艺术,追求优雅的代码就像追求优美的音乐。 很多有多年工作经验的开发者,在使用RestTemplate之前常常使用HttpClient,然而接触了RestTemplate之后,却愿意放弃多年相处的“老朋友”,转向RestTemplate。那么一定是RestTemplate有它的魅力,有它的艺术风范。…

CCAA质量管理【学习笔记】​​ 备考知识点笔记(三)质量管理方法与常见工具

第二部分 质量管理领域专业知识 《质量管理体系基础考试大纲》中规定的考试内容&#xff1a; 3.2 质量管理领域专业知识 a) 了解质量管理方法与工具相关知识&#xff0c;包括&#xff1a; 质量管理方法与工具的内涵与作用、发展历程与应用现状、分类与选择常用的应用软件…

使用 Vue CLI 脚手架生成 Vue 项目

最近我参与了一个前端Vue2的项目。尽管之前也有过参与Vue2项目的经验&#xff0c;但对一些前端Web技术并不十分熟悉。这次在项目中遇到了很多问题&#xff0c;所以我决定借此机会深入学习Vue相关的技术栈。然而&#xff0c;直接开始深入钻研这些技术可能会显得枯燥&#xff0c;…

随笔-来了,安了

依照领导定的规矩&#xff0c;周五又去了分公司&#xff0c;赋能一线去了。到了地方就是开会->现场解决问题->干饭->开会过需求、提供解决方案&#xff0c;充实得厉害。强度也不小&#xff0c;中午干的一大碗饭&#xff0c;到五点就饿了。 六点带着分公司催着上线的需…

jupyter notebook中使用不同的anaconda环境及常用conda命令

conda命令 在jupyter notebook中使用不同的anaconda环境其他常用conda命令 在jupyter notebook中使用不同的anaconda环境 创建环境 myenvname 需替换为自己的环境名称 conda create --name myenvname python3.7激活环境 conda activate myenvname 在该环境中安装Jupyter N…

MongoDB~事务了解;可调一致性模型功能与因果一致性模型功能分析

背景 MongoDB 从 3.0版本引入 WiredTiger 存储引擎之后开始支持事务&#xff0c;MongoDB 3.6之前的版本只能支持单文档的事务&#xff0c;从 MongoDB 4.0版本开始支持复制集部署模式下的事务&#xff0c;从 MongoDB 4.2版本开始支持分片集群中的事务。 根据官方文档介绍&…

C++11 move左值转化为右值

单纯的左值只能用左值引用和右值只能用右值引用有些局限&#xff0c;在一些情况下&#xff0c;我们也需要对左值去调用右值引用&#xff0c;从而实现将左值里的内容转移到右值中 标准定义&#xff1a; 功能就是将一个左值强制转化为右值&#xff0c;然后实现移动语义 注意&…

IP地址、子网掩码、网段、网关

前面相同就是在同一个网段 如果子网掩码和网络号相与的结果是一样的&#xff0c;那么他们就在同一个子网 IP地址、子网掩码、网络号、主机号、网络地址、主机地址以及ip段/数字-如192.168.0.1/24是什么意思?_掩码248可以用几个ip-CSDN博客

CTFshow之RCE代码命令远程执行第29关到第40关详细讲解。一定教会

不在沉默中崛起&#xff0c;便在沉默中变态 --莫迪大仙 引言&#xff1a;最近学习没有头绪&#xff0c;想着把ctf随便刷一刷&#xff0c;过过瘾&#xff0c;试着看能不能每条10到15道题目&#xff0c;并且写下相关的学习记录&#xff0c;持续十天&#xff01;今天是RCE板块29关…

解决 kali 中使用 vulhub 拉取不到镜像问题

由于默认情况下&#xff0c;访问的镜像是国外的&#xff0c;而从 2023 年开始&#xff0c;docker 的镜像网站就一直访问不了&#xff0c;所以我们可以把镜像地址改成国内的阿里云镜像地址。 1、在 cd /etc/docker/目录下创建或修改daemon.json文件 sudo touch daemon.json 2、在…

统计信号处理基础 习题解答10-13

题目&#xff1a; 利用矩阵求逆引理&#xff0c;证明&#xff08;10.32&#xff09;和&#xff08;10.33&#xff09;。提示&#xff1a;首先证明&#xff08;10.33&#xff09;然后利用它来证明&#xff08;10.32&#xff09; 解答&#xff1a; 由&#xff08;10.28&#xf…

【TypeScript】类型兼容(协变、逆变和双向协变)

跟着小满zs 学习 ts&#xff0c;原文&#xff1a;学习TypeScript进阶类型兼容_typescript进阶阶段类型兼容 小满-CSDN博客 类型兼容&#xff0c;就是用于确定一个类型是否能赋值给其他的类型。如果A要兼容B 那么A至少具有B相同的属性。 // 主类型 interface A {name: string,a…

LVS-DR模式详解:提升网站性能的最佳解决方案

LVS-DR模式原理 用户请求到达Director Server&#xff1a; 用户请求到达Director Server&#xff08;负载均衡服务器&#xff09;&#xff0c;数据包首先到达内核空间的PREROUTING链。数据包源IP&#xff1a;CIP&#xff0c;目标IP&#xff1a;VIP&#xff0c;源MAC&#xff1a…

JProfiler 性能分析案列——基于 dump.hprof 堆内存快照文件分析排查内存溢出

在 windows 环境下实现。 一、配置 JVM 参数 配置两个 JVM 参数&#xff1a; -XX:HeapDumpOnOutOfMemoryError&#xff0c;配置这个参数&#xff0c;会在发生内存溢出时 dump 生成内存快照文件&#xff08;xxx.hprof&#xff09;-XX:HeapDumpPathF:\logs&#xff0c;指定生成…

LVS负载均衡:理解IPVS和IPVSADM的内部工作原理

LVS 负载均衡工作模式 LVS&#xff08;Linux Virtual Server&#xff09; 共有三种工作模式&#xff1a;DR、Tunnel、NAT。 DR&#xff08;Direct Routing&#xff09;&#xff1a; 技术原理&#xff1a;DR模式下&#xff0c;LVS调度器接收到请求后&#xff0c;直接通过MAC地址…