Spring项目:文字花园(四)
一.实现登录
集群环境下⽆法直接使⽤Session.原因分析: 我们开发的项⽬, 在企业中很少会部署在⼀台机器上, 容易发⽣单点故障. (单点故障: ⼀旦这台服务器挂了, 整个应⽤都没法访问了). 所以通常情况下, ⼀个Web应⽤会部署在多个服务器上, 通过Nginx等进⾏负载均衡. 此时, 来⾃⼀个⽤⼾的请求就会被分发到不同的服务器上.

接下来我们介绍第三种⽅案: 令牌技术
1.1JWT令牌

{"userId":"123","userName":"zhangsan"} , 也可以存在jwt提供的现场字段, ⽐如exp(过期时间戳)等. 此部分不建议存放敏感信息, 因为此部分可以解码还原原始内容.
防⽌被篡改, ⽽不是防⽌被解析.JWT之所以安全, 就是因为最后的签名. jwt当中任何⼀个字符被篡改, 整个令牌都会校验失败.就好⽐我们的⾝份证, 之所以能标识⼀个⼈的⾝份, 是因为他不能被篡改, ⽽不是因为内容加密.(任何⼈都可以看到⾝份证的信息, jwt 也是)

JWT令牌⽣成和校验。
1.2项目导入令牌
1.在pom文件,引入依赖
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-api --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api</artifactId><version>0.11.5</version></dependency><!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-impl --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-impl</artifactId><version>0.11.5</version><scope>runtime</scope></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is preferred --><version>0.11.5</version><scope>runtime</scope></dependency>
2.生成令牌(utils/JwsUtils)
代码:
package com.example.demo.utils;import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;import java.security.Key;
import java.util.Date;
import java.util.Map;@Slf4j
public class JwtUtils {private static final long JWT_EXPIRATION = 60 * 60 * 1000;private static final String secretStr = "Le++o8NQWVXWo3+SJtAtnjBW9iA0OvPL0c0mMrol2fU=";private static final Key key = Keys.hmacShaKeyFor(Decoders.BASE64.decode(secretStr));/*** 生成token*/public static String genJwtToken(Map<String, Object> claim) {String token = Jwts.builder().setClaims(claim).setExpiration(new Date(System.currentTimeMillis() + JWT_EXPIRATION)).signWith(key).compact();return token;}/*** 校验token* Claims 为 null 表示校验失败*/public static Claims parseToken(String token) {JwtParser build = Jwts.parserBuilder().setSigningKey(key).build();Claims claims = null;try {claims = build.parseClaimsJws(token).getBody();} catch (Exception e) {log.error("解析token失败, token:{}", token);return null;}return claims;}
}
3.编写后端接口(User Controller)
package com.example.demo.controller;import com.example.demo.model.Result;
import com.example.demo.model.UserInfo;
import com.example.demo.service.UserService;
import com.example.demo.utils.JwtUtils;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.authenticator.Constants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.HashMap;
import java.util.Map;@Slf4j
@RequestMapping("/user")
@RestController
public class UserController {@Autowiredprivate UserService userService;@RequestMapping("/login")public Result login(String username, String password){log.info("UserController#login接收参数: username:{}, password:{}", username, password);//1. 参数校验合法性//2. 校验密码是否正确//3. 密码正确, 返回token//4. 密码错误, 返回错误信息if (!StringUtils.hasLength(username) || !StringUtils.hasLength(password)){return Result.fail("账号或密码不能为空");}//从数据库中查找用户UserInfo userInfo = userService.selectByName(username);//用户不存在if (userInfo==null){log.error("用户不存在");return Result.fail("用户不存在");}//密码错误if (!password.equals(userInfo.getPassword())){log.error("密码错误");return Result.fail("密码错误");}//密码正确, 返回tokenMap<String,Object> claim = new HashMap<>();claim.put("id", userInfo.getId());claim.put("userName", userInfo.getUserName());String token = JwtUtils.genJwtToken(claim);log.info("UserController#login 返回结果token:{}",token);return Result.success(token);}/*** 获取当前登录用户的信息* @return*/// @RequestMapping("/getAuthorInfo")
// public UserInfo getAuthorInfo(Integer blogId){
//
// }
}
service层:
package com.example.demo.service;import com.example.demo.mapper.UserInfoMapper;
import com.example.demo.model.UserInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class UserService {@Autowiredprivate UserInfoMapper userInfoMapper;public UserInfo selectByName(String userName) {return userInfoMapper.selectByName(userName);}public UserInfo selectById(Integer userId) {return userInfoMapper.selectById(userId);}
}
mapper层:
package com.example.demo.mapper;import com.example.demo.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;@Mapper
public interface UserInfoMapper {/*** 根据用户名, 查询用户信息* @return*/@Select("select * from user where delete_flag =0 and user_name = #{userName}")UserInfo selectByName(String userName);/*** 根据用户ID, 查询用户信息* @return*/@Select("select * from user where delete_flag =0 and id = #{id}")UserInfo selectById(Integer id);
}
4.编写前端代码进行交互:
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>博客登陆页</title><link rel="stylesheet" href="css/common.css"><link rel="stylesheet" href="css/login.css"></head><body><div class="nav"><img src="pic/rose.jpg" alt=""><span class="blog-title">文字花园</span><div class="space"></div><a class="nav-span" href="blog_list.html">主页</a><a class="nav-span" href="blog_edit.html">写博客</a></div><div class="container-login"><div class="login-dialog"><h3>登陆</h3><div class="row"><span>用户名</span><input type="text" name="username" id="username"></div><div class="row"><span>密码</span><input type="password" name="password" id="password"></div><div class="row"><button id="submit" onclick="login()">提交</button></div></div></div><script src="js/jquery.min.js"></script><script>function login() {$.ajax({type:"post",url: "/user/login",data:{username:$("#username").val(),password: $("#password").val()},success:function(result){if(result.code=="SUCCESS" && result.data !=null){//后端处理成功localStorage.setItem("user_token", result.data);//将令牌存储到本地//页面跳转location.href = "blog_list.html";}else{alert(result.errMsg);}}});}</script>
</body></html>
展示成功 .
二.实现强制登录
1.1编写拦截器
新添类:interceptor/LoginInterceptor
代码:
package com.example.demo.interceptor;import com.example.demo.utils.JwtUtils;
import io.jsonwebtoken.Claims;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.authenticator.Constants;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//1. 获取token//2. 校验token, 判断是否放行String token = request.getHeader("user_token_header");log.info("从header中获取token:{}", token);Claims claims = JwtUtils.parseToken(token);if (claims==null){//校验失败response.setStatus(401);return false;}return true;}
}
1.2应用到项目
1.新添类:config/webconfig
package com.example.demo.config;import com.example.demo.interceptor.LoginInterceptor;
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;import java.util.Arrays;
import java.util.List;@Configuration
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate LoginInterceptor loginInterceptor;private final List excludes = Arrays.asList("/**/*.html","/blog-editormd/**","/css/**","/js/**","/pic/**","/user/login");@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginInterceptor).addPathPatterns("/**").excludePathPatterns(excludes);}
}
2. 编写前端代码(blog_list.html):
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>博客列表页</title><link rel="stylesheet" href="css/common.css"><link rel="stylesheet" href="css/list.css"></head>
<body><div class="nav"><img src="pic/rose.jpg" alt=""><span class="blog-title">文字花园</span><div class="space"></div><a class="nav-span" href="blog_list.html">主页</a><a class="nav-span" href="blog_edit.html">写博客</a><a class="nav-span" href="#" onclick="logout()">注销</a></div><div class="container"><div class="left"><div class="card"><img src="pic/doge.jpg" alt=""><h3>小李同学</h3><a href="#">GitHub 地址</a><div class="row"><span>文章</span><span>分类</span></div><div class="row"><span>2</span><span>1</span></div></div></div><div class="right">sbnbjnmssssssd</div></div><script src="js/jquery.min.js"></script><script src="js/common.js"></script><script>$.ajax({type: "get",url: "/blog/getList",success: function(result){if(result.code=="SUCCESS"){var blogs = result.data;var finalHtml = "";for(var blog of blogs){finalHtml +='<div class="blog">';finalHtml +='<div class="title">'+blog.title+'</div>';finalHtml +='<div class="date">'+blog.createTime+'</div>';finalHtml +='<div class="desc">'+blog.content+'</div>';finalHtml +='<a class="detail" href="blog_detail.html?blogId='+blog.id+'">查看全文>></a>';finalHtml +='</div>';console.log(finalHtml);}$(".container .right").html(finalHtml);}}, error:function(error){if(error!=null && error.status==401){location.href = "blog_login.html";}}});</script></body>
</html>
3.定义全局 AJAX 事件处理器,它在每个 AJAX 请求发送之前被触发。
4.展示
相关文章:

Spring项目:文字花园(四)
一.实现登录 传统思路: • 登陆⻚⾯把⽤⼾名密码提交给服务器. • 服务器端验证⽤⼾名密码是否正确, 并返回校验结果给后端 • 如果密码正确, 则在服务器端创建 Session . 通过 Cookie 把 sessionId 返回给浏览器. 问题: 集群环境下⽆法直接使⽤Session. 原因分析: 我们开…...

Web开发:ORM框架之Freesql的入门和技巧使用小结
目录 零、官网链接 一、字段映射表 二、查询 1.freesql独特封装:between关键字 2.分页(每页 20 条数据,查询第 1 页) 3.Withsql(子查询,不建议) 3.简单查询、映射查询 4.参数查询、自定义…...

软件工程(4)面向对象方法:面向对象软件工程OOSE与案例实践
OOSE(Object-Oriented Software Engineering,面向对象软件工程)是一种用于开发软件系统的工程方法论,它强调使用面向对象的技术和方法来设计和实现软件。OOSE 方法是由 Ivar Jacobson 提出的,主要包括以下几个关键方面…...

【数据结构篇】~链表算法题1(含快慢指针的解析)
前言 关于环形指针与快慢指针是算法题中的常客,如果能掌握将是我们的一大助力! 1.快慢指针 1 移除链表元素 https://leetcode.cn/problems/remove-linked-list-elements/description/ 1)思路 这道题可以用一个新链表来保存原链表中不…...

洛谷 P1135 奇怪的电梯
链接直达:P1135 奇怪的电梯 - 洛谷 | 计算机科学教育新生态 题目来源 洛谷 题目内容 奇怪的电梯 题目背景 感谢 yummy 提供的一些数据。 题目描述 呵呵,有一天我做了一个梦,梦见了一种很奇怪的电梯。大楼的每一层楼都可以停电梯&…...

vue使用axios请求后端数据
前后端分离项目的基础: 前后端跨域访问 vite.config.js中加入 // 1.为什么要跨域 //因为浏览器的同源策略,不同站点之间访问需要跨域 //实现跨域的方式:server: {proxy: {// 假设要跨域访问的后端 API 地址以 /api 开头/api: { //表示拦截以/api开头的…...

目标检测 | yolov10 原理和介绍
相关系列: 目标检测 | yolov1 原理和介绍 目标检测 | yolov2/yolo9000 原理和介绍 目标检测 | yolov3 原理和介绍 目标检测 | yolov4 原理和介绍 目标检测 | yolov5 原理和介绍 目标检测 | yolov6 原理和介绍 目标检测 | yolov7 原理和介绍 目标检测 | yolov8 原理和…...

基于Springboot 和Vue 的高校宿舍管理系统源码
网络上很多宿舍管理系统都不完整,大多数缺少数据库文件,所在使用极其不方便,由于本人程序员,根据代码,自己花时间不全了数据库文件,并且可以完美运行!!!!&…...

3:2比例的程序员专业显示器,效率提升显著,摸鱼时间又多了
对于我们程序员来说,显示器的重要性不言而喻,作为我们与代码交流的直接工具,他影响着我们的工作效率、舒适度和整体编程体验。我在家用的是自己笔记本的屏幕,简单写写代码还行,涉及到多任务协同或者大代码量开发就有点…...

vue3 cascader省市区三级联动如何指定字段,如何根据id查到对应的名字
如果我们接口数据字段名不是value和code。要加个props :props"{ value:code,label:regionName}"根据id查name需要一个ref和一个change事件<el-cascader :options"areaData" ref"addressCodeRef" change"handleChange" :props"…...

算法4:前缀和(上)
文章目录 一维前缀和二维前缀和寻找数组的中心下标除自身以外数组的乘积 一维前缀和 二维前缀和 寻找数组的中心下标 class Solution { public:int pivotIndex(vector<int>& nums) {int n nums.size();vector<int> f(n), g(n);f[0] nums[0];g[n - 1] num…...

美国政府紧急应对三星Galaxy手机安全漏洞
一、美国政府紧急通知更新三星Galaxy手机系统 美国政府近日发布紧急通知,要求联邦政府雇员在8月28日前更新三星Galaxy手机系统,否则将面临禁止使用这些设备的后果。这是继7月针对Pixel手机用户的类似要求之后的又一次紧急行动。此次事件的导火索是谷歌发…...

看 逆行人生
电影和我的职业本身有相关性,而且我特别喜欢徐峥执导的电影,这次的题材也算是碰上自己的胃口。 周六,下了大半天的雨,早上驱车到公司加班,下午六点多到时候特别想去看电影,果断再驱车从公司赶回来ÿ…...

0819、0820梳理及一些面试题梳理
一、抓包分析 二、HTTP服务器 三、动态库与静态库 四、一些面试题 指针数组和数组指针的区别:指针数组本质是一个数组,只是数组中存储的是指针变量。数组指针存储的是该数组的起始地址,对该指针来说每偏移一个单位就是偏移了一整个数组的地…...

HttpUtils工具类(一)常见的HttpUtils工具类及如何自定义java的http连接池
目录 一、几种常见的Http调用方式 1. 使用 Apache HttpClient 2. 使用 OKhttpClient 3. 使用第三方库(Hutool)的http链接池 4. 使用 Spring RestTemplate 5. 使用 Java 原生的HttpURLConnection 二、总结 常用三种HttpUtils对比总结 一、几种常见…...

使用 Lombok 遇到一个问题
起因是换了一个电脑,重新从服务器上拉了一个项目。项目是由maven构建的,在控制台中使用mvn命令编译项目时,没有任何问题,编译成功。如下图: 可是idea里面的源码,却标红了,如下: 错误…...

Linux基础环境开发工具gcc/g++ make/Makefile
1.Linux编译器-gcc/g使用 1. 预处理(进行宏替换) 预处理功能主要包括宏定义,文件包含,条件编译,去注释等。 预处理指令是以#号开头的代码行。 实例: gcc –E hello.c –o hello.i 选项“-E”,该选项的作用是让 gcc 在预处理结束后停止编译过程。 选项“-o”是指目标…...

ES 模糊查询 wildcard 的替代方案探索
一、Wildcard 概述 Wildcard 是一种支持通配符的模糊检索方式。在 Elasticsearch 中,它使用星号 * 代表零个或多个字符,问号 ? 代表单个字符。 其使用方式多样,例如可以通过 {"wildcard": {"field_name": "value&…...

Linux安装MQTT 服务器(图文教程)
MQTT(Message Queuing Telemetry Transport)是一种轻量级的消息传输协议,专为低带宽和不稳定的网络环境设计,非常适合物联网(IoT)应用。 官网地址:https://www.emqx.com/ 一、版本选择 根据自己…...

【TCP】核心机制:延时应答、捎带应答和面向字节流
文章目录 延时应答捎带应答面向字节流粘包问题方案一:指定分隔符方案二:指定数据的长度 TCP 报头首部长度保留(6 位)选项序号确认序号 延时应答 尽可能降低可靠传输带来的性能影响 提升性能>让滑动窗口变大 如果我们立即返回 …...

题解:AT_abc352_e [ABC352E] Clique Connect
[题目通道]([ABC352E] Clique Connect - 洛谷) 鄙人今日写人生第一篇题解 希望管理大大通过 首先,我们先看题: 它说一共有n个点,m回操作。。。 每次操作 都有 一个Ki 和 Ci Ki代表有Ki个点,Ci代表每条边所赋的边权 一看就知道这是个最小生成树的板子…...

【代码随想录训练营第42期 Day32打卡 - 从零开始动态规划 - LeetCode 509. 斐波那契数 70. 爬楼梯 746. 使用最小花费爬楼梯
目录 一、做题心得 二、动规五步走 三、题目与题解 题目一:509. 斐波那契数 题目链接 题解1:记忆性递归 题解2:动态规划 题目二:70. 爬楼梯 题目链接 题解:动态规划 题目三:746. 使用最小花费爬楼…...

源码构建LAMP
目录 一、安装Apache 二、安装Mysql 三、安装PHP 四、安装论坛 一、安装Apache 1.cd 到opt目录下面,将压缩包拉进Xhell 2.解压缩apr和httpd压缩包 tar xf apr-1.6.2.tar.gz tar xf apr-util-1.6.0.tar.gz tar xf httpd-2.4.29.tar.bz2 3.将apr-1.6.2 移动到ht…...

Java:封装树结构
实体类 public class DictTreeselectVO {private String value;private String label;/*** 节点*/private String parentId;private List<DictTreeselectVO> children new ArrayList<DictTreeselectVO>();public String getValue() {return value;}public void s…...

linux内核 pintrl子系统
1、什么是pinctrl子系统 在 Linux 内核中,pinctrl子系统是一个专门用于管理和控制 SoC引脚复用和配置的子系统。SoC 通常具有大量的引脚(pin),这些引脚可以被配置为不同的功能,比如 GPIO(通用输入输出&…...

网络通信要素
网络介绍 定义:将具有独立功能的多台计算机通过通信线路和通信设备连接起来,在网络管理软件及网络通信协议下,实现资源共享和信息传递的虚拟平台。 学习网络的目的: 能够编写基于网络通信的软件或程序,通常来说就是网…...

day03_作业
一、简答题 继承的格式与好处 格式:class A extends B 好处:1.可以实现代码的复用,将共性的代码向上抽取,抽取到父类中。需要使用这些属性和行为的类,通过继承即可使用。2.当需要添加新的功能时,可以通过…...

pyinstaller程序打包,资源嵌入exe
参考:https://blog.csdn.net/qq_48979387/article/details/132359366 一、参数说明 -F 最终打包为一个可执行文件。-w 取消Windows显示窗口-add-data ‘dll;dll’,将当前目录dll下的文件打包到可执行文件的dll中,最终会在解压文件的dll文件…...

如何使用 OCR 和 GPT-4o mini 轻松提取收据信息
利用 OCR 和强大的 GPT-4o 迷你模型对收据进行信息提取 利用 OCR 和强大的 GPT-4o 迷你模型对收据进行信息提取 欢迎来到雲闪世界。,我将向您展示如何从收据中提取信息,并提供收据的简单图像。首先,我们将利用 OCR 从收据中提取信息。然后&a…...

go 事务
事务处理 首先启动事务时一定要做错误判断建议在启动事务之后马上写defer方法在defer方法内对err进行判断,如果全局中有err!nil就回滚全局中err都为nil则提交事务在提交事务之后我们可以定义一个钩子函数afterCommit,来统一处理事务提交后的逻辑。 示例…...