【Day12】登录认证、异常处理

news2024/11/26 0:57:37

1 登录

 先创建一个新的 controller 层:LoginController

@RestController
public class LoginController {
    @Autowired
    private EmpService empService;// 注入

    @PostMapping("/login")
    public Result login(@RequestBody Emp emp) { // 包装对象
        Emp e = empService.login(emp);
        return e != null ? Result.success() : Result.error("error");
    }
}

在 service 层实现

service 层调用 mapper 层,mapper 层操作数据库

测试:

2 登录校验

        刚才的程序有 bug,即只要改一下 url,可以跳过登录直接进入员工管理界面,此时需要校验

2.1 会话技术

会话

        用户打开浏览器,访问 web 服务器的资源,会话建立,直到有一方断开连接,会话结束。在一次会话中可以包含多次请求和响应

会话跟踪

        一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据

会话跟踪方案

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

2.1.1 Cookie

        Cookie 是一种小型数据存储,通常存储在用户浏览器中,可以由服务器在用户访问网站时发送给用户的浏览器,并由浏览器在随后的请求中发送回服务器

2.1.2 Session

        Session 是另一种用于在用户和服务器之间保持状态的技术。与 Cookie 不同,Session 通常不直接存储在用户的浏览器中,而是存储在服务器端

2.1.3 令牌

JWT 令牌(JSON Web Token)

        定义了一种简洁的、自包含 的格式,用于在通信双方以 json 数据格式安全的传输信息。由于数字签名的存在,这些信息是可靠的

自包含:JWT 包含所有必要的信息,不需要访问服务器来验证

JWT 令牌的组成

  1. Header(头),记录令牌类型,签名算法等,例如{"alg":"HS256","type":"JWT"}
  2. Payload(有效载荷),携带一些自定义信息、默认信息等,例如{"id":"1","username":"Tom"}
  3. Signature(签名),防止 Token 被篡改、确保安全性。将 header、payload,并加入指定秘钥,通过指定签名算法计算而来

Header,Payload,Signature 字符串通过 Base64 编码变成 JWT 字符串

原理

  1. 登录成功后,生成令牌
  2. 后续每个请求,都要携带 JWT 令牌,系统在每次请求处理之前,先校验令牌,通过后,再处理

生成 JWT 令牌

1、引入依赖——在 pom.xml 中

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

2、使用 jwts 库下的 jwt.builder() 方法

(在单元测试中写)

public void testJWT() {
        Map<String, Object> claim = new HashMap<>();
        claim.put("id", 1);
        claim.put("name", "wyn");

        String jwt = Jwts.builder()
                .signWith(SignatureAlgorithm.HS256, "mi_yao") // JWT 第一部分 Signature签名
                .setClaims(claim) // JWT 第二部分 Payload载荷
                .setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000)) // 设置令牌有限期为1h
                .compact();// 生成 JWT 令牌(字符串)

        System.out.println(jwt);
        // print
        // eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoid3luIiwiaWQiOjEsImV4cCI6MTcyMTM5NDcwMn0.prh9gbgvW5PTWvogGqkjPE2ofrXz-5FVGkYoDkEaNoI}
    }

 链式编程

.signWith()

        第一个参数是加密算法,第二个参数是密钥

.setClaims()

        设置自定义的信息,参数是 map 类型

.setExpiration()

        设置令牌有效时间

.compact()

        最终生成 JWT 令牌的字符串

(注:每次生成的令牌不一样)

JWT 的解析

@Test
    public void testParseJWT() {
        Map<String, Object> claims = Jwts.parser()
                .setSigningKey("mi_yao") // 指定签名
                // 解析 JWS(JSON Web Signature),返回一个 JWS 对象
                .parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoid3luIiwiaWQiOjEsImV4cCI6MTcyMTM5NDcwMn0.prh9gbgvW5PTWvogGqkjPE2ofrXz-5FVGkYoDkEaNoI")
                .getBody(); // 提取负载部分

        System.out.println(claims); // print {name=wyn, id=1, exp=1721394702}
    }

如果 JWT 输入不正确,就会报错

如果上面设置的 JWT 令牌有效时间过期了,也会报错

2.2 使用 JWT 令牌登录&校验

在 utils 软件包下导入一个工具类 JWTUtils:

package com.wyn.tlias.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import java.util.Map;

public class JwtUtils {

    private static String key = "itheima";
    private static Long expire = 43200000L;

