当前位置: 首页 > news >正文

登录相关功能的优化【JWT令牌+拦截器+跨域】

登录相关功能的优化

  1. 登录后显示当前登录用户
  2. el-dropdown: Element - The world's most popular Vue UI framework
<el-dropdown style="float: right; height: 60px; line-height: 60px"><span class="el-dropdown-link" style="color: white; font-size: 16px">{{ user.name }}<i class="el-icon-arrow-down el-icon--right"></i></span><el-dropdown-menu slot="dropdown"><el-dropdown-item><div @click="logout">退出登录</div></el-dropdown-item></el-dropdown-menu>
</el-dropdown>
  1. 登录成功后,将登录的用户信息存储到前端的localStorage里
localStorage.setItem("user", JSON.stringify(res.data));
  1. 登录成功后,从localStorage里获取当前的登录用户
data () {return {user: localStorage.getItem("user") ? JSON.parse(localStorage.getItem("user")) : {},}
},
  1. 退出登录后,清localStorage,跳到登录页
methods: {logout() {localStorage.removeItem("user");this.$router.push("/login");}
}

这样安全吗??
肯定不安全,用户可以跳过登录,直接在浏览器上输入后台的路由地址,即可直接进入系统,访问敏感数据。

前端路由守卫

在路由配置文件index.js里,配上路由守卫

// 路由守卫
router.beforeEach((to ,from, next) => {if (to.path ==='/login') {next();}const user = localStorage.getItem("user");if (!user && to.path !== '/login') {return next("/login");}next();
})

这样就安全了吗??
还是不安全,因为前端的数据是不安全的,是可以认为篡改的!就是说,鉴权放在前端,是不安全的。我们的登录鉴权肯定是要放在服务端来完成。

使用jwt在后端进行鉴权

在用户登录后,后台给前台发送一个凭证(token),前台请求的时候需要带上这个凭证(token),才可以访问接口,如果没有凭证或者凭证跟后台创建的不一致,则说明该用户不合法。

  1. pom.xml
<dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.10.3</version>
</dependency>
<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.3.7</version>
</dependency>
  1. 给后台接口加上统一的前缀/api,然后我们统一拦截该前缀开头的接口,所以配置一个拦截器(这个可有可无,看自己意愿)
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
// 使用@Configuration注解标记该类为配置类,用于替代xml配置文件
public class WebConfig implements  WebMvcConfigurer {@Override// 实现configurePathMatch方法,用于配置路径匹配策略public void configurePathMatch(PathMatchConfigurer configurer) {// 指定controller统一的接口前缀// 通过addPathPrefix方法为所有带有RestController注解的控制器添加"/api"前缀configurer.addPathPrefix("/api", clazz -> clazz.isAnnotationPresent(RestController.class));}
}

request封装里面,baseUrl也需要加个 /api 前缀

  1. Jwt配置

JwtTokenUtils.java
jwt的规则


