【图文详解】搭建 Spring Authorization oauth2-server-resource-client-gateway-eureka 完整Demo
1、项目概述
1.1、概述
本项目是在前面章节的基础上,进行的升级改造。增加了注册中心、网关,更加贴近于实际需求。
在进行本节之前,请先搭建前面项目:
【图文详解】搭建 Spring Authorization Server + Resource + Client 完整Demo
【oauth2 客户端模式】Spring Authorization Server + Resource + Client 资源服务间的相互访问
1.2、整体架构图
业务流程解析
1.2.1、总体结构
总体上分为三个部分:
- 前端和第三方授权中心
- 网关:把前后两部分隔离开,所有的请求访问都通过网关转发
- 后端:所有的资源服务(微服务)、注册中心、配置中心等
1.2.2、用户访问
1、用户
访问 客户端
,准备请求资源
2、客户端
需要用户认证/授权,并请求 授权中心
发放令牌
3、客户端
拿到令牌后,通过 网关
, 去 注册中心
查找 资源服务
4、注册中心 把资源服务告之 客户端,客户端
携带令牌去访问 资源服务
5、资源服务
验证令牌后,把资源数据,通过 网关
,返回 客户端
,再呈现给 用户
12.3、资源服务间调用
资源服务之间的访问也需要经过网关(图中略),减少服务间的耦合
a、资源服务B 希望调用 资源服务A;资源服务B
通过 网关
,向 授权中心
申请令牌;授权中心 验证ID/密钥(客户端模式)后,通过 网关
,直接发放令牌给 资源服务B
b、资源服务B
通过 网关
,向 注册中心
查找 资源服务A
c、注册中心 把 资源服务A,再通过 网关 ,告之 资源服务B,资源服务B
携带令牌访问 资源服务A
d、资源服务A
验证令牌后,把资源数据,通过 网关 ,返给 资源服务B
1.2.4、额外说明
- 所有资源服务 都需要注册到 注册中心,访问者只需要知道 网关地址 和 服务名称,就可以访问;不用关心资源服务的具体部署位置,不管是单机部署还是集群部署,对访问者来说都是一样的。
- 网关 是前后端访问的桥梁,网关也可以注册到注册中心;所有的访问都应该经过网关转发,减少服务间的耦合
- 授权中心 作为第三方存在,不应该注册到注册中心
- 客户端 作为工程的前端,也不用注册到注册中心
1.3、搭建环境
- Spring Security 5.6.3 (Client/Resource)
- Spring Authorization Server 0.2.3
- Spring Boot 2.6.7(gateway、eureka…)
- jdk 1.8
- mysql 5.7
- lombok、log4j、fastjson2 …
2、结构搭建
模块 | 端口 | 说明 |
---|---|---|
oauth2-server-resource-client-gateway-eureka | — | 父工程 |
oauth2-client-8000 | 8000 | 项目前端(oauth2客户端) |
oauth2-server-9000 | 9000 | 认证授权中心(oauth2服务端) |
oauth2-resource-a-8001 | 8001 | 微服务A(oauth2资源服务器),受保护对象 |
oauth2-resource-b-8002 | 8002 | 微服务B(oauth2资源服务器),受保护对象 |
eureka-7000 | 7000 | 注册中心 |
gateway-9999 | 9999 | 网关 |
2.1、父工程
创建普通meven工程 oauth2-server-resource-client-gateway-eureka
;打包格式pom
,删除 src
- pom.xml
<?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><groupId>com.tuwer</groupId><artifactId>oauth2-server-resource-client-gateway-eureka</artifactId><version>1.0-SNAPSHOT</version><packaging>pom</packaging><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><mysql-connector-java.version>8.0.29</mysql-connector-java.version><lombok.version>1.18.22</lombok.version><log4j.version>1.2.17</log4j.version><fastjson2.version>2.0.3</fastjson2.version><commons-lang.version>2.6</commons-lang.version></properties><dependencyManagement><dependencies><!--spring-cloud-dependencies--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>2021.0.0</version><type>pom</type><scope>import</scope></dependency><!-- spring-boot-dependencies--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>2.6.7</version><type>pom</type><!--<scope>provided</scope>--><scope>import</scope></dependency><!-- Spring Security OAuth2 依赖 --><!-- 授权服务器 Spring Authorization Server--><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-oauth2-authorization-server</artifactId><version>0.2.3</version></dependency><!-- mysql-connector-java --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>${mysql-connector-java.version}</version></dependency><!--fastjson--><dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2</artifactId><version>${fastjson2.version}</version></dependency><!--lombok--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>${lombok.version}</version></dependency><!--日志--><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>${log4j.version}</version></dependency><!-- StringUtils --><dependency><groupId>commons-lang</groupId><artifactId>commons-lang</artifactId><version>${commons-lang.version}</version></dependency></dependencies></dependencyManagement></project>
2.2、子模块
3、注册中心
3.1、pom.xml
<?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"><parent><artifactId>oauth2-server-resource-client-gateway-eureka</artifactId><groupId>com.tuwer</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>eureka-7000</artifactId><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target></properties><dependencies><!-- spring-cloud-starter-netflix-eureka-server --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId></dependency><!-- 热部署 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>compile</scope></dependency></dependencies>
</project>
3.2、application.yml
server:port: 7000spring:application:# 注册中心name: eureka-server-7000# Eureka配置
eureka:instance:# Eureka服务端的实例名字hostname: localhostclient:# 表示是否向 Eureka 注册中心注册自己 (这个模块本身是服务器,所以不需要)register-with-eureka: false# fetch-registry 是否拉取其他的服务;如果为 false,则表示自己为注册中心或服务提供者;服务消费者的话为 truefetch-registry: false# Eureka监控页面~service-url:defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
3.3、启动类
package com.tuwer;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;/*** @author 土味儿* Date 2022/5/21* @version 1.0*/
@SpringBootApplication
@EnableEurekaServer
public class EurekaServer_7000 {public static void main(String[] args) {SpringApplication.run(EurekaServer_7000.class, args);}
}
4、网关
4.1、pom.xml
<?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"><parent><artifactId>oauth2-server-resource-client-gateway-eureka</artifactId><groupId>com.tuwer</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>gateway-9999</artifactId><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target></properties><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><!-- actuator完善监控信息 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><!-- 热部署 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>compile</scope></dependency></dependencies>
</project>
4.2、application.yml
server:port: 9999spring:application:# 网关应用名称name: gatewey-9999# 配置 Spring Cloud 相关属性cloud:# 配置 Spring Cloud Gateway 相关属性gateway:# 配置网关发现机制discovery:# 配置处理机制locator:# ----------------# 只要请求地址符合规则:http://网关地址:端口/微服务名称/微服务请求地址,就自动映射# 把请求转发到:http://微服务提供者地址:端口/微服务名称/微服务请求地址# ----------------# 开启网关自动映射处理机制# 商业开发中,一般不设置为 true,使用默认值 false;避免不必要的自动转发规则enabled: false# 开启服务名称小写转换(默认为false)lower-case-service-id: true# 配置 Eureka
eureka:client:# 默认值:true 需要从注册中心拉取其他的服务#fetch-registry: trueservice-url:# 注册中心地址defaultZone: http://localhost:7000/eureka/instance:# 修改Eureka上的默认描述信息instance-id: gatewey-9999# 以IP地址注册到服务中心prefer-ip-address: true# 监控端口配置
management:endpoints:web:exposure:# 开启 info,health;新版本中只默认开启了 healthinclude: info,health
4.3、启动类
package com.tuwer;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;/*** @author 土味儿* Date 2022/5/21* @version 1.0*/
@SpringBootApplication
@EnableEurekaClient
public class Gateway_9999 {public static void main(String[] args) {SpringApplication.run(Gateway_9999.class, args);}
}
4.4、MyInfo.java
这一步可以省略;作用:给外部一个查看该模块基本信息的接口;
- 需要导入
actuator
依赖- application.yml 中配置
- 如果有security安全设置,需要放开端口
/actuator/info
@Component
public class MyInfo implements InfoContributor {@Overridepublic void contribute(Info.Builder builder) {HashMap<String, Object> map = new HashMap<>();// 可以从数据库获取信息map.put("ServiceName","路由网关");map.put("version","1.0-SNAPSHOT");map.put("author","tuwer");builder.withDetails(map);}
}
# 监控端口配置
management:endpoints:web:exposure:# 开启 info,health;新版本中只默认开启了 healthinclude: info,health
5、整合OAuth2三元素
5.1、复制旧项目中OAuth2三元素模块
5.2、配置资源服务到注册中心等
以
oauth2-resource-a-8001
为例,其它资源模块操作方法一样;
- 授权中心不需要添加到注册中心,它作为第三方存在
- 客户端作为工程前端,也不用添加到注册中心
5.2.1、添加依赖
<!-- 注册中心客户端 -->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
5.2.2、配置注册中心
# 配置 Eureka
eureka:client:# 默认值:true 需要从注册中心拉取其他的服务#fetch-registry: trueservice-url:# 注册中心地址defaultZone: http://localhost:7000/eureka/instance:# 修改Eureka上的默认描述信息instance-id: oauth2-resource-a-8001# 以IP地址注册到服务中心prefer-ip-address: true
5.2.3、启动类添加 @EnableEurekaClient
@SpringBootApplication
@EnableEurekaClient
public class Resource_a_8001 {public static void main(String[] args) {SpringApplication.run(Resource_a_8001.class, args);}
}
5.2.4、配置MyInfo.java
这一步可以省略;作用:给外部一个查看该模块基本信息的接口;
- 导入依赖
<!-- actuator完善监控信息 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- MyInfo.java
@Component
public class MyInfo implements InfoContributor {@Overridepublic void contribute(Info.Builder builder) {HashMap<String, Object> map = new HashMap<>();// 可以从数据库获取信息map.put("ServiceName","资源服务器A");map.put("version","1.0-SNAPSHOT");map.put("author","tuwer");builder.withDetails(map);}
}
- application.yml
# 监控端口配置
management:endpoints:web:exposure:# 开启 info,health;新版本中只默认开启了 healthinclude: info,health
- 安全策略中放行端点
// 第一种方法
@Bean
SecurityFilterChain oauth2SecurityFilterChain(HttpSecurity http) throws Exception {http.authorizeRequests(requests ->// 任何请求都需要认证requests.antMatchers("/actuator/**").permitAll().anyRequest().authenticated())// ...return http.build();
}
// 第二种方法
@Bean
WebSecurityCustomizer webSecurityCustomizer() {return web -> web.ignoring().antMatchers("/actuator/health", "/actuator/info");
}
5.3、配置网关中路由规则
在
spring.cloud.gateway.routes
节点下配置
- 资源服务A
# 资源服务A# 把对 【网关/o2_resource_a/**】 的请求,转发到 【lb://oauth2-resource-a-8001】# 转发时去掉第1节 o2_resource_a# lb: 表示负载均衡 loadbalance# oauth2-resource-a-8001 是【资源服务A】在注册中心中的名称- id: oauth2-resource-a-8001uri: lb://oauth2-resource-a-8001predicates:- Path=/o2_resource_a/**- Method=GETfilters:- name: StripPrefixargs:# 过滤掉第1节parts: 1
- 资源服务B
# 资源服务B- id: oauth2-resource-b-8002uri: lb://oauth2-resource-b-8002predicates:- Path=/o2_resource_b/**- Method=GETfilters:- name: StripPrefixargs:# 过滤掉第1节parts: 1
- 客户端
# 客户端- id: oauth2-client-8000uri: http://127.0.0.1:8000predicates:- Path=/o2_client/**- Method=GETfilters:- name: StripPrefixargs:# 过滤掉第1节parts: 1
- 认证中心
# 认证中心- id: oauth2-server-9000uri: http://os.com:9000predicates:- Path=/o2_server/**- Method=GET,POSTfilters:- name: StripPrefixargs:# 过滤掉第1节parts: 1
5.4、旧项目部分内容修改
5.4.1、授权中心
- 修改注册的两个客户端ID和名称;为了与旧项目区分。修改完之后,在客户端引用位置也要相应修改
//String clientId_1 = "my_client";
String clientId_1 = "my_client_2";//String clientId_2 = "micro_service";
String clientId_2 = "micro_service_2";// 客户端名称:可省略
//.clientName("my_client_name")
.clientName("my_client_name_2") //.clientName("micro_service")
.clientName("micro_service_2")
5.4.2、客户端
- application.yml
#client-id: my_client
client-id: my_client_2
ResourceController.java
把对资源的直接调用,改为通过网关调用
// ...
// 网关地址
private String BASE_URL = "http://192.168.62.1:9999";@GetMapping("/server/a/res1")
public String getServerARes1(@RegisteredOAuth2AuthorizedClientOAuth2AuthorizedClient oAuth2AuthorizedClient) {// 网关地址/路由路径...return getServer(BASE_URL + "/o2_resource_a/res1", oAuth2AuthorizedClient);
}
// ...
- 本地模拟客户
/*** 虚拟一个本地用户** @return UserDetailsService*/@BeanUserDetailsService userDetailsService() {return username -> User.withUsername("local_admin").password("123456").roles("TEST","ABC")//.authorities("ROLE_ADMIN", "ROLE_USER").build();}
- 本地权限提升
@Controller
public class IndexController {@AutowiredUserDetailsService userDetailsService;/*** 权限提升* 第三方用户进入本系统后,绑定本地用户,获取本地用户的角色和权限* @param model* @return*/@GetMapping("/")public String user(Model model) {// 从安全上下文中获取登录信息,返回给modelMap<String, Object> map = new HashMap<>(5);Authentication auth = SecurityContextHolder.getContext().getAuthentication();String username = auth.getName();map.put("当前用户", username);map.put("原来权限", auth.getAuthorities());//Set<GrantedAuthority> authorities = new ArraySet<>(auth.getAuthorities());Set<GrantedAuthority> authorities = new HashSet<>(auth.getAuthorities());// 根据三方用户查绑定的本地用户String localUser = getLocalUser(username);UserDetails userDetails = userDetailsService.loadUserByUsername(localUser);map.put("本地用户", localUser);// 本地用户权限//List<GrantedAuthority> authorities1 = new ArrayList<>(userDetails.getAuthorities());Set<GrantedAuthority> authorities1 = new HashSet<>(userDetails.getAuthorities());map.put("本地用户权限", authorities1);// 把本地用户权限加入原来权限集中authorities.addAll(authorities1);map.put("新的权限", authorities);// 生成新的认证信息Authentication newAuth = new OAuth2AuthenticationToken((OAuth2User) auth.getPrincipal(),authorities,"myClient");// 重置认证信息SecurityContextHolder.getContext().setAuthentication(newAuth);model.addAttribute("user", map);return "index";}/*** 模拟通过第三方用户,得到本地用户* @param remoteUsername* @return*/private String getLocalUser(String remoteUsername){String u = "";// 模拟通过三方用户查本地用户if(StringUtils.isNotEmpty(remoteUsername)){u = "local_admin";}return u;}@GetMapping("/out")public void logout(HttpServletRequest request,HttpServletResponse response) {// ...}
}
5.4.3、资源服务器
本项目中需要 资源服务B 调用 资源服务A;只需要修改
资源服务B
ResourceController.java
申请令牌时,认证中心地址可以通过网关转发,地址为:
BASE_URL + '/o2_server/...'
;客户端模式申请令牌时,不需要认证/授权/授权码等步骤,没有redirect_url
转发路径
// ...
// 网关地址
private String BASE_URL = "http://192.168.62.1:9999";@GetMapping("/res1")
public String getRes1(HttpServletRequest request) {//return getServer("http://127.0.0.1:8001/res2", request);return getServer(BASE_URL + "/o2_resource_a/res2", request);//return JSON.toJSONString(new Result(200, "服务B -> 资源1"));
}private String getServer(String url,HttpServletRequest request) {// ...// 对id及密钥加密//byte[] userpass = Base64.encodeBase64(("micro_service:123456").getBytes());byte[] userpass = Base64.encodeBase64(("micro_service_2:123456").getBytes());// ...try {// 发起申请令牌请求responseEntity1 = restTemplate.exchange(BASE_URL + "/o2_server/oauth2/token?grant_type=client_credentials", HttpMethod.POST, httpEntity1, String.class);} catch (RestClientException e) {//System.out.println("令牌申请失败");}// ...
}
6、测式
权限测试
客户端中需要加入本地用户,第三方用户绑定本地用户,再把本地用户角色/权限赋于给第三方用户,实现客户端的角色管理。
详细设计及说明见:OAuth2在分布式微服务架构下基于角色的权限设计(RBAC)
- 资源服务器放开客户端的访问权限,只要是合法的客户端请求都通过
- 客户端根据角色/权限控制访问;修改客户端中安全策略;重启客户端测试
- 添加403页面
Git仓库:https://gitee.com/tuwer/oauth2
【图文详解】搭建 Spring Authorization oauth2-server-resource-client-gateway-eureka 完整Demo相关推荐
- php强类型 vscode,VSCode + WSL 2 + Ruby环境搭建图文详解
vscode配置ruby开发环境 vscode近年来发展迅速,几乎在3年之间就抢占了原来vim.sublime text的很多份额,犹记得在2015-2016年的时候,ruby推荐的开发环境基本上都是 ...
- php mac 开发环境搭建_Mac搭建php的开发环境(图文详解)
搭建php的开发环境(图文详解) 这篇文章主要介绍了Mac下搭建php开发环境教程,Mac OS X内置了Apache 和 PHP,这样使用起来非常方便.本文以Mac OS X 10.6.3为例,需要 ...
- 大数据学习系列之七 ----- Hadoop+Spark+Zookeeper+HBase+Hive集群搭建 图文详解
引言 在之前的大数据学习系列中,搭建了Hadoop+Spark+HBase+Hive 环境以及一些测试.其实要说的话,我开始学习大数据的时候,搭建的就是集群,并不是单机模式和伪分布式.至于为什么先写单 ...
- 基于CentOS6.5下snort+barnyard2+base的入侵检测系统的搭建(图文详解)(博主推荐)...
为什么,要写这篇论文? 是因为,目前科研的我,正值研三,致力于网络安全.大数据.机器学习研究领域! 论文方向的需要,同时不局限于真实物理环境机器实验室的攻防环境.也不局限于真实物理机器环境实验室的大数 ...
- x264代码剖析(一):图文详解x264在Windows平台上的搭建
x264代码剖析(一):图文详解x264在Windows平台上的搭建 X264源码下载地址:http://ftp.videolan.org/pub/videolan/x264/ 平台:win7 PC. ...
- 图文详解如何搭建Windows的Android C++开发环境
原地址:http://www.apkbus.com/android-18595-1-1.html //================================================= ...
- ftp linux包,图文详解Ubuntu搭建Ftp服务器的方法(包成功)
一.今天下午由于课程的要求不得已做了Ubuntu搭建Ftp服务器的实验,但是实验指导书还是N年前的技术,网上搜了一大把,都是模模糊糊的! 在百般困难中终于试验成功,特把经验分给大家 希望大家少走弯路! ...
- pycharm+python3.7+pyqt配置_Python3+Pycharm+PyQt5环境搭建步骤图文详解
搭建环境: 操作系统:Win10 64bit Python版本:3.7 Pycharm:社区免费版 一.Python3.7安装 下载到安装包后打开,如果想安装到默认路径(C盘)的话一直点下一步就可以了 ...
- linux rpm安装zabbix,CentOS 7上安装Zabbix Server 3.0 图文详解
CentOS 7上安装Zabbix Server 3.0 图文详解 1.查看系统信息. cat /etc/RedHat-release CentOS Linux release 7.0.1406 (C ...
最新文章
- Java解决递归栈溢出_方法递归调用中java栈溢出的问题 及 解答 | 学步园
- SQL SERVER 判断是否存在并删除某个数据库、表、视图、触发器、储存过程、函数
- 通知:前blog文章全丢了..
- 两条曲线所围成的面积_三个视频搞定:求曲边梯形面积的思想、微积分基本定理及其几何意义、微积分理论的可视化解读、...
- 【转】Web实现音频、视频通信
- mysql文件_mysql 的各种文件详细说明
- 关于面向对象和面向过程本质的区别(个人感悟)
- Leetcode46. Permutations全排列
- 导入jasperreports出现Cannot resolve com.lowagie:itext:2.1.7.js6异常、生成PDF中文不显示中文解决方法、使用命令安装jar包
- Windows批处理添加注释
- .NET6东西--可写的JSON DOM API
- chm文件打开出现已取消该网页的导航
- abaqus中六面体单元对比四面体
- 没有网服务器怎么打开网页,苹果手机没有浏览器怎么打开网页
- 软件:股票小助手/盯盘小助手!
- java 免登录_最简单、稳定的免密登录
- CTU 2017 B - Pond Cascade 二分
- java 接口,接口的特性,接口实现多态,面向接口编程
- 实验 详解K8S多节点部署群集
- Prometheus Operator 部署