    /**
     * 生成JWT令牌
     * @param claims JWT第二部分负载 payload 中存储的内容
     * @return
     */
    public static String generateJwt(Map<String, Object> claims){
        String jwt = Jwts.builder()
                .addClaims(claims)
                .signWith(SignatureAlgorithm.HS256, key)
                .setExpiration(new Date(System.currentTimeMillis() + expire))
                .compact();
        return jwt;
    }

    /**
     * 解析JWT令牌
     * @param jwt JWT令牌
     * @return JWT第二部分负载 payload 中存储的内容
     */
    public static Claims parseJWT(String jwt){
        Claims claims = Jwts.parser()
                .setSigningKey(key)
                .parseClaimsJws(jwt)
                .getBody();
        return claims;
    }
}

2.2.1 登录

在 LoginControlller 里面直接调用 JWTUtils:

claim 信息自己添加给一个 map,然后作为一个 Result 统一响应结果返回

@RestController
public class LoginController {
    @Autowired
    private EmpService empService;

    /**
     * 登录,示例 username = "jinyong",password = 123456
     * @param emp
     * @return
     */
    @PostMapping("/login")
    public Result login(@RequestBody Emp emp) {
        Emp e = empService.login(emp);

        if (e != null) {
            Map<String, Object> claims = new HashMap<>();
            claims.put("id",emp.getId());
            claims.put("username", emp.getUsername());
            claims.put("name",emp.getName());
//            claims.put("password", emp.getPassword()); // 注意不要封装密码

            String jwt = JwtUtils.generateJwt(claims);
            return Result.success(jwt);
        }
        return Result.error("error");
    }
}

测试,返回了一个 JWT 令牌

2.2.2 校验

        现在拿到令牌,需要进行 统一拦截,如果令牌存在,就可以继续;如果没有,就返回登录页面

        统一拦截两种解决方案:

  • 过滤器 Filter
  • 拦截器 Interceptor
过滤器 Filter

        JavaWeb 三大组件(Servlet、Filter、Listener)之一

        Servlet 是一个运行在服务器端的 Java 小程序,它是 Web 应用程序的基石。Servlet 可以响应客户端的请求(通常是 HTTP 请求),并生成响应

        Filter 是一个在 Servlet 之前或之后执行的程序,用于在请求到达 Servlet 之前或响应发送给客户端之前进行预处理或后处理

        Listener 是一个监听特定事件的对象,它可以在事件发生时接收通知并执行相应的处理逻辑

        过滤器可以把对资源的请求 拦截 下来,从而实现一些特殊的功能

        过滤器一般完成一些通用的操作,比如:登录校验、统一编码处理、敏感字符处理等

