SpringBoot跨系统单点登陆的实现

article/2025/9/3 19:46:17

什么是单点登陆

单点登录(英语:Single sign-on,缩写为 SSO),又译为单一签入,一种对于许多相互关连,但是又是各自独立的软件系统,提供访问控制的属性。当拥有这项属性时,当用户登录时,就可以获取所有系统的访问权限,不用对每个单一系统都逐一登录。这项功能通常是以轻型目录访问协议(LDAP)来实现,在服务器上会将用户信息存储到LDAP数据库中。相同的,单一退出(single sign-off)就是指,只需要单一的退出动作,就可以结束对于多个系统的访问权限。

单点登陆带来的好处

  1. 降低访问第三方网站的风险(不存储用户密码,或在外部管理)
  2. 减少因不同的用户名和密码组合而带来的密码疲劳
  3. 减少为相同的身份重新输入密码所花费的时间
  4. 因减少与密码相关的调用IT服务台的次数而降低IT成本

单点登陆技术

现在很多语言都拥有自己的单点登陆实现方案,本次案例中我们用SpringBoot Oauh2来实现跨系统的单点登陆

单点登陆 流程

你的项目可能有很多个模块,如订单管理、商户管理、会员管理、财务管理,这些都是需要登陆后才能访问,当我只要登陆一次,其它的系统都能访问。

在这里插入图片描述

在这里插入图片描述
ps这张图网上找的,也是最清晰描述单点登陆的流程,如上图就是最基本的单点登陆流程。

oauth2 的四种模式:
密码模式(resource owner password credentials)
授权码模式(authorization code)
简化模式(implicit)
客户端模式(client credentials)

我们一般都用授权码模式 这个模式用的人也最多。

这几种模式如果想要了解的更清楚可以看阮一峰老师的oauth2
阮一峰老师的oauth2精讲

单点登陆准备工作

首先我们创建一个叫spring_sso_parent 普通的maven工程 作为整个项目的父工程,创建好后,删除src目录,并且修改pom.xml的依赖
spring_sso_parent 父工程的依赖如下:

<?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.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.6.RELEASE</version><relativePath/></parent><groupId>cn.com.scitc</groupId><artifactId>spring_sso_parent</artifactId><version>1.0-SNAPSHOT</version><packaging>war</packaging><!--  通用配置  --><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version><!-- spring oauth2 版本 --><oauth.version>2.3.6.RELEASE</oauth.version><!-- Spring Security OAuth2 AutoConfigure 版本 --><oauth-auto.version>2.1.6.RELEASE</oauth-auto.version></properties>
</project>

开始编写单点登陆

我们在spring_sso_parent 父工程中 添加一个子模块叫oauth_server的SpringBoot工程,
依赖如下

<?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>cn.com.scitc</groupId><artifactId>spring_sso_parent</artifactId><version>1.0-SNAPSHOT</version></parent><groupId>cn.com.scitc</groupId><artifactId>oauth_server</artifactId><version>0.0.1-SNAPSHOT</version><name>oauth_server</name><packaging>war</packaging><description>this is oauth2 server</description><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.springframework.security.oauth</groupId><artifactId>spring-security-oauth2</artifactId><version>${oauth.version}</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></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>

需要注意的是这里的SpringBoot 版本使用的是父模块的版本

<parent><groupId>cn.com.scitc</groupId><artifactId>spring_sso_parent</artifactId><version>1.0-SNAPSHOT</version>
</parent>

我们在oauth_server 中创建一个config的包,并且创建一个WebSecurityConfig的类

@Configuration
@Order(1)
public class WebSecurityConfig  extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.requestMatchers().antMatchers("/login").antMatchers("/oauth/authorize").and().authorizeRequests().anyRequest().authenticated().and().formLogin().loginPage("/login").permitAll().and().csrf().disable();}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {//使用内存模拟数据库查询的用户auth.inMemoryAuthentication() //内存认证.withUser("admin")//admin 内存认证用户名.password(passwordEncoder().encode("123456"))//被加密的123456密码.roles("ADMIN");//ROLE_ADMIN的角色}@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}
}