import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.example.entity.Admin;
import com.example.service.AdminService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;@Component
public class JwtTokenUtils {private static AdminService staticAdminService;private static final Logger log = LoggerFactory.getLogger(JwtTokenUtils.class);@Resourceprivate AdminService adminService;@PostConstructpublic void setUserService() {staticAdminService = adminService;}/*** 生成token*//*** 生成JWT令牌* * @param adminId 管理员ID,将被保存到令牌的载荷中* @param sign 用于生成令牌的签名密钥* @return 生成的JWT令牌字符串* * 此方法使用JWT库创建一个带有特定载荷和过期时间的令牌,并使用指定的签名密钥进行签名* 载荷中包含管理员ID,用于标识令牌的受众* 令牌将在创建后2小时过期,过期时间从当前时间开始计算* 签名使用HMAC256算法,以确保令牌的安全性*/public static String genToken(String adminId, String sign) {return JWT.create().withAudience(adminId) // 将 user id 保存到 token 里面,作为载荷.withExpiresAt(DateUtil.offsetHour(new Date(), 2)) // 2小时后token过期.sign(Algorithm.HMAC256(sign)); // 以 password 作为 token 的密钥}/*** 获取当前登录的用户信息* 通过解析请求中的token,查找对应的管理员信息* 如果无法获取token或解析失败,则返回null* * @return 当前登录的管理员对象,如果获取失败则返回null*/public static Admin getCurrentUser() {// 初始化token变量String token = null;try {// 从请求中获取tokenHttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();token = request.getHeader("token");// 如果token为空,则尝试从请求参数中获取if (StrUtil.isBlank(token)) {token = request.getParameter("token");}// 如果token仍然为空,则记录错误日志并返回nullif (StrUtil.isBlank(token)) {log.error("获取当前登录的token失败, token: {}", token);return null;}// 解析token,获取用户的idString adminId = JWT.decode(token).getAudience().get(0);// 根据用户id查找并返回管理员信息return staticAdminService.findByById(Integer.valueOf(adminId));} catch (Exception e) {// 如果出现异常,则记录错误日志并返回nulllog.error("获取当前登录的管理员信息失败, token={}", token,  e);return null;}}
}

用户在登录成功后,需要返回一个token给前台

// 生成jwt token给前端
String token = JwtTokenUtils.genToken(user.getId().toString(), user.getPassword());
user.setToken(token);

前台把token获取到,下次请求的时候,带到header里

const user = localStorage.getItem("user");
if (user) {config.headers['token'] = JSON.parse(user).token;
}

拦截器:JwtInterceptor.java

拦截器一般与jwt令牌联合使用

import cn.hutool.core.util.StrUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.example.entity.Admin;
import com.example.exception.CustomException;
import com.example.service.AdminService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/*** JWT拦截器,用于拦截请求并验证JWT令牌*/
@Component
public class JwtInterceptor implements HandlerInterceptor {// 日志记录器private static final Logger log = LoggerFactory.getLogger(JwtInterceptor.class);// 注入的管理员服务,用于查询管理员信息@Resourceprivate AdminService adminService;/*** 在处理请求之前执行拦截操作* * @param request  当前的HTTP请求对象* @param response 当前的HTTP响应对象* @param handler  当前处理请求的处理器* @return 如果返回false,请求将不会继续;如果返回true,请求将继续* @throws Exception 如果在预处理过程中发生异常*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {// 从HTTP请求的header中获取tokenString token = request.getHeader("token");if (StrUtil.isBlank(token)) {// 如果header中没有token,尝试从请求参数中获取token = request.getParameter("token");}// 开始执行认证if (StrUtil.isBlank(token)) {throw new CustomException("无token,请重新登录");}// 获取token中的userIdString userId;Admin admin;try {userId = JWT.decode(token).getAudience().get(0);// 根据token中的userid查询数据库admin = adminService.findById(Integer.parseInt(userId));} catch (Exception e) {String errMsg = "token验证失败,请重新登录";log.error(errMsg + ", token=" + token, e);throw new CustomException(errMsg);}if (admin == null) {throw new CustomException("用户不存在,请重新登录");}try {// 使用用户密码加签验证tokenJWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(admin.getPassword())).build();jwtVerifier.verify(token); // 验证token} catch (JWTVerificationException e) {throw new CustomException("token验证失败,请重新登录");}// 验证通过,继续执行下一个拦截器或目标处理器return true;}
}

拦截器配置好了,但是如何生效?在webConfig里添加拦截器规则:

import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;import javax.annotation.Resource;@Configuration
// 使用@Configuration注解标记该类为配置类,用于替代xml配置文件
public class WebConfig implements  WebMvcConfigurer {@Resourceprivate JwtInterceptor jwtInterceptor;@Override// 实现configurePathMatch方法,用于配置路径匹配策略public void configurePathMatch(PathMatchConfigurer configurer) {// 指定controller统一的接口前缀// 通过addPathPrefix方法为所有带有RestController注解的控制器添加"/api"前缀configurer.addPathPrefix("/api", clazz -> clazz.isAnnotationPresent(RestController.class));}/*** 添加自定义拦截器JwtInterceptor* 设置拦截规则,用于对请求进行鉴权* * @param registry 拦截器注册对象,用于向Spring MVC注册自定义拦截器*/@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 注册JwtInterceptor拦截器,并设置拦截路径为/api/**,即对所有/api下的请求进行拦截// 排除/api/admin/login和/api/admin/register路径,这些路径不需要鉴权即可访问registry.addInterceptor(jwtInterceptor).addPathPatterns("/api/**").excludePathPatterns("/api/admin/login").excludePathPatterns("/api/admin/register");}}

跨越相关问题:

CorsConfig.java设置自定义头

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;@Configuration
public class CorsConfig {/*** 创建并配置CorsFilter bean以支持跨域请求* 通过分析UrlBasedCorsConfigurationSource和CorsConfiguration的设置,* 可以允许所有来源的跨域请求,并对请求头和请求方法无限制** @return 配置好的CorsFilter实例*//*** 该函数用于创建并配置一个CorsFilter Bean,以支持跨域请求。* 通过设置UrlBasedCorsConfigurationSource和CorsConfiguration,* 该函数允许所有来源的跨域请求,并且对请求头和请求方法没有限制。* 返回配置好的CorsFilter实例,将其注册到Spring上下文中,以便在处理请求时自动应用跨域配置。* @return*/@Beanpublic CorsFilter corsFilter() {UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();CorsConfiguration corsConfiguration = new CorsConfiguration();corsConfiguration.addAllowedOrigin("*"); // 允许所有来源的跨域请求corsConfiguration.addAllowedHeader("*"); // 允许所有请求头corsConfiguration.addAllowedMethod("*"); // 允许所有请求方法source.registerCorsConfiguration("/**", corsConfiguration); // 对所有路径下的请求应用跨域配置return new CorsFilter(source);}
}

相关文章:

登录相关功能的优化【JWT令牌+拦截器+跨域】

登录相关功能的优化 登录后显示当前登录用户el-dropdown: Element - The worlds most popular Vue UI framework <el-dropdown style"float: right; height: 60px; line-height: 60px"><span class"el-dropdown-link" style"color: white;…...

向日葵没有显示器会卡住

前言 有一台机器【ubuntu20】&#xff0c;用于远程开发&#xff0c;使用向日葵时候&#xff0c;如果不接显示器是会卡住的。。。 显示屏是有限的&#xff0c;所以现在解决一下这个问题。 卡在登录界面 双击启动 由于Ubuntu默认显示管理器是gdm&#xff0c;而向日葵使用的是l…...

【机器学习西瓜书学习笔记——聚类】

机器学习西瓜书学习笔记【第九章】 第九章 聚类9.1 聚类任务9.2 性能度量两类指标 9.3距离计算基本性质属性有序属性无序属性 混合距离加权距离 9.4 原型聚类K-MEANS聚类算法步骤优势劣势 学习向量量化高斯混合聚类步骤难点例子EM思想的体现小结 9.5 密度聚类9.6 层次聚类 第九…...

MATLAB(8)深度变化模型

一、前言 在MATLAB中模拟深度变化模型通常依赖于具体的应用场景&#xff0c;比如海洋深度、地下水深度、地形高度变化等。由于“深度变化”可以涉及多种物理过程和数学模型&#xff0c;我将提供一个简化的示例&#xff0c;该示例模拟了一个基于时间变化的深度变化模型&#xff…...

mp3格式转换器哪个好用?汇总七款音频格式转换方法(无损转换)

音乐已经成为我们生活中不可或缺的一部分。但是在播放的时候&#xff0c;可能会遇到音频格式不兼容的情况。特别是在一些下载站或音乐平台获取的音频&#xff0c;有些特殊格式在播放器上无法正常播放&#xff0c;一般这种情况我们需要借助mp3转换器解决。 mp3是一种常见的数字音…...

移行前的复盘:CodeCommit 的重要地位分析

前言 截至7月28日&#xff0c;关于AWS CodeCommit的现状如下&#xff1a; 现有账号的现有存储库可以继续使用CodeCommit&#xff0c;不受限制。之前未使用过CodeCommit的账号&#xff08;或没有现有存储库的账号&#xff09;无法创建新的存储库。 这并不意味着CodeCommit的服…...

Java中等题-括号生成(力扣)

数字 n 代表生成括号的对数&#xff0c;请你设计一个函数&#xff0c;用于能够生成所有可能的并且 有效的 括号组合。 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff1a;["((()))","(()())","(())()","()(())","()()(…...

Flink 实时数仓(八)【DWS 层搭建(二)流量域、用户域、交易域搭建】

前言 今天的任务是完成流量域最后一个需求、用户域的两个需求以及交易域的部分需求&#xff1b; 1、流量域页面浏览各窗口汇总表 任务&#xff1a;从 Kafka 页面日志主题读取数据&#xff0c;统计当日的首页和商品详情页独立访客数。 注意&#xff1a;一般我们谈到访客&…...

gitlab-runner /var/run/docker.sock connect permission denied

usermod -aG docker gitlab-runner sudo service docker restart参考&#xff1a;https://gitlab.com/gitlab-org/gitlab-runner/-/issues/3492...

网络安全 - 应急响应检查表

前言 本项目旨在为应急响应提供全方位辅助&#xff0c;以便快速解决问题。结合自身经验和网络资料&#xff0c;形成检查清单&#xff0c;期待大家提供更多技巧&#xff0c;共同完善本项目。愿大家在应急之路一帆风顺。 图片皆来源于网络&#xff0c;如有侵权请联系删除。 一…...

AD常用PCB设计规则介绍 (详细版)

AD09常用PCB设计规则介绍 电气设计规则用来设置在电路板布线过程中所遵循的电气方面的规则&#xff0c;包括安全间距、短路、未布线网络和未连接引脚这四个方面的规则&#xff1a; &#xff08;1&#xff09;、安全间距规则(clearance) 该规则用于设定在PCB设计中&#xff0…...

mysql主从服务配置

主从MySQL服务器 [rootlocalhost ~]# yum -y install ntpdate [rootlocalhost ~]# ntpdate cn.ntp.org.cn [rootlocalhost ~]# yum -y install rsync [rootlocalhost ~]# vim mysql.sh #!/bin/bash yum list installed |grep libaio if [ $? ne 0 ]; then yum -y install…...

Redis基础总结、持久化、主从复制、哨兵模式、内存淘汰策略、缓存

文章目录 Redis 基础Redis 是什么&#xff0c;有哪些特点为什么要使用 Redis 而不仅仅依赖 MySQLRedis 是单线程吗Redis 单线程为什么还这么快 Redis 数据类型和数据结构五种基本数据结构及应用场景其他数据类型Redis 底层数据结构 Redis 持久化数据不丢失的实现AOF 日志RDB 快…...

Java与Python优劣势对比:具体例子与深入分析

在软件开发的世界里&#xff0c;Java和Python是两座不可忽视的高峰。它们各自拥有独特的优势和应用场景&#xff0c;为开发者提供了多样化的选择。本文将通过具体例子&#xff0c;深入分析Java和Python在不同方面的表现&#xff0c;以期为读者提供更为详尽的参考。 1. 语法简洁…...

C++内存泄漏介绍

C内存泄漏&#xff08;Memory Leak&#xff09;是指程序在运行过程中&#xff0c;动态分配的内存没有被适当地释放或回收&#xff0c;导致这部分内存始终被占用&#xff0c;无法再被程序或其他程序使用。这种情况通常发生在使用了new或malloc等函数动态分配内存后&#xff0c;忘…...

C++分析红黑树

目录 红黑树介绍 红黑树的性质与平衡控制关系 红黑树节点的插入 情况1&#xff1a;不需要调整 情况2&#xff1a;uncle节点为红色 情况3&#xff1a;uncle节点为黑色 总结与代码实现 红黑树的删除&#xff08;待实现&#xff09; 红黑树的效率 红黑树介绍 红黑树是第二种平衡二…...

mysql线上查询之前要性能调优

查询优化是数据库性能调优的关键方面&#xff0c;目的是减少查询的执行时间和资源消耗。以下是一些常见的查询优化技巧及其示例&#xff1a; 使用合适的索引 问题&#xff1a; 全表扫描导致查询缓慢优化&#xff1a; 为经常用于搜索条件的列添加索引示例&#xff1a; 假设有一…...

GPIO输出控制之LED闪烁、LED流水灯以及蜂鸣器应用案例

系列文章目录 STM32之GPIO&#xff08;General Purpose Input/Output&#xff0c;通用型输入输出&#xff09; 文章目录 系列文章目录前言一、LED和蜂鸣器简介1.1 LED1.2 蜂鸣器1.3 面包板 二、LED硬件电路2.1 低电平驱动电路2.2 高电平驱动电路 三、蜂鸣器硬件电路3.1 PNP型三…...

体系结构论文导读(三十四):Design of Reliable DNN Accelerator with Un-reliable ReRAM

文章核心 这篇文章主要讨论了一种在不可靠的ReRAM&#xff08;阻变存储器&#xff09;设备上设计可靠的深度神经网络&#xff08;DNN&#xff09;加速器的方法。文章提出了两种关键技术来解决ReRAM固有的不可靠性问题&#xff1a;动态定点&#xff08;DFP&#xff09;数据表示…...

WebStock会话

其实使用消息队列也可以实现会话&#xff0c;直接前端监听指定的队列&#xff0c;使用rabbitmq的分组还可以实现不同群聊的效果。 1、依赖搭建&#xff1a; <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org…...

Ubuntu系统下SocketCAN实战:免驱配置PCAN/PCAN FD设备全流程

1. 认识SocketCAN与PCAN设备 在嵌入式开发和汽车电子领域&#xff0c;CAN总线就像设备之间的"神经传导系统"&#xff0c;而SocketCAN则是Linux内核为这个系统提供的"标准语言接口"。我第一次接触PCAN设备时&#xff0c;发现它有个巨大优势——大多数型号在…...

Legacy iOS Kit:让旧款iPhone/iPad重获新生的终极降级工具

Legacy iOS Kit&#xff1a;让旧款iPhone/iPad重获新生的终极降级工具 【免费下载链接】Legacy-iOS-Kit An all-in-one tool to restore/downgrade, save SHSH blobs, jailbreak legacy iOS devices, and more 项目地址: https://gitcode.com/gh_mirrors/le/Legacy-iOS-Kit …...

SSL4MIS社区贡献指南:从代码提交到算法实现的完整流程

SSL4MIS社区贡献指南&#xff1a;从代码提交到算法实现的完整流程 【免费下载链接】SSL4MIS Semi Supervised Learning for Medical Image Segmentation, a collection of literature reviews and code implementations. 项目地址: https://gitcode.com/gh_mirrors/ss/SSL4MI…...

还在手动刷新Elsevier审稿页面?这个免费插件让你一目了然!

还在手动刷新Elsevier审稿页面&#xff1f;这个免费插件让你一目了然&#xff01; 【免费下载链接】Elsevier-Tracker 项目地址: https://gitcode.com/gh_mirrors/el/Elsevier-Tracker 每天打开Elsevier审稿页面&#xff0c;看着那个永远不变的"Under Review"…...

Qwen3-0.6B-FP8快速上手:用Chainlit打造专属聊天机器人实战

Qwen3-0.6B-FP8快速上手&#xff1a;用Chainlit打造专属聊天机器人实战 1. 准备工作与环境检查 1.1 了解Qwen3-0.6B-FP8模型 Qwen3-0.6B-FP8是Qwen系列最新一代的语言模型&#xff0c;采用FP8精度优化&#xff0c;在保持高性能的同时显著降低计算资源需求。这个60亿参数的模…...

五年磨剑与二十年深耕:5 年与 20 年程序员的差距,远不止代码本身

在信息技术飞速迭代的今天&#xff0c;程序员这一职业始终站在时代前沿。有人说&#xff0c;程序员是吃 “青春饭” 的行业&#xff0c;年轻意味着精力充沛、学习速度快、能熬夜加班&#xff1b;也有人说&#xff0c;真正的技术高手&#xff0c;往往藏在十几年甚至二十余年的行…...

Linux持久化配置GRE接口

Centos7持久化配置GRE接口GRE协议简介前置条件1.开发ipv4转发1.1启用TCP窗口缩放1.2启用自动MTU1.3禁用ipv6--可选1.4使内核配置生效2.加载内核模块3.放通防火墙方案1:ip命令临时配置GRE隧道VM1临时配置GRE接口配置邻居启动GRE隧道VM2临时配置GRE接口配置邻居启动GRE隧道关闭和…...

Ant Media Server性能优化:10个提升流媒体质量的关键技巧

Ant Media Server性能优化&#xff1a;10个提升流媒体质量的关键技巧 【免费下载链接】Ant-Media-Server Ant Media Server — Ultra-low latency streaming engine with WebRTC (~0.5s), SRT, RTMP, HLS, CMAF, adaptive bitrate, transcoding & scaling 项目地址: http…...

星图AI云:Qwen3-VL:30B私有化部署,飞书机器人快速搭建

星图AI云&#xff1a;Qwen3-VL:30B私有化部署&#xff0c;飞书机器人快速搭建 1. 为什么你需要一个能看图说话的飞书助手&#xff1f; 想象一下这个场景&#xff1a;周一早上&#xff0c;你刚打开飞书&#xff0c;就看到同事在群里发了一张密密麻麻的Excel表格截图&#xff0…...

自动化测试中的“等待”策略:聪明地等待,而不是傻等

一、为什么等待策略是自动化测试的命脉 在自动化测试中&#xff0c;等待策略直接决定脚本的稳定性和执行效率。当测试代码以毫秒级速度运行时&#xff0c;浏览器渲染、网络请求和异步加载往往需要秒级响应。若缺乏合理的等待机制&#xff0c;将引发三大致命问题&#xff1a; 元…...