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

conda相比python好处

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

零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?

一、核心优势&#xff1a;专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发&#xff0c;是一款收费低廉但功能全面的Windows NAS工具&#xff0c;主打“无学习成本部署” 。与其他NAS软件相比&#xff0c;其优势在于&#xff1a; 无需硬件改造&#xff1a;将任意W…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解

本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说&#xff0c;直接开始吧&#xff01; 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...

DBAPI如何优雅的获取单条数据

API如何优雅的获取单条数据 案例一 对于查询类API&#xff0c;查询的是单条数据&#xff0c;比如根据主键ID查询用户信息&#xff0c;sql如下&#xff1a; select id, name, age from user where id #{id}API默认返回的数据格式是多条的&#xff0c;如下&#xff1a; {&qu…...

JDK 17 新特性

#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持&#xff0c;不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的&#xff…...

ios苹果系统,js 滑动屏幕、锚定无效

现象&#xff1a;window.addEventListener监听touch无效&#xff0c;划不动屏幕&#xff0c;但是代码逻辑都有执行到。 scrollIntoView也无效。 原因&#xff1a;这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作&#xff0c;从而会影响…...

分布式增量爬虫实现方案

之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面&#xff0c;避免重复抓取&#xff0c;以节省资源和时间。 在分布式环境下&#xff0c;增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路&#xff1a;将增量判…...

探索Selenium:自动化测试的神奇钥匙

目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...

Python网页自动化Selenium中文文档

1. 安装 1.1. 安装 Selenium Python bindings 提供了一个简单的API&#xff0c;让你使用Selenium WebDriver来编写功能/校验测试。 通过Selenium Python的API&#xff0c;你可以非常直观的使用Selenium WebDriver的所有功能。 Selenium Python bindings 使用非常简洁方便的A…...

Matlab实现任意伪彩色图像可视化显示

Matlab实现任意伪彩色图像可视化显示 1、灰度原始图像2、RGB彩色原始图像 在科研研究中&#xff0c;如何展示好看的实验结果图像非常重要&#xff01;&#xff01;&#xff01; 1、灰度原始图像 灰度图像每个像素点只有一个数值&#xff0c;代表该点的​​亮度&#xff08;或…...