这个类使用了两个注解,@Configuration 让这个类成为了一个配置类, @Order(1) 这个注解是优先级,使用优先级来加载。

 http.requestMatchers().antMatchers("/login").antMatchers("/oauth/authorize")

http.requestMatchers() 这个方法下配置的就是security 接收以什么样的请求,我们这里只接受/login和/oauth/authorize的请求 。

 .authorizeRequests().anyRequest().authenticated()

这两句配置的意思是除了以上请求所有的请求都需要身份认证才能访问。

.formLogin().loginPage("/login").permitAll()
.and().csrf().disable();

这几个配置的意思是采用form表单登陆默认登陆页面是/login,任何人都能访问,关闭csrf的保护。

@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {//使用内存模拟数据库查询的用户auth.inMemoryAuthentication().withUser("admin").password(passwordEncoder().encode("123456")).roles("ADMIN");}

这里采用的是AuthenticationManagerBuilder 允许内存验证,这里我添加了一个用户名为admin 密码是 123456,角色是ADMIN的 一个用户 来模拟数据库查询的用户信息。

 @Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}

PasswordEncoder 是Spring 官方提供的一个md5 密码加密器,一般用于密码的加密。

这个就是WebSecurityConfig的配置

下面我们在config中继续创建一个叫OauthServerConfig的类

@Configuration
@EnableAuthorizationServer
public class OauthServerConfig extends AuthorizationServerConfigurerAdapter {@Autowiredprivate PasswordEncoder passwordEncoder;@Overridepublic void configure(final  AuthorizationServerSecurityConfigurer security) throws Exception {security.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");}@Overridepublic void configure(final  ClientDetailsServiceConfigurer clients) throws Exception {clients.inMemory().withClient("handleCilentId")//客户端id.secret(passwordEncoder.encode("secret"))//客户端密钥.authorizedGrantTypes("authorization_code")//授权码模式.scopes("user_info") //授权范围.autoApprove(true)//开启自动授权.redirectUris("http://localhost:8882/login") //认证成功重定向.accessTokenValiditySeconds(10);//设置超时时间}
}

这个类上也使用了两个注解,@Configuration 这个注解成为Spring的一个配置类,@EnableAuthorizationServer 注解是开启授权服务器认证

这个类继承了AuthorizationServerConfigurerAdapter 这个类提供了授权服务器策略。

这里我们实现了两个configure 认证策略方法,分别是AuthorizationServerSecurityConfigurer 和 ClientDetailsServiceConfigurer,
而AuthorizationServerSecurityConfigurer提供了十几个配置的方法,这里我们不会多去深入
其中 tokenKeyAccess意思是:oauth2授权服务器会提供一个/oauth/token_key的url来供资源服务器获取公钥,这个方法就是配置获取公钥的权限范围,它使用的是SpEL表达式且默认不开启, 这里我们使用的是permitAll(),让本身的oauth的访问不需要授权

checkTokenAccess意思是:授权服务器提供一个/oauth/check_token的url来供资源服务器解码令牌,该方法就是配置权限范围,同样使用的是SpEL表达式且默认不开启,我们这里设置的是 isAuthenticated(),检查access_token需要进行授权

当客户端向认证服务器认证的时候,我们需要判断这个客户端是否通过了认证那么就要使用ClientDetailsServiceConfigurer 它提供了三种认证方式

