当前位置: 首页 > 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…...

Docker 离线安装指南

参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性&#xff0c;不同版本的Docker对内核版本有不同要求。例如&#xff0c;Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本&#xff0c;Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...

conda相比python好处

Conda 作为 Python 的环境和包管理工具&#xff0c;相比原生 Python 生态&#xff08;如 pip 虚拟环境&#xff09;有许多独特优势&#xff0c;尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处&#xff1a; 一、一站式环境管理&#xff1a…...

Leetcode 3576. Transform Array to All Equal Elements

Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接&#xff1a;3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到&#xf…...

服务器硬防的应用场景都有哪些?

服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式&#xff0c;避免服务器受到各种恶意攻击和网络威胁&#xff0c;那么&#xff0c;服务器硬防通常都会应用在哪些场景当中呢&#xff1f; 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...

定时器任务——若依源码分析

分析util包下面的工具类schedule utils&#xff1a; ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类&#xff0c;封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz&#xff0c;先构建任务的 JobD…...

什么是库存周转?如何用进销存系统提高库存周转率?

你可能听说过这样一句话&#xff1a; “利润不是赚出来的&#xff0c;是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业&#xff0c;很多企业看着销售不错&#xff0c;账上却没钱、利润也不见了&#xff0c;一翻库存才发现&#xff1a; 一堆卖不动的旧货…...

将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?

Otsu 是一种自动阈值化方法&#xff0c;用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理&#xff0c;能够自动确定一个阈值&#xff0c;将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...

Java多线程实现之Thread类深度解析

Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台

🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...

Pinocchio 库详解及其在足式机器人上的应用

Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库&#xff0c;专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性&#xff0c;并提供了一个通用的框架&…...