SpringBoot+JWT实现单点登录解决方案
一、什么是单点登录?
单点登录是一种统一认证和授权机制,指在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的系统,不需要重新登录验证。
单点登录一般用于互相授信的系统,实现单一位置登录,其他信任的应用直接免登录的方式,在多个应用系统中,只需要登录一次,就可以访问其他互相信任的应用系统。
随着时代的演进,大型web系统早已从单体应用架构发展为如今的多系统分布式应用群。但无论系统内部多么复杂,对用户而言,都是一个统一的整体,访问web系统的整个应用群要和访问单个系统一样,登录/注销只要一次就够了,不可能让一个用户在每个业务系统上都进行一次登录验证操作,这时就需要独立出一个单独的认证系统,它就是单点登录系统。
二、单点登录的优点
1.方便用户使用。用户不需要多次登录系统,不需要记住多个密码,方便用户操作。
2.提高开发效率。单点登录为开发人员提供类一个通用的验证框架。
3.简化管理。如果在应用程序中加入了单点登录的协议,管理用户账户的负担就会减轻。
三、JWT 机制
JWT(JSON Web Token)它将用户信息加密到token里,服务器不保存任何用户信息。服务器通过使用保存的密钥验证JWTToken的正确性,只要正确就通过验证。
数据结构:
JWT包含三个部分:Header头部,Payload负载和Signature签名。三个部门用“.”分割。校验也是JWT内部自己实现的 ,并且可以将你存储时候的信息从token中取出来无须查库。
JWT执行流程:
JWT的请求流程也特别简单,首先使用账号登录获取Token,然后后面的各种请求,都带上这个Token即可。具体流程如下:
1. 客户端发起登录请求,传入账号密码;
2. 服务端使用私钥创建一个Token;
3. 服务器返回Token给客户端;
4. 客户端向服务端发送请求,在请求头中携带Token;
5. 服务器验证该Token;
6. 返回结果。

四.创建Maven父项目

2.指定打包类型为pom

五.创建认证模块sso