  1. clients.withClientDetails() :使用数据库认证
  2. clients.jdbc(): 传入一个dataSource 这里可以使用自定义的dataSource
  3. clients.inMemory():内存认证 相当于将认证信息 写死

这样我们就将授权服务器配置好了。

下面我们创建一个controller的包
创建一个LoginController 登陆的控制器

@Controller
public class LoginController {@GetMapping("/login")public String loginPage() {return "login";}
}

这里返回的是一个login的 html 页面

login.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>login</title>
</head>
<body>
<h1>标准登陆</h1>
<form action="/auth/login" method="post">username: <input type="text" name="username"/> <br/>password: <input type="password" name="password"/> <br/><button type="submit">登陆</button></form>
</body>
</html>

在创建一个UserInfoController 用于获取认证成功的用户信息

@RestController
public class UserInfoController {private Logger logger = LoggerFactory.getLogger(this.getClass());@RequestMapping("/user")public ResponseEntity<Object> getUser(Principal principal) {logger.info("principal:" + principal);return new ResponseEntity<Object>(principal, HttpStatus.OK);}
}

applicatin.yml 配置

server:port: 8880servlet:context-path: /auth

然后我们创建2个客户端分别是oauth_client1 和 oauth_client2
oauth_client1 的依赖如下:

<?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>cn.com.scitc</groupId><artifactId>spring_sso_parent</artifactId><version>1.0-SNAPSHOT</version></parent><groupId>cn.com.scitc</groupId><artifactId>oauth_clinet1</artifactId><version>0.0.1-SNAPSHOT</version><packaging>war</packaging><name>oauth_clinet1</name><description>this is client1</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.springframework.security.oauth.boot</groupId><artifactId>spring-security-oauth2-autoconfigure</artifactId><version>${oauth-auto.version}</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></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>

同样创建一个config 包 并且创建一个 Oauth2ClientSeurityConfig这个类

@Configuration
@EnableOAuth2Sso
public class Oauth2ClientSeurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().disable() //关闭csrf保护.antMatcher("/**") //使用以任意开头的url.authorizeRequests() // 配置路径拦截,表明路径访问所对应的权限,角色,认证信息.antMatchers("/", "/login**") //控制不同的url接受不同权限的用户访问.permitAll()// 允许所有人访问.anyRequest() .authenticated(); //除了以上请求都需要身份认证}
}

这个类继承了 WebSecurityConfigurerAdapter 这个SpringSecurity的适配器,实现了HttpSecurity 的 configure 方法。 这个类也是两个注解 @Configuration 成为一个配置类,
@EnableOAuth2Sso 启用Oauth2的单点登陆。

我们再创建一个controller 包 ,并且创建一个 InfoController

