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

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...

synchronized 学习

学习源&#xff1a; https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖&#xff0c;也要考虑性能问题&#xff08;场景&#xff09; 2.常见面试问题&#xff1a; sync出…...

调用支付宝接口响应40004 SYSTEM_ERROR问题排查

在对接支付宝API的时候&#xff0c;遇到了一些问题&#xff0c;记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...

阿里云ACP云计算备考笔记 (5)——弹性伸缩

目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...

从零实现富文本编辑器#5-编辑器选区模型的状态结构表达

先前我们总结了浏览器选区模型的交互策略&#xff0c;并且实现了基本的选区操作&#xff0c;还调研了自绘选区的实现。那么相对的&#xff0c;我们还需要设计编辑器的选区表达&#xff0c;也可以称为模型选区。编辑器中应用变更时的操作范围&#xff0c;就是以模型选区为基准来…...

FFmpeg 低延迟同屏方案

引言 在实时互动需求激增的当下&#xff0c;无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作&#xff0c;还是游戏直播的画面实时传输&#xff0c;低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架&#xff0c;凭借其灵活的编解码、数据…...

Python爬虫实战:研究feedparser库相关技术

1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...

1.3 VSCode安装与环境配置

进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件&#xff0c;然后打开终端&#xff0c;进入下载文件夹&#xff0c;键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...

在Ubuntu中设置开机自动运行(sudo)指令的指南

在Ubuntu系统中&#xff0c;有时需要在系统启动时自动执行某些命令&#xff0c;特别是需要 sudo权限的指令。为了实现这一功能&#xff0c;可以使用多种方法&#xff0c;包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法&#xff0c;并提供…...