使用方法

        1.定义 Filter:定义一个类,实现 Filter 接口,并重写其所有方法(就重写一个就行

        2.配置 Filter:Filter 类上加 @WebFilter 注解,配置拦截资源的路径。引导类上加@ServletcomponentScan 开启 Seflet 组件支持

        在启动类里面加上注解

@ServletComponentScan

        然后启动,发现原来的登录,发送 json 数据,已经没有响应了,被拦截(拦截了没有后续,所以没有响应),仅仅单纯拦截,而没有 放行

放行:

调用方法

filterChain.doFilter(servletRequest,servletResponse);

过滤器链

一个web应用中,可以配置多个过滤器,这多个过滤器就形成了一个过滤器链


校验

测试:

1、访问 /login,获得一个 jwt 令牌

eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjpudWxsLCJpZCI6bnVsbCwidXNlcm5hbWUiOiJqaW55b25nIiwiZXhwIjoxNzIxNTE0NzI3fQ.vS6YA_M-9ptBG01ly0ELjti1QvWzs_tlqeHClhh6o-M

2、访问其他路径时,要 在请求头中携带令牌

如果不带:

携带:

拦截器 Interceptor


        拦截器是一种动态拦截方法调用的机制,类似于过滤器。是 Spring 框架中提供的,用来动态拦截控制器方法的执行
        作用:拦截请求,在指定的方法调用前后,根据业务需要执行预先设定的代码

使用方法

1、定义拦截器,实现 Handlerlnterceptor 接口,并重写其所有方法

注意加上注解 @component


 

2、注册拦截器

流程

登录校验

@Component
public class LoginCheckInterceptor implements HandlerInterceptor {
    @Override // 目标资源方法运行前运行,返回 true 代表放行;返回 false 代表不放行
    // 登录校验在这里
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 注意形参直接是 HttpServletRequest 和 HttpServletResponse,不需要强制转换

        // 获取 url,这段可以不用写,因为在配置文件里规定了不拦截/login
        String url = request.getRequestURL().toString();
        if (url.contains("login")){
            // 登录,放行
            return true;
        }

        // 获取令牌
        String jwt = request.getHeader("token");
        if (!StringUtils.hasLength(jwt)) {
            // 令牌不存在,不放行
            Result error = Result.error("NOT_LOGIN");
            String notLogin = JSONObject.toJSONString(error);
            response.getWriter().write(notLogin);
            return false;
        }

        // 验证令牌
        try {
            JwtUtils.parseJWT(jwt);
        } catch (Exception e) {
            e.printStackTrace();

            // 令牌错误,不放行
            Result error = Result.error("NOT_LOGIN");
            String notLogin = JSONObject.toJSONString(error);
            response.getWriter().write(notLogin);
            return false;
        }
        // 令牌正确,放行
        return true;
    }

    @Override // 目标资源方法运行后运行
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    @Override // 视图渲染完毕后运行,最后运行
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

 

3 异常处理

使用全局异常处理器(GlobalExceptionHandler):

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

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

相关文章

html 单页面引用vue3和element-plus

引入方式&#xff1a; element-plus基于vue3.0&#xff0c;所以必须导入vue3.0的js文件&#xff0c;然后再导入element-plus自身所需的js以及css文件&#xff0c;导入文件有两种方法&#xff1a;外部引用、下载本地使用 通过外部引用ElementPlus的css和js文件 以及Vue3.0文件 …

Golang | Leetcode Golang题解之第260题只出现一次的数字III

题目&#xff1a; 题解&#xff1a; func singleNumber(nums []int) []int {xorSum : 0for _, num : range nums {xorSum ^ num}lsb : xorSum & -xorSumtype1, type2 : 0, 0for _, num : range nums {if num&lsb > 0 {type1 ^ num} else {type2 ^ num}}return []in…

【数据结构】二叉树OJ题_对称二叉树_另一棵的子树

对称二叉树 题目 101. 对称二叉树 - 力扣&#xff08;LeetCode&#xff09; 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 示例 1&#xff1a; 输入&#xff1a;root [1,2,2,3,4,4,3] 输出&#xff1a;true示例 2&#xff1a; 输入&#xff1a;root [1,2…

不同类型的指针变量进行++操作的效果

可以看到 不同变量的指针进行操作的时候&#xff0c;他的地址移动的大小是不一样的 运行了打印了一些东西 &#xff0c; 没想到可以用sizeof来打印出 names[0][]这个字符串的长度方法 &#xff0c; 只能用这个 strlen1来判断这个字符串的长度。

使用minio cllient(mc)完成不同服务器的minio的数据迁移和mc基本操作

minio client 前言使用1.拉取minio client 镜像2.部署mc容器3.添加云存储服务器4.迁移数据1.全量迁移2.只迁移某个桶3.覆盖重名文件 5.其他操作1.列出所有alias、列出列出桶中的文件和目录1.1.列出所有alias1.2.列出桶中的文件和目录 2.创建桶、删除桶2.1.创建桶2.2.删除桶 3.删…

DX-10A信号继电器 柜内安装,板前接线 约瑟JOSEF

DX-10型闪光信号继电器型号&#xff1a; DX-10A闪光信号继电器&#xff1b; DX-10B闪光信号继电器&#xff1b; DX-10C闪光信号继电器; 用途 DX-10 闪光继电器用于电力系统断路器的位置信号灯不对应闪光&#xff0c;该继电器是为了适应当前推广使用发光二极管节能指示灯而…

“狂飙”过后,大模型未来在何方?

2024年6月14日&#xff0c;第六届“北京智源大会”在中关村展示中心开幕。 开幕现场&#xff0c;智源研究院、OpenAI、百度、零一万物、百川智能、智谱AI、面壁智能等国内主流大模型公司CEO与CTO&#xff0c;人工智能顶尖学者和产业专家&#xff0c;在围绕人工智能关键技术路径…

rockchip的yolov5 rknn python推理分析

rockchip的yolov5 rknn推理分析 对于rockchip给出的这个yolov5后处理代码的分析&#xff0c;本人能力十分有限&#xff0c;可能有的地方描述的很不好&#xff0c;欢迎大家和我一起讨论&#xff0c;指出我的错误&#xff01;&#xff01;&#xff01; RKNN模型输出 将官方的Y…

GD 32 环形队列

1.0 为什么要使用环形队列 在代码中使用环形队列进行程序的编写&#xff0c;由于在实际开发过程中&#xff0c;会出现接收数据频率太快快于主流程读取数据的频率&#xff0c;这个时候后面来的数据会覆盖前面一包数据&#xff0c;这个时候可以使用环形队列的方式解决这个问题。 …

离散数学,格与子格,格的性质,格的代数系统定义,格的同态与同构,特殊格

目录 1.格与子格 相互对偶 2.格的性质 对偶式 格的保序性 3.格的代数系统定义 格对应的偏序关系就是s的子集之间的包含关系 该格对应的偏序关系就是整除关系 子格必然是格 4.格的同态与同构 格同态&#xff0c;序同态 同态是保序的 例子 5.特殊格 全下…

明星应援系统小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;线上应援管理&#xff0c;线下应援管理&#xff0c;应援物品管理&#xff0c;购买订单管理&#xff0c;集资应援管理&#xff0c;集资订单管理&#xff0c;市集订单管理&#xff0…

CentOS部署MySQL

1.配置yum仓库 #更新秘钥 rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2023 #安装MySQL rpm -Uvh http://dev.mysql.com/get/mysql80-community-release-el7-2.noarch.rpm 2.使用yum安装MySQL yum -y install mysql-community-server 3.启动MySQL并配置开机自启…

PCB系统学习(1)--PCB印制电路板

PCB印制电路板 1.1PCB的定义1.2PCB的层叠结构1.2.1PCB单层板1.2.2PCB双层板1.2.3PCB四层板 1.3PCB的通孔&#xff0c;盲孔&#xff0c;埋孔1.4元器件的符号与封装1.5PCB的生产过程 1.1PCB的定义 PCB(PrintedCircuitBoard)&#xff0c;中文即印制电路板&#xff0c;或印刷线路板…

C语言八皇后问题可视化界面

插件使用easyx 以下是部分代码。需要源码的私信 #include<stdio.h> #include<easyx.h> #define width 1100//设置窗口的宽度和高度 #define height 900 int place[8] { 0 };//皇后位置 int flag[8] { 1,1,1,1,1,1,1,1 };//定义列 int d1[15] { 1,1,1,1,1,1,1,…

【Node.js基础03】利用http模块创建Web服务

一&#xff1a;使用步骤 1 加载http模块&#xff0c;并创建Web服务程序 2 利用Web服务程序监听request事件&#xff0c;设置响应头和响应体 3 配置端口号并启动Web服务 4 浏览器请求设置的端口号&#xff0c;进行Web服务程序测试 二&#xff1a;简单应用 const http requir…

HarmonyOS Web组件(二)

1. HarmonyOS Web组件 官方文档 1.1. 混合开发的背景和好处 混合开发&#xff08;Hybrid Development&#xff09;是一种结合原生应用和Web应用的开发模式&#xff0c;旨在同时利用两者的优势。随着移动应用需求的多样化和复杂化&#xff0c;单一的开发方式往往难以满足所有…

qt 在线安装包下载

1.获取在线安装包直接运行安装即可&#xff0c;需要注册或登录会员 如下是官网地址&#xff0c;可以直接注册下线最新版。 官网&#xff1a;https://www.qt.io/ 官网中文&#xff1a;https://www.qt.io/zh-cn/product/qt-for-application-development 下载界面如下&#xff…

netcat 使用

GPT-4o (OpenAI) Netcat (通常缩写为nc) 是一个功能强大的网络工具&#xff0c;可以方便地读写网络连接。它被广泛用于漏洞测试、网络调试和数据传输。Netcat 可以作为客户端&#xff0c;也可以作为服务器使用。 以下是一些常见的 Netcat 用法&#xff1a;基础用法 连接到服务…

Linux系统之部署扫雷小游戏(三)

Linux系统之部署扫雷小游戏(三) 一、小游戏介绍1.1 小游戏简介1.2 项目预览二、本次实践介绍2.1 本地环境规划2.2 本次实践介绍三、检查本地环境3.1 检查系统版本3.2 检查系统内核版本3.3 检查软件源四、安装Apache24.1 安装Apache2软件4.2 启动apache2服务4.3 查看apache2服…

卷积神经网络学习问题总结

问题一&#xff1a; 深度学习中的损失函数和应用场景 回归任务&#xff1a; 均方误差函数&#xff08;MSE&#xff09;适用于回归任务&#xff0c;如预测房价、预测股票价格等。 import torch.nn as nn loss_fn nn.MSELoss() 分类任务&#xff1a; 交叉熵损失函数&…