SpringCloud搭建微服务之Gateway+Jwt实现统一鉴权

news/2024/5/8 3:50:21/文章来源:https://blog.csdn.net/liu320yj/article/details/129743499

1. 概述

在微服务项目中,需要对整个微服务系统进行权限校验,通常有两种方案,其一是每个微服务各自鉴权,其二是在网关统一鉴权,第二种方案只需要一次鉴权就行,避免了每个微服务重复鉴权的麻烦,本文以网关统一鉴权为例介绍如何搭建微服务鉴权项目。
本文案例中共有四个微服务模块,服务注册中心、网关服务、鉴权服务和业务提供者
案例中使用组件版本号如下:

组件版本
JDK11
SpringBoot2.7.9
SpringCloud2021.0.6
Mybatis-Plus3.5.3.1
jjwt0.11.5

2. 鉴权微服务

新建一个SpringBoot项目,命名为springcloud-auth-server

2.1. 引入核心依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>${mybatis-plus.version}</version>
</dependency>
<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-sleuth</artifactId>
</dependency>
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api</artifactId><version>${jjwt.version}</version>
</dependency>
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-impl</artifactId><version>${jjwt.version}</version>
</dependency>
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-jackson</artifactId><version>${jjwt.version}</version>
</dependency>
<dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope>
</dependency>

2.2. 编写JWT业务类

@Service
public class JwtService {private static final String SECRET = "JOE38R39GNGRTU49Y534YNIGEYR534YNDEUR7964GEUR735";public void validateToken(final String token) {Jwts.parserBuilder().setSigningKey(getSignKey()).build().parseClaimsJws(token);}public String generateToken(String username) {Map<String, Object> claims = new HashMap<>();return createToken(claims, username);}private String createToken(Map<String, Object> claims, String username) {return Jwts.builder().setClaims(claims).setSubject(username).setIssuedAt(new Date(Instant.now().toEpochMilli())).setExpiration(new Date(Instant.now().toEpochMilli() + 1000 * 30 * 60)).signWith(getSignKey(), SignatureAlgorithm.HS256).compact();}private Key getSignKey() {byte[] keyBytes = Decoders.BASE64.decode(SECRET);return Keys.hmacShaKeyFor(keyBytes);}
}

2.3. 编写配置类

@Configuration
@EnableWebSecurity
public class AuthConfig {@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http.csrf().disable().authorizeHttpRequests().antMatchers("/auth/register", "/auth/token", "/auth/validate").permitAll();return http.build();}@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}@Beanpublic UserDetailsService userDetailsService() {return new CustomUserDetailsService();}@Beanpublic AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {return configuration.getAuthenticationManager();}@Beanpublic AuthenticationProvider authenticationProvider() {DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();authenticationProvider.setUserDetailsService(userDetailsService());authenticationProvider.setPasswordEncoder(passwordEncoder());return authenticationProvider;}
}

2.4. 编写Security用户认证

新建CustomUserDetails类实现UserDetails接口

public class CustomUserDetails implements UserDetails {private String username;private String password;public CustomUserDetails(UserCredential userCredential) {this.username = userCredential.getUsername();this.password = userCredential.getPassword();}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return null;}@Overridepublic String getPassword() {return password;}@Overridepublic String getUsername() {return username;}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}
}

编写CustomUserDetailsService类实现UserDetailsService接口

@Service
public class CustomUserDetailsService implements UserDetailsService {@Autowiredprivate AuthService authService;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {Optional<UserCredential> credential = authService.findUserByUsername(username);return credential.map(CustomUserDetails::new).orElseThrow(() -> new UsernameNotFoundException("user not found"));}
}

说明:文中用到的用户认证类UserCredential只有用户名和密码字段,实体类、持久层接口和业务类接口都比较简单,文中就不一一列举

2.5. 编写权限Controller类

@RestController
@RequestMapping(value = "/auth")
public class AuthController {@Autowiredprivate AuthService authService;@Autowiredprivate PasswordEncoder passwordEncoder;@Autowiredprivate JwtService jwtService;@Autowiredprivate AuthenticationManager authenticationManager;@PostMapping(value = "/register")public ResponseEntity createUser(@RequestBody UserCredential credential) {credential.setPassword(passwordEncoder.encode(credential.getPassword()));authService.save(credential);return ResponseEntity.status(HttpStatus.CREATED).build();}@PostMapping(value = "/token")public ResponseEntity<String> generateToken(@RequestBody AuthRequest authRequest) {final Authentication authenticate = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(authRequest.getUsername(), authRequest.getPassword()));if (authenticate.isAuthenticated()) {final String token = jwtService.generateToken(authRequest.getUsername());return ResponseEntity.status(HttpStatus.OK).body(token);} else {throw new RuntimeException("invalid access");}}@GetMapping(value = "/validate")public ResponseEntity validateToken(@RequestParam String token) {jwtService.validateToken(token);return ResponseEntity.status(HttpStatus.ACCEPTED).build();}
}

3. 网关微服务

新建一个SpringBoot项目,命名为springcloud-gateway

