Java实现单点登录(SSO)详解:从理论到实践
✨✨谢谢大家捧场,祝屏幕前的小伙伴们每天都有好运相伴左右,一定要天天开心哦!✨✨
🎈🎈作者主页: 喔的嘛呀🎈🎈
✨✨ 帅哥美女们,我们共同加油!一起进步!✨✨
目录
引言
一、什么是单点登录(SSO)?
二、SSO的工作原理
三、SSO的具体实现
SSO的核心概念
1. 令牌(Token)机制
2. 身份验证协议
SSO实现步骤
1. 选择身份验证协议
2. 创建认证服务器
3. 创建资源服务器
4. 客户端集成
总结
引言
随着互联网应用的不断发展,用户需要在多个系统之间无缝切换,而单点登录(Single Sign-On,简称SSO)技术应运而生。本篇博客将深入探讨Java中如何实现单点登录,通过详细代码和深度解析,带领读者逐步了解SSO的原理、流程和具体实现。
一、什么是单点登录(SSO)?
单点登录是一种身份认证的机制,允许用户在访问多个相关但独立的软件系统时,只需一次登录,便可无缝访问所有系统。这大大提高了用户体验,并简化了管理和维护的复杂性。
二、SSO的工作原理
SSO的工作原理基于令牌(Token)和身份验证协议。用户一旦登录系统,将获得一个令牌,该令牌包含了用户的身份信息。在之后的访问中,用户只需提供该令牌,而无需再次输入用户名和密码。
三、SSO的具体实现
SSO的核心概念
在深入代码之前,让我们先理解SSO的核心概念。
1. 令牌(Token)机制
SSO通过使用令牌实现用户的无缝切换。用户在登录成功后,获得一个令牌,该令牌包含了用户的身份信息。在之后的访问中,用户只需提供有效的令牌,而无需再次输入用户名和密码。
2. 身份验证协议
常见的身份验证协议包括OAuth 2.0和OpenID Connect。OAuth 2.0主要用于授权,而OpenID Connect在OAuth 2.0的基础上提供了身份验证的支持。
SSO实现步骤
1. 选择身份验证协议
在我们的实现中,我们选择使用OAuth 2.0和OpenID Connect协议。这两者的结合提供了强大的身份验证和授权机制。
2. 创建认证服务器
认证服务器是SSO系统的核心。我们使用Spring Security OAuth2和Spring Boot来实现一个简单但强大的认证服务器。
创建认证服务器是实现单点登录(SSO)系统的关键步骤之一。在这里,我们将使用Spring Security OAuth2和Spring Boot创建一个简单而强大的认证服务器。以下是详细的代码示例:
// 认证服务器配置类
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {@Autowiredprivate AuthenticationManager authenticationManager;@Autowiredprivate UserDetailsService userDetailsService;@Autowiredprivate DataSource dataSource;@Value("${security.oauth2.client.client-id}")private String clientId;@Value("${security.oauth2.client.client-secret}")private String clientSecret;@Value("${security.oauth2.client.authorized-grant-types}")private String[] authorizedGrantTypes;@Value("${security.oauth2.client.scopes}")private String[] scopes;@Overridepublic void configure(ClientDetailsServiceConfigurer clients) throws Exception {clients.jdbc(dataSource);}@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {endpoints.authenticationManager(authenticationManager).userDetailsService(userDetailsService);}@Overridepublic void configure(AuthorizationServerSecurityConfigurer security) throws Exception {security.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");}@Beanpublic JdbcTokenStore tokenStore() {return new JdbcTokenStore(dataSource);}@Beanpublic TokenStoreUserApprovalHandler userApprovalHandler(TokenStore tokenStore) {TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler();handler.setTokenStore(tokenStore);handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientId));handler.setClientDetailsService(clientDetailsService());handler.setUseApprovalStore(true);return handler;}@Beanpublic ApprovalStore approvalStore(TokenStore tokenStore) {TokenApprovalStore store = new TokenApprovalStore();store.setTokenStore(tokenStore);return store;}@Beanpublic ClientDetailsService clientDetailsService() {return new JdbcClientDetailsService(dataSource);}@Beanpublic DefaultTokenServices tokenServices() {DefaultTokenServices tokenServices = new DefaultTokenServices();tokenServices.setTokenStore(tokenStore());tokenServices.setSupportRefreshToken(true);tokenServices.setClientDetailsService(clientDetailsService());tokenServices.setAuthenticationManager(authenticationManager);return tokenServices;}@Beanpublic BCryptPasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}@Beanpublic OAuth2RequestFactory requestFactory() {return new DefaultOAuth2RequestFactory(clientDetailsService());}@Bean@Primarypublic DefaultTokenServices defaultTokenServices() {DefaultTokenServices defaultTokenServices = new DefaultTokenServices();defaultTokenServices.setTokenStore(tokenStore());defaultTokenServices.setSupportRefreshToken(true);defaultTokenServices.setClientDetailsService(clientDetailsService());return defaultTokenServices;}@Beanpublic BCryptPasswordEncoder bCryptPasswordEncoder() {return new BCryptPasswordEncoder();}@Beanpublic DataSourceInitializer dataSourceInitializer(DataSource dataSource) {DataSourceInitializer initializer = new DataSourceInitializer();initializer.setDataSource(dataSource);initializer.setDatabasePopulator(databasePopulator());return initializer;}private DatabasePopulator databasePopulator() {ResourceDatabasePopulator populator = new ResourceDatabasePopulator();populator.addScript(new ClassPathResource("schema.sql"));return populator;}
}
上述代码中,我们配置了认证服务器的各个方面,包括客户端信息、用户信息、令牌存储、用户批准处理等。这是一个基础配置,你可以根据实际需求进行调整和扩展。
此外,为了更好地演示,我们在配置中引入了一些外部配置(例如,client-id、client-secret、authorized-grant-types、scopes),你可以在应用的配置文件中定义这些属性。
通过这个认证服务器的配置,我们为SSO系统打下了坚实的基础。接下来,你可以继续配置资源服务器、客户端应用,并深入了解OAuth2和OpenID Connect协议的更多细节。
3. 创建资源服务器
在单点登录(SSO)系统中,资源服务器的作用是验证令牌并提供受保护的资源。以下是使用Spring Security实现资源服务器的详细代码示例:
// 资源服务器配置类
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {@Value("${security.oauth2.resource.id}")private String resourceId;@Overridepublic void configure(ResourceServerSecurityConfigurer resources) throws Exception {resources.resourceId(resourceId);}@Overridepublic void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/public/**").permitAll().antMatchers("/api/**").authenticated();}
}
在上述代码中,我们配置了资源服务器的两个主要部分:资源ID和HTTP安全性。
-
configure(ResourceServerSecurityConfigurer resources)方法用于配置资源服务器的资源ID。资源ID是资源服务器标识自身的唯一标识符,与授权服务器中的配置相对应。 -
configure(HttpSecurity http)方法定义了资源服务器的HTTP安全性配置。在这个例子中,我们允许对/public/**路径的请求进行公开访问,而对/api/**路径的请求进行身份验证。
请注意,你可能需要根据实际应用的需求进行调整和扩展,例如更复杂的授权规则、自定义访问决策等。
为了更好地演示,我们引入了一个外部配置(security.oauth2.resource.id),你可以在应用的配置文件中定义这个属性。
这个资源服务器配置类将帮助你在SSO系统中建立一个安全的资源服务,确保只有合法的用户(通过有效的令牌)能够访问受保护的资源。接下来,你可以继续配置客户端应用,使其能够通过令牌访问资源服务器上的资源。
4. 客户端集成
在单点登录(SSO)系统中,客户端应用需要集成认证服务器以获取令牌并访问资源服务器。以下是使用Spring Security OAuth2 Client实现客户端集成的详细代码示例:
// 客户端配置类
@Configuration
@EnableOAuth2Client
public class OAuth2ClientConfig {@Autowiredprivate OAuth2RestTemplate restTemplate;@Beanpublic OAuth2RestTemplate restTemplate(OAuth2ProtectedResourceDetails resource) {return new OAuth2RestTemplate(resource);}@Beanpublic OAuth2ProtectedResourceDetails resourceDetails() {AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails();details.setClientId("client-id");details.setClientSecret("client-secret");details.setAccessTokenUri("http://localhost:8080/oauth/token");details.setUserAuthorizationUri("http://localhost:8080/oauth/authorize");details.setScope(Arrays.asList("read", "write"));return details;}@Beanpublic RequestInterceptor oauth2FeignRequestInterceptor(OAuth2ClientContext oAuth2ClientContext,OAuth2ProtectedResourceDetails resource) {return new OAuth2FeignRequestInterceptor(oAuth2ClientContext, resource);}
}
在上述代码中,我们配置了客户端的关键组件:
-
OAuth2RestTemplate: 用于与认证服务器进行交互,获取令牌等。
-
OAuth2ProtectedResourceDetails: 包含了客户端的配置信息,如客户端ID、客户端密钥、授权服务器的地址等。
-
RequestInterceptor: 用于Feign客户端,确保在使用Feign进行远程调用时,令牌被正确传递。
在实际应用中,你可能需要更复杂的配置,例如刷新令牌、处理令牌错误、自定义用户信息等。这个配置类提供了一个基础的配置,你可以根据具体需求进行扩展。
请注意,上述代码中的URL和客户端信息应该与你的认证服务器的配置相匹配。同时,你可以在应用的配置文件中定义这些属性,以便更好地管理和维护。
通过这个客户端配置,你的应用将能够通过OAuth2协议与认证服务器进行交互,获取令牌,并在需要时访问资源服务器上的受保护资源。这是SSO系统中客户端应用的关键配置。
总结
通过本文,我们详细讨论了SSO的核心概念、选择了适当的身份验证协议,并提供了完整的Java代码实现。实现SSO系统需要深入理解身份验证协议、使用合适的框架,以及合理配置认证和资源服务器。
希望这篇博客能够为你提供深度且全面的SSO实现指南。通过这个实践,你将更好地理解和应用SSO技术,提升应用的用户体验和安全性。
相关文章:
Java实现单点登录(SSO)详解:从理论到实践
✨✨谢谢大家捧场,祝屏幕前的小伙伴们每天都有好运相伴左右,一定要天天开心哦!✨✨ 🎈🎈作者主页: 喔的嘛呀🎈🎈 ✨✨ 帅哥美女们,我们共同加油!一起进步&am…...
【leetcode82-91动态规划,91-95多维动态规划】
动态规划【82-91】 多维动态规划【91-95】...
Django学习第四天
启动项目命令 python manage.py runserver 分页功能封装到类中去 封装的类的代码 """ 自定义的分页组件,以后如果想要使用这个分页组件,你需要做: def pretty_list(request):# 靓号列表data_dict {}search_data request.GET.get(q, &…...
redis-benchmark 使用
Redis 自带了一个叫 redis-benchmark 的工具来模拟 N 个客户端同时发出 M 个请求。 Usage: redis-benchmark [-h <host>] [-p <port>] [-c <clients>] [-n <requests>] [-k <boolean>]-h <hostname> Server hostname (default 127.0…...
什么是 qobject_cast?
前言 在 C++ 中,类型转换是一项常见的操作,比如将 int 转换为 char 或将 QString 用于 QMessageBox。但是,为什么我们需要将一个类转换为另一个类呢?本文将解释 qobject_cast 是什么,它的作用以及为什么需要类型转换。 dynamic_cast 和 qobject_cast 的概述 什么是 dyn…...
Python酷库之旅-第三方库Pandas(001)
目录 一、Pandas库的由来 1、背景与起源 1-1、开发背景 1-2、起源时间 2、名称由来 3、发展历程 4、功能与特点 4-1、数据结构 4-2、数据处理能力 5、影响与地位 5-1、数据分析“三剑客”之一 5-2、社区支持 二、Pandas库的应用场景 1、数据分析 2、数据清洗 3…...
Firefox 编译指南2024 Windows10篇- 编译Firefox(三)
1.引言 在成功获取了Firefox源码之后,下一步就是将这些源码编译成一个可执行的浏览器。编译是开发流程中的关键环节,通过编译,我们可以将源代码转换为可执行的程序,测试其功能,并进行必要的优化和调试。 对于像Firef…...
CSS弹性布局:打造响应式与灵活的网页设计
一、弹性布局是什么? 弹性布局(Flexbox)是一种CSS布局模型,它提供了一种更加高效的方式来对容器中的项目进行布局、对齐和分配空间。与传统的布局方式相比,Flexbox旨在提供一个更加灵活的方式来布局复杂的网页结构&am…...
【高阶数据结构】图的应用--最短路径算法
文章目录 一、最短路径二、单源最短路径--Dijkstra算法三、单源最短路径--Bellman-Ford算法四、多源最短路径--Floyd-Warshall算法 一、最短路径 最短路径问题:从在带权有向图G中的某一顶点出发,找出一条通往另一顶点的最短路径,最短也就是沿…...
腾讯云函数node.js返回自动带反斜杠
云函数返回自动带反斜杠 这里建立了如下一个云函数,目的是当APP过来请求的时候响应支持的版本号: use strict; function main_ret(status,code){let ret {status: status,error: code};return JSON.stringify(ret); } exports.main_handler async (event, context) > {/…...
大模型知识学习
大模型训练过程 数据清洗 拟人化描述:知识库整理 预训练 拟人化描述:知识学习可以使用基于BERT预训练模型进行训练 指令微调 拟人化描述:实际工作技能学习实际操作:让大模型模仿具体的输入输出进行拟合,即模仿学…...
JAVA声明数组
一、声明并初始化数组 直接初始化:在声明数组的同时为其分配空间并初始化元素。 int[] numbers {1, 2, 3, 4, 5}; 动态初始化:先声明数组,再为每个元素分配初始值。 double[] decimals;decimals new double[5]; // 分配空间,但…...
VBA通过Range对象实现Excel的数据写入
前言 本节会介绍通过VBA中的Range对象,来实现Excel表格中的单元格写入、区域范围写入,当然也可以写入不同类型的数据,如数值、文本、公式,以及实现公式下拉自动填充的功能。 一、单元格输入数据 1.通过Value方法实现输入不同类型…...
记录OSPF配置,建立邻居失败的过程
1.配置完ospf后,在路由表中不出现ospf相关信息 [SW2]ospf [SW2-ospf-1]are [SW2-ospf-1]area 0 [SW2-ospf-1-area-0.0.0.0]net [SW2-ospf-1-area-0.0.0.0]network 0.0.0.0 Jul 4 2024 22:11:58-08:00 SW2 DS/4/DATASYNC_CFGCHANGE:OID 1.3.6.1.4.1.2011.5.25 .1…...
算法体系-25 第二十五节:窗口内最大值或最小值的更新结构
一 滑动窗口设计知识点 滑动窗口是什么? 滑动窗口是一种想象出来的数据结构: 滑动窗口有左边界L和有边界R 在数组或者字符串或者一个序列上,记为S,窗口就是S[L..R]这一部分 L往右滑意味着一个样本出了窗口,R往右滑意味…...
等保2.0中还有哪些针对云计算的安全要求?
等保2.0中针对云计算的安全要求概述 等保2.0是中国信息安全等级保护制度的升级版,它对云计算环境提出了一系列特定的安全要求,以确保云服务的安全性和合规性。以下是一些关键的云计算安全扩展要求: 基础设施位置:要求云计算基础…...
数组与 ArrayList 的区别是什么?
在Java中,数组和ArrayList都是非常常见的数据结构,但它们在使用场景、特点和功能上各有千秋。 理解它们的不同,对于初级Java工程师来说,是提升编程技能的一个重要环节。 下面,我将以一种简单明了的方式,对…...
华为OD机考题(HJ50 四则运算)
前言 经过前期的数据结构和算法学习,开始以OD机考题作为练习题,继续加强下熟练程度。 描述 输入一个表达式(用字符串表示),求这个表达式的值。 保证字符串中的有效字符包括[‘0’-‘9’],‘’,‘-’, ‘*’,‘/’ …...
SpringBoot实现文章点赞功能
提示:今日是2024年的6月30日,未来的你看到这篇文章,希望你依旧快乐 文章目录 前言 首先在这里前缀部分我就不做要求了,比如说登录信息什么的 数据库表格 这里实现点赞功能,主要是围绕论坛项目完成的 user_info代表用户信息表 for…...
产品经理系列1—如何实现一个电商系统
具体笔记如下,主要按获客—找货—下单—售后四个部分进行模块拆解...
7.4.分块查找
一.分块查找的算法思想: 1.实例: 以上述图片的顺序表为例, 该顺序表的数据元素从整体来看是乱序的,但如果把这些数据元素分成一块一块的小区间, 第一个区间[0,1]索引上的数据元素都是小于等于10的, 第二…...
RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...
HTML 列表、表格、表单
1 列表标签 作用:布局内容排列整齐的区域 列表分类:无序列表、有序列表、定义列表。 例如: 1.1 无序列表 标签:ul 嵌套 li,ul是无序列表,li是列表条目。 注意事项: ul 标签里面只能包裹 li…...
拉力测试cuda pytorch 把 4070显卡拉满
import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试,通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小,增大可提高计算复杂度duration: 测试持续时间(秒&…...
图表类系列各种样式PPT模版分享
图标图表系列PPT模版,柱状图PPT模版,线状图PPT模版,折线图PPT模版,饼状图PPT模版,雷达图PPT模版,树状图PPT模版 图表类系列各种样式PPT模版分享:图表系列PPT模板https://pan.quark.cn/s/20d40aa…...
重启Eureka集群中的节点,对已经注册的服务有什么影响
先看答案,如果正确地操作,重启Eureka集群中的节点,对已经注册的服务影响非常小,甚至可以做到无感知。 但如果操作不当,可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...
基于Java+MySQL实现(GUI)客户管理系统
客户资料管理系统的设计与实现 第一章 需求分析 1.1 需求总体介绍 本项目为了方便维护客户信息为了方便维护客户信息,对客户进行统一管理,可以把所有客户信息录入系统,进行维护和统计功能。可通过文件的方式保存相关录入数据,对…...
C++.OpenGL (20/64)混合(Blending)
混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...
6个月Python学习计划 Day 16 - 面向对象编程(OOP)基础
第三周 Day 3 🎯 今日目标 理解类(class)和对象(object)的关系学会定义类的属性、方法和构造函数(init)掌握对象的创建与使用初识封装、继承和多态的基本概念(预告) &a…...
GraphQL 实战篇:Apollo Client 配置与缓存
GraphQL 实战篇:Apollo Client 配置与缓存 上一篇:GraphQL 入门篇:基础查询语法 依旧和上一篇的笔记一样,主实操,没啥过多的细节讲解,代码具体在: https://github.com/GoldenaArcher/graphql…...