@Controller
public class InfoController {@GetMapping("/getUser")public ResponseEntity<Object> userPage(Principal principal) {//客户端认证成功后返回这个用户信息return  new ResponseEntity<Object>(principal, HttpStatus.OK);}@GetMapping("/")public String indexPage() {return "index";}
}

index.html 页面

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>index</title>
</head>
<body>
<h1>请登录授权</h1>
<a href="/getUser">login</a>
</body>
</html>

application.yml

auth-server: http://localhost:8880/auth
server:port: 8881servlet:context-path: /security:basic:enabled: falseoauth2:client:clientId: handleCilentIdclientSecret: secretaccessTokenUri: ${auth-server}/oauth/tokenuserAuthorizationUri: ${auth-server}/oauth/authorizeresource:userInfoUri: ${auth-server}/user
spring:thymeleaf:cache: false

auth-server:是目标认证服务器
clientId: 目标认证服务器设置的客户端id
clientSecret: 目标认证服务器设置的密码
accessTokenUri:从目标认证服务器获取令牌token
userAuthorizationUri:从目标认证服务器请求授权默认url是/oauth/authorize
userInfoUri: 从目标认证服务器上将认证信息Principal通过形参绑定的方法通过URL的方式获取用户信息

oauth_client2配置和 oauth_client1是一样的

我们启动 认证服务器oauth_server 和 两个客户端 oauth_client1 和 oauth_client2
chrome 浏览器访问 localhost:8881
在这里插入图片描述

当我们点击login的时候会跳转到认证服务器进行登陆授权

在这里插入图片描述
授权成功后 返回了 这个用户的所有的信息
在这里插入图片描述
我们再去访问localhost:8082
在这里插入图片描述

当我点击登陆的时候 ,并没有出现登陆授权,直接拿到了用户信息
在这里插入图片描述
注意这里我们不管是访问客户端1还是客户端2 ,还是n多个客户端,只要有一个授权成功 那么访问其它的客户端就不需要登陆 就能访问相关的rest服务了。

总结

oauth2 实现的单点登陆 并不是很复杂,归根结底,Spring帮我们做了太多的底层实现,让我们实现起来非常的简单 实现几个接口就可以搞定授权服务器的配置,客户端的配置也非常的简单。现在我们流行看到了如百度,我登陆了百度,那么就可以直接访问百度贴吧,百度糯米,等。单点登陆运用已经使用的非常的广泛。所以我认为掌握单点登陆是十分有必要的。

源码地址

github


http://chatgpt.dhexx.cn/article/UmLT3dG6.shtml

相关文章

单点登陆的实现

王昱 yuwang881gmail.com 博客地址 http://yuwang881.blog.sohu.com 摘要 &#xff1a;单点登录&#xff08; SSO &#xff09;的技术被越来越广泛地运用到各个领域的软件系统当中。本文从业务的角度分析了单点登录的需求和应用领域&#xff1b;从技术本身的角度分析了单点…

CAS 单点登陆

一、Tomcat配置SSL 1. 生成 server key 以命令方式换到目录%TOMCAT_HOME%,在command命令行输入如下命令&#xff1a; keytool -genkey -alias tomcat_key -keyalg RSA -storepass changeit -keystore server.keystore -validity 3600 用户名输入域名&#xff0c;如localhos…

单点登陆的测试

今天做了个单点登陆 。 但是怎么测试呢&#xff1f; 下面请看详解&#xff1a; 源码中是这样的&#xff1a; /*** 单点登录改造* * param request* param response* return* throws IOException* throws HttpException* throws IOException*/RequestMapping(value "/rcbS…

LINUX单点登陆

1.在grub引导界面(如下图)按e进入编辑模式 2.按↓键&#xff0c;找到以linux16开头的行&#xff0c;在最后加上 rd.break(如下图&#xff0c;注意前面有一个空格) 3.按Ctrlx进入救援模式 4.重新挂载/sysroot为可读写模式&#xff0c;并切换根目录为/sysroot # mount -o remou…

java实现单点登陆(SSO)

java实现单点登陆&#xff08;SSO&#xff09; 网络域名必须完全一致&#xff0c;才代表同一站点。 域名映射 &#xff1a;访问后面的 会跳转到前面 单点登陆概念&#xff1a; 多系统&#xff0c;单一位置登录&#xff0c;实现多系统同时登陆。常出现在互联网和企业级平台中。…

OAuth2:单点登陆客户端

基于EnableOAuth2Sso实现 前面我们将验证服务器已经搭建完成了&#xff0c;现在我们就来实现一下单点登陆吧&#xff0c;SpringCloud为我们提供了客户端的直接实现&#xff0c;我们只需要添加一个注解和少量配置即可将我们的服务作为一个单点登陆应用&#xff0c;使用的是第四种…

单点登陆概述

概述 什么是单点登陆 单点登陆&#xff08;single sign on&#xff09;&#xff0c;简称SSO&#xff0c;是比较流行的企业业务整合的解决方案之一。SSO的定义是在多个应用系统中&#xff0c;用户只需要登陆一次就可以访问所以相互信任的应用系统。 单点登陆的实现方案 一般…

怎么做登陆(单点登陆)功能?

登陆是系统最基础的功能之一。这么长时间了&#xff0c;一直在写业务&#xff0c;这个基础功能反而没怎么好好研究&#xff0c;都忘差不多了。今天没事儿就来撸一下。 以目前在接触的一个系统为例&#xff0c;来分析一下登陆该怎么做。 简单上个图&#xff08;有水印。因为穷所…

SSO单点登陆

1 SSO简介 1.1 什么是SSO 单点登录(SingleSignOn&#xff0c;SSO)&#xff0c;在多个应用系统中&#xff0c;用户只需一次登录就可以访问所有相互信任的应用系统。单点登录常用的协议包括 CAS、OAuth、OpenID Connect、SAML。 例如&#xff1a;百度旗下有很多的产品&#xff…

sso单点登陆实现

多系统实现单点登录方案&#xff1a;SSO 单点登录 一、什么是单点登录SSO&#xff08;Single Sign-On&#xff09; SSO是一种统一认证和授权机制&#xff0c;指访问同一服务器不同应用中的受保护资源的同一用户&#xff0c;只需要登录一次&#xff0c;即通过一个应用中的安全验…

单点登陆和无状态登陆

很多人都听说过单点登陆。今天我们来说说什么是单点登陆和无状态登陆。 传统的项目都是使用session来验证登陆&#xff0c;但是在分布式项目中使用session是不行的。因为每个服务都是一个独立的项目&#xff0c;那么我们将服务拆分&#xff0c;肯定会有一个登陆的模块。如果将用…

windows文件句柄修改

找到如下注册表分支&#xff1a; HKEY_LOCAL_MACHINE – SOFTWARE – – Microsoft – – – Windows NT – – – – CurrentVersion – – – – – Windows 在右侧窗格中可以看到名为“GDIProcessHandleQuota”与“USERProcessHandleQuota”的注册表项; GDIProcessHandleQuo…

Linux 查看文件句柄信息

查看系统的最大文件句柄数和文件句柄的使用者PID ulimit -n查看当前系统的最大句柄数显示如下 ulimit命令详解 ulimit -HSn x设置当前系统的文件句柄数为x 以上命令中&#xff0c;H指定了硬性大小&#xff0c;S指定了软性大小&#xff0c;n表示设定单个进程最大的打开文件句柄…

Windows查看文件句柄

2019独角兽企业重金招聘Python工程师标准>>> 图形界面方式 打开任务管理器 2. 性能tab,点击链接打开资源监视器; 3. 现在cpu tab,关联的句柄后面的输入框可以输入你要搜索的文件路径,可模糊匹配; 命令方式 Windows系统本身并不内置命令查看句…

linux文件句柄数

1、问题阐述&#xff1a; too many open files&#xff1a;顾名思义即打开过多文件数。 不过这里的files不单是文件的意思&#xff0c;也包括打开的通讯链接(比如socket)&#xff0c;正在监听的端口等等&#xff0c;所以有时候也可以叫做句柄(handle)&#xff0c;这个错误通常…

windows 查看打开的文件句柄

经常当我们删除文件时&#xff0c;有时会提示【操作无法完成&#xff0c;因为文件已在另一个程序中打开&#xff0c;请关闭该文件并重试】&#xff0c; 这个时候可以资源监控器进行查看运行的进程打开的句柄列表。 具体结果如下显示&#xff1a;

Linux下文件句柄

Too many open files 如果Java打开文件的时候&#xff0c;没有关闭IO流&#xff0c;那么打开到一定数量&#xff0c;在Linux下就会抛出Too many open files的异常。 public static class HoldIOTask implements Runnable {Overridepublic void run() {int count0;try {while(t…

【Linux】文件句柄说明

Linux 文件句柄说明 Linux中所有的事物或资源都是以文件的形式存在&#xff0c;比如消息、共享内存、连接等&#xff0c;句柄可以理解为指向这些文件的指针。 对于这些句柄&#xff0c;Linux是有数量限制的&#xff0c;单个进程默认可以打开的句柄数上限&#xff0c;可以用以…

windows 文件句柄查询

windows经常出现删除/剪切文件时提示文件被占用的问题 打开任务管理器&#xff0c; 选择“性能”选项卡 2. 打开资源管理器&#xff0c; 在关联句柄查询框中输入关键字&#xff0c;过滤查询&#xff0c;然后右键选中对应项&#xff0c;结束任务即可释放句柄

文件句柄

一、文件句柄是什么&#xff1f; 对象在内存中是经常来回移动的&#xff0c;如何快速定位找到这个对象呢&#xff1f; 句柄说&#xff1a; “我来帮你找” windows系统给出的方案 1.进程创建时&#xff0c;windows系统为进程构造了一个句柄表 2.当该进程希望获得一个内核对…