3.1. 引入核心依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api</artifactId><version>${jjwt.version}</version>
</dependency>
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-impl</artifactId><version>${jjwt.version}</version>
</dependency>
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-jackson</artifactId><version>${jjwt.version}</version>
</dependency>

3.2. 编写权限过滤器类

@Component
public class AuthenticationFilter extends AbstractGatewayFilterFactory<AuthenticationFilter.Config> {@Autowiredprivate RouteValidator validator;public AuthenticationFilter() {super(Config.class);}@Overridepublic GatewayFilter apply(Config config) {return ((exchange, chain) -> {if (validator.isSecured.test(exchange.getRequest())) {if (!exchange.getRequest().getHeaders().containsKey(HttpHeaders.AUTHORIZATION)) {throw new RuntimeException("missing authorization header");}String authHeader = exchange.getRequest().getHeaders().get(HttpHeaders.AUTHORIZATION).get(0);if (null != authHeader && authHeader.startsWith("Bearer ")) {authHeader = authHeader.substring(7);}try {JwtUtil.validateToken(authHeader);} catch (Exception e) {e.printStackTrace();throw new RuntimeException("un authorized access to application");}}return chain.filter(exchange);});}public static class Config {}
}

3.3. 编写application.yml配置

spring:application:name: CLOUD-GATEWAYcloud:gateway:discovery:locator:enabled: trueroutes:- id: provider_routhuri: lb://CLOUD-PROVIDER-SERVERpredicates:- Path=/provider/server/**filters:- AuthenticationFilter- id: auth_routhuri: lb://CLOUD-AUTH-SERVERpredicates:- Path=/auth/**

4. 测试

依次启动注册中心服务、网关服务、鉴权服务和业务提供服务
postman发起post请求http://localhost:8000/auth/token获取token
测试获取token
postman发起get请求http://localhost:8000/provider/server/info,将上面获取的token携带上,如下配置
验证token
Type类型选择No Auth,再次发起请求
验证无token访问
查看后台日志会发现是因为没有token
异常日志
说明:本文只是简单实现了gateway实现统一鉴权功能,有些地方还需要小伙伴自行优化,例如没有token异常提示可以返回给前端

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_275838.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

服务端测试知识汇总

目录 服务端测试思想 经济学⻆度 ⾦字塔模型 技术⻆度 HTTP协议 三次握⼿ HTTP完整请求 通信模式 URI信息 请求⽅法 请求状态码 请求/响应头 常⽤请求数据格式 COOKIE请求流程 SESSION请求流程 TOKEN请求流程 API测试维度 单接⼝测试 多个接⼝测试 …

【tensorboard】深度学习的日志信息events.out.tfevents文件可视化工具

在用深度学习模型训练完模型后&#xff0c;会有一些events.out.tfevents格式的日志信息文件&#xff0c;如下图&#xff1a; 在这类文件需要用tensorboard进行打开&#xff0c;并且查看训练过程的信息内容。 1. tensorboard安装 pip install tensorboard -i https://pypi.do…

从零开始学Python第06课:循环结构

我们在写程序的时候&#xff0c;极有可能遇到需要重复执行某条指令或某些指令的场景&#xff0c;例如我们需要每隔1秒钟在屏幕上输出一次“hello, world”并持续输出一个小时。如下所示的代码可以完成一次这样的操作&#xff0c;如果要持续输出一个小时&#xff0c;我们就需要把…

shell:简单易明白的变量和引用

目录什么是变量shell的变量类型declare定义变量的类型根据数据类型分类根据作用域分类变量的定义shell 中的引用什么是变量 可以变化的量。本质上讲&#xff0c;变量就是在程序中保存用户数据的一块内存空间&#xff0c;而变量名就是这块内存空间的地址。 shell的变量类型 s…

“先人一步”!从华为P60看手机品牌如何找到新趋势、新玩法、新增量

对大多数人来说&#xff0c;换新手机是一件充满新鲜感的事&#xff0c;新机到手让人兴奋&#xff0c;可更让老蔡这样的科技发烧友们兴奋的是“比别人更快拿上新机”。朋友圈里晒图&#xff0c;一群人向他询问使用体验&#xff0c;总能让他获得一种不错的“尝鲜感”。这种现象&a…

【javaweb】SpringBoot初次体验

工具&#xff1a;idea 创建maven文件 导入依赖&#xff0c;在pom.xml中&#xff08;在spring boot的官方文档找&#xff09; <!-- spring工程中需要继承的父工程 --><parent><groupId>org.springframework.boot</groupId><artifactId>spring-b…

F1-F7快恢复二极管 SOD-123 1A 50V~1000V

之前东沃电子&#xff08;DOWOSEMI&#xff09;科普过快恢复二极管ES1A-ES1J&#xff1a;SMA封装、正向平均电流1A、最大反向恢复时间35ns、型号齐全&#xff0c;具体型号有&#xff1a;ES1A、ES1B、ES1C、ES1D、ES1E、ES1G、ES1H、ES1J&#xff0c;所对应的工作峰值反向电压分…

composer详解