1.添加依赖,完整的pom文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.13</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.example</groupId><artifactId>sso</artifactId><version>0.0.1-SNAPSHOT</version><name>sso</name><description>sso</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--lombok--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!--jwt--><dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.8.2</version></dependency><!--thymeleaf--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>
2添加jwt相关配置
3.创建JWT配置类和JWT工具类
示例代码如下:
package com.example.sso.bean;import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;/*** @author qx* @date 2023/7/4* @des Jwt配置类*/
@Component
@ConfigurationProperties(prefix = "jwt")
@Getter
@Setter
public class JwtProperties {/*** 过期时间-分钟*/private Integer expireTime;/*** 密钥*/private String secret;
}
package com.example.sso.util;import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.example.sso.bean.JwtProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.util.Date;/*** @author qx* @date 2023/7/4* @des JWT工具类*/
@Component
public class JwtUtil {@Autowiredprivate JwtProperties jwtProperties;/*** 生成一个jwt字符串** @param username 用户名* @return jwt字符串*/public String sign(String username) {Algorithm algorithm = Algorithm.HMAC256(jwtProperties.getSecret());return JWT.create()// 设置过期时间1个小时.withExpiresAt(new Date(System.currentTimeMillis() + jwtProperties.getExpireTime() * 60 * 1000))// 设置负载.withClaim("username", username).sign(algorithm);}public static void main(String[] args) {Algorithm algorithm = Algorithm.HMAC256("KU5TjMO6zmh03bU3");String username = "admin";String token = JWT.create()// 设置过期时间1个小时.withExpiresAt(new Date(System.currentTimeMillis() + 60 * 60 * 1000))// 设置负载.withClaim("username", username).sign(algorithm);System.out.println(token);}/*** 校验token是否正确** @param token token值*/public boolean verify(String token) {if (token == null || token.length() == 0) {throw new RuntimeException("token为空");}try {Algorithm algorithm = Algorithm.HMAC256(jwtProperties.getSecret());JWTVerifier jwtVerifier = JWT.require(algorithm).build();jwtVerifier.verify(token);return true;} catch (Exception e) {e.printStackTrace();return false;}}}
4.创建服务层
package com.example.sso.service;import com.example.sso.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;/*** @author qx* @date 2023/7/4* @des 登录服务层*/
@Service
public class LoginService {@Autowiredprivate JwtUtil jwtUtil;/*** 登录** @param username 用户名* @param password 密码* @return token值*/public String login(String username, String password) {if ("".equals(username) || "".equals(password)) {throw new RuntimeException("用户名或密码不能为空");}// 为了测试方便 不去数据库比较密码if ("123".equals(password)) {// 返回生成的tokenreturn jwtUtil.sign(username);}return null;}/*** 校验jwt是否成功** @param token token值* @return 校验是否超过*/public boolean checkJwt(String token) {return jwtUtil.verify(token);}
}
5.创建控制层
package com.example.sso.controller;import com.example.sso.service.LoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;/*** @author qx* @date 2023/7/4* @des 验证控制层*/
@Controller
@RequestMapping("/sso")
public class AuthController {@Autowiredprivate LoginService loginService;/*** 登录页面*/@GetMapping("/login")public String toLogin() {return "login";}/*** 登录** @param username 用户名* @param password 密码* @return token值*/@PostMapping("/login")@ResponseBodypublic String login(String username, String password) {return loginService.login(username, password);}/*** 验证jwt** @param token token* @return 验证jwt是否合法*/@RequestMapping("/checkJwt")@ResponseBodypublic boolean checkJwt(String token) {return loginService.checkJwt(token);}}
6.创建一个登录页面login.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>登录</title>
</head>
<body><form method="post" action="/sso/login">用户名:<input type="text" name="username"/><br/>密码:<input type="password" name="password"/><br/><button type="submit">登录</button></form>
</body>
</html>
六、创建应用系统projectA

1.项目pom文件如下所示
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.example</groupId><artifactId>my-sso</artifactId><version>1.0-SNAPSHOT</version></parent><artifactId>projectA</artifactId><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.7.13</version></dependency><!--okhttp--><dependency><groupId>com.squareup.okhttp3</groupId><artifactId>okhttp</artifactId><version>4.10.0</version></dependency></dependencies>
</project>
2.修改配置文件

3.创建过滤器
package com.example.projectA.filter;import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;/*** @author qx* @date 2023/7/4* @des 登录过滤器*/
@Component
@WebFilter(urlPatterns = "/**")
public class LoginFilter implements Filter {@Value("${sso_server}")private String serverHost;@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;String token = httpServletRequest.getParameter("token");if (this.check(token)) {filterChain.doFilter(servletRequest, servletResponse);} else {HttpServletResponse response = (HttpServletResponse) servletResponse;String redirect = serverHost + "/login";response.sendRedirect(redirect);}}/*** 验证token** @param token* @return* @throws IOException*/private boolean check(String token) throws IOException {if (token == null || token.trim().length() == 0) {return false;}OkHttpClient client = new OkHttpClient();// 请求验证token的合法性String url = serverHost + "/checkJwt?token=" + token;Request request = new Request.Builder().url(url).build();Response response = client.newCall(request).execute();return Boolean.parseBoolean(response.body().string());}
}
4.创建测试控制层
package com.example.projectA.controller;import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;/*** @author qx* @date 2023/7/4* @des 测试A*/
@RestController
public class IndexController {@GetMapping("/testA")public String testA() {return "输出testA";}
}
5.启动类
package com.example.projectA;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;/*** @author qx* @date 2023/7/4* @des Projecta启动类*/
@SpringBootApplication
@ServletComponentScan
public class ProjectaApplication {public static void main(String[] args) {SpringApplication.run(ProjectaApplication.class,args);}
}
6.启动项目测试
我们访问http://localhost:8081/testA 系统跳转到了验证模块的登录页面

我们输入账号密码登录成功后返回token

如何我们复制这段数据,把数据传递到token参数。

我们看到正确获取到了数据。
七、创建应用系统projectB
我们再次模仿projectA创建projectB子模块。



启动模块B
我们直接测试带上token参数
通过之前的token,无需登录即可成功进入了应用系统B。说明我们的单点登录系统搭建成功。
相关文章:
SpringBoot+JWT实现单点登录解决方案
一、什么是单点登录? 单点登录是一种统一认证和授权机制,指在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的系统,不需要重新登录验证。 单点登录一般用于互相授信的系统,实现单一位置登录,其他信任的…...
中国国债发行数据集(2002-2023)
国债是由国家发行的债券,由于国债的发行主体是国家,所以它具有最高的信用度,被公认为是最安全的投资工具。国债按照交易市场的不同分为三类,即银行间市场国债、交易所市场国债和柜台市场国债;按照交易方式的不同分为三…...
曲师大2023大一新生排位赛-C.String题解
C.String 题目描述 众所周知,许师哥精通字符串。 一天,许师哥意外的获得了一个字符串,但他发现这个字符串并不是一个回文串,因此他非常生气。于是他决定从这个字符串中删除若干个字符使得 剩余的字符串为一个回文串。 回想回文串…...
Linux Ubuntu安装RabbitMQ服务
文章目录 前言1.安装erlang 语言2.安装rabbitMQ3. 内网穿透3.1 安装cpolar内网穿透(支持一键自动安装脚本)3.2 创建HTTP隧道 4. 公网远程连接5.固定公网TCP地址5.1 保留一个固定的公网TCP端口地址5.2 配置固定公网TCP端口地址 前言 RabbitMQ是一个在 AMQP(高级消息队列协议)基…...
什么是测试用例?如何设计?
在学习或者实际的测试工作中经常都会提到“测试用例”这个词,没错,测试用例是测试工作的核心,不管要做的是什么样的测试,在真正动手执行测试之前,我们都需要先根据软件需求来设计测试用例,之后再依据设计好…...
Maven —— 项目管理工具
前言 在这篇文章中,荔枝会介绍如何在项目工程中借助Maven的力量来开发,主要涉及Maven的下载安装、环境变量的配置、IDEA中的Maven的路径配置和信息修改以及通过Maven来快速构建项目。希望能对需要配置的小伙伴们有帮助哈哈哈哈~~~ 文章目录 前言 一、初…...
Ubuntu 命令行编辑文件后如何保存退出
在 Ubuntu 命令行中编辑文件后,可以使用以下步骤保存并退出: 按下键盘上的 Ctrl 键和 X 键组合,以退出编辑模式。如果文件已更改,你将看到提示,询问是否保存更改。按下 Y 键来确认保存更改,或按下 N 键取消…...
AHB协议理解
从小父亲就教育我,做一个对社会有用的人! 目录 Chapter1 AHB Block Diagram Ginput signal lnput signals Output Signal Chapter3 Transfers AHB接口Overview Chapter6 Data Buses HWDATA HRDATA Chapter1 Introduction AHB: Advanced High-performanc…...
【UE5 多人联机教程】01-创建主界面
目录 工程地址 步骤 参考链接 工程地址 GitHub - UE4-Maple/C_MP_Lobby: 多人大厅教程项目 步骤 1. 先新建一个工程 2. 将下载的工程中的Plugins目录拷贝到自己新建的工程下 3. 打开工程,新建一个游戏实例 这里命名为“GI_Main” 在项目设置中设置游戏实例类为…...
HarmonyOS学习路之方舟开发框架—学习ArkTS语言(基本语法 五)
Styles装饰器:定义组件重用样式 如果每个组件的样式都需要单独设置,在开发过程中会出现大量代码在进行重复样式设置,虽然可以复制粘贴,但为了代码简洁性和后续方便维护,我们推出了可以提炼公共样式进行复用的装饰器St…...
React(3)
1.案例选项卡 import React, { Component } from reactexport default class App extends Component {state{tabList:[{id:1,text:"电影"},{id:2,text:"影院"},{id:3,text:"我的"}]}render() {return (<div><ul>{this.state.tabList…...
LangChain大型语言模型(LLM)应用开发(三):Chains
LangChain是一个基于大语言模型(如ChatGPT)用于构建端到端语言模型应用的 Python 框架。它提供了一套工具、组件和接口,可简化创建由大型语言模型 (LLM) 和聊天模型提供支持的应用程序的过程。LangChain 可以轻松管理与语言模型的交互&#x…...
FPGA——点亮led灯
文章目录 一、实验环境二、实验任务三、实验过程3.1 编写verliog程序3.2 引脚配置 四、仿真4.1 仿真代码4.2仿真结果 五、实验结果六、总结 一、实验环境 quartus18.1 vscode Cyclone IV开发板 二、实验任务 每间隔1S实现led灯的亮灭,实现流水灯的效果。 三、实…...
idea创建spark教程
1、环境准备 java -version scala -version mvn -version spark -version 2、创建spark项目 创建spark项目,有两种方式;一种是本地搭建hadoop和spark环境,另一种是下载maven依赖;最后在idea中进行配置,下面分别记录两…...
【JavaEE】DI与DL的介绍-Spring项目的创建-Bean对象的存储与获取
Spring的开发要点总结 文章目录 【JavaEE】Spring的开发要点总结(1)1. DI 和 DL1.1 DI 依赖注入1.2 DL 依赖查询1.3 DI 与 DL的区别1.4 IoC 与 DI/DL 的区别 2. Spring项目的创建2.1 创建Maven项目2.2 设置国内源2.2.1 勾选2.2.2 删除本地jar包2.2.3 re…...
C#图片处理
查找图片所在位置 原理:使用OpenCvSharp对比查找小图片在大图片上的位置 private static System.Drawing.Point Find(Mat BackGround, Mat Identify, double threshold 0.8) {using (Mat res new Mat(BackGround.Rows - Identify.Rows 1, BackGround.Cols - Iden…...
php 开发微信 h5 支付 APIv3 接入超详细流程
✨ 目录 🎈 申请商户号🎈 申请商户证书🎈 设置V3密钥🎈 开通H5支付🎈 设置支付域名🎈 SDK 下载🎈 第一次下载平台证书🎈非第一次下载平台证书🎈 H5下单 🎈 申…...
HTML学习 第一部分(前端学习)
参考学习网站: 网页简介 (w3schools.com) 我的学习思路是:网站实践视频。 视频很重要的,因为它会给你一种开阔思路的方式。你会想,噢!原来还可以这样。这是书本或者网站教程 所不能教给你的。而且,对一些教程&#…...
python 实现串口指令通讯
上一篇文章文章写了串口数据的读取,这篇文章讲串口数据的写入(指令控制) 与下位机通信往往需要十六进制形式进行数据通信,根据设备串口通信指令文档进行指令通信,本篇以灯光控制为例: 1.pyserial模块封装…...
pytorch深度学习逻辑回归 logistic regression
# logistic regression 二分类 # 导入pytorch 和 torchvision import numpy as np import torch import torchvision from torch.autograd import Variable import torch.nn as nn import torch.nn.functional as F import torch.optim as optim import matplotlib.pyplot as …...
19c补丁后oracle属主变化,导致不能识别磁盘组
补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...
智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
Docker 运行 Kafka 带 SASL 认证教程
Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明:server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...
Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器
第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
Rust 异步编程
Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...
Caliper 配置文件解析:config.yaml
Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...
学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2
每日一言 今天的每一份坚持,都是在为未来积攒底气。 案例:OLED显示一个A 这边观察到一个点,怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 : 如果代码里信号切换太快(比如 SDA 刚变,SCL 立刻变&#…...
力扣热题100 k个一组反转链表题解
题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...
基于PHP的连锁酒店管理系统
有需要请加文章底部Q哦 可远程调试 基于PHP的连锁酒店管理系统 一 介绍 连锁酒店管理系统基于原生PHP开发,数据库mysql,前端bootstrap。系统角色分为用户和管理员。 技术栈 phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 注册/登录/注销 2 个人中…...