一.composer简介什么是ComposerComposer 是 PHP 的一个依赖管理工具&#xff0c;它涉及 "packages" 和 "libraries",简单的说就是我们的项目通常会使用其它代码工具库&#xff0c;这时仅仅是在项目中申明依赖哪些代码工具库&#xff0c;它在每个项目的基础…

基于JavaWeb+jsp实现企业员工工资管理系统

一、项目简介 本项目是一套基于ServletJsp实现的学生成绩管理系统&#xff0c;主要针对计算机相关专业的正在做bishe的学生和需要项目实战练习的Java学习者。 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目可以直接作为bishe使用。 项目都经过严格调试&#xff0…

Android开发工程师想找工作需要掌握哪些

前言 目前互联网行业越来越好&#xff0c;进入这个行业的人员也是越来越多。从开发的角度来看&#xff0c;开发的职位主要分前端&#xff0c;后端&#xff0c;客户端&#xff08;主要分为ios和android开发&#xff09;以及算法工程师等。 Android开发一直是当前互联网行业中最…

playwright--pytest-playwright、pytest-base-url插件编写用例

文章目录前言一、安装插件二、编写用例三、运行用例四、内置fixture五、全局配置base_url前言 官方的 pytest-playwright 插件可以编写端到端测试。它提供上下文隔离&#xff0c;开箱即用地在多个浏览器配置上运行。它继承了pytest框架&#xff0c;以及支持playwright的一些基…

【FPGA-DSP】第一期:DSP基础

目录 1. DSP基础 1.1 DSP基本概念 1.2 FPGA实现DSP的特点 2. DSP硬核的结构与使用 3. FPGA设计DSP技术 3.1. 浮点数与定点数的表示与转换 3.1.1. 双精度浮点数表示 3.1.2. 双精度浮点数与定点数的转换 本章作为FPGA数字信号处理的入门介绍课程&#xff0c;将介绍DSP的…

如何在Windows 10中恢复丢失的分区?

一般来说&#xff0c;未分配的空间不能在分区前直接用于存储数据&#xff0c;因此大多数Windows用户会将硬盘划分为不同的分区以存储各种数据。你可以在Windows安装期间或安装完系统后通过硬盘管理、DiskPart命令或第三方工具创建分区。 在分区丢失后&#xff0c;你会…

C的强符号/弱符号

首先上代码和结果&#xff1a; 代码&#xff1a; #include <stdio.h> int k; int k; int main() {printf("addr of k %p\n", &k);printf("value of k %d\n", k);return 0; }结果&#xff1a; addr of k 00408074 value of k 0问题&…

《辉煌优配》消费医疗加速回暖 数字化新竞赛鸣枪

“栽培牙集采将给口腔职业带来深远影响。”瑞尔齿科副总经理胡云帆3月23日承受证券时报记者采访时表明&#xff0c;资料费用下降将惠及公司运营端&#xff0c;同时激活了一大批存量客户。公司相关产品和服务价格已经开端呈现一些变动的迹象。 栽培牙费用包含栽培体费用、牙冠费…

狗都能看懂的VAE笔记

文章目录自编码器普通Auto-Encoder的问题解决的方法如何运作数学细节生成模型Auto-Encoder一直是一个非常有创造性的方向。期中的VAE变分编码器一直是我没搞懂的部分&#xff0c;在AI绘画突然火起来的时候&#xff0c;不得不搞清楚VAE了。看了很多VAE的讲解&#xff0c;没有良好…

The Shebeen——爱尔兰酒吧 NFT 来袭!

从爱尔兰神像到标志性的爱尔兰帽子&#xff0c;The Shebeen——爱尔兰酒吧 NFT 系列是一系列非常吸引的独特数字资产&#xff0c;体现了爱尔兰的精神和风俗。 The Shebeen NFT 系列均来自 The Shebeen——爱尔兰酒吧游戏体验&#xff0c;3 月 17 日至 3 月 29 日可在 The Sandb…

c/c++开发,无可避免的自定义类类型(篇八).为类妥善处理异常

目录 一、异常简述 1.1 异常是什么 1.2 异常处理概念 二、异常处理 2.1 try……catch异常处理语法 2.2 动态异常说明-throw 2.3 标准异常体系 2.4 try ...catch抛出异常对象的处理 2.5 异常捕获处理级别 2.6 抛出对象方式 2.7 try 块以及处理块内严禁跳转语法使用 2.8 异常捕…

【MySQL高级篇】 第8章_索引的创建与设计原则

第8章_索引的创建与设计原则 1. 索引的声明与使用 1.1 索引的分类 MySQL的索引包括普通索引、唯一性索引、全文索引、单列索引、多列索引和空间索引等。 从 功能逻辑 上说&#xff0c;索引主要有 4 种&#xff0c;分别是普通索引、唯一索引、主键索引、全文索引。 按照 物…

Python如何实现读写txt文件?读写txt文件的方法有哪些?

前言 又是一篇纯知识点的文章&#xff0c;现在看文章的人越来越少了&#xff0c;是都去看视频了吗 今天就来聊聊 - Python实现读写txt文件的方法 一、读写模式&#xff1a; w&#xff1a;向文件中写入内容&#xff0c;w会清空原来文本内容a&#xff1a;向文件中追加内容r&am…