SpringCloud Gateway整合Spring Security Webflux的关键点(痛点解析),及示例项目

本文主要是介绍SpringCloud Gateway整合Spring Security Webflux的关键点(痛点解析),及示例项目,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

最近公司项目需要用到后端的认证、授权,且公司项目目前是基于SpringCloud Gateway的,所以想到都是一家的产品就决定使用Spring Security了。

但是在整合过程中,经历了种种磨难,所以把最终的整合关键点列出来,让需要的读者不用再碰的头破血流了。。。

网上也有基于SpringCloud和Spring Security整合的方案,关键在于我们公司的项目使用的是Gateway,所以和网上现有的方案都不一样(网上很多用的是zuul)!现有的方案都是使用的Spring Security的传统SpringMVC(servlet)整合方案,而不是纯粹的WebFlux的。而SpringCloud Gateway是基于webFlux的,跟SpringMVC传统方式是不兼容的:比如你在里面没法使用HttpServletRequest、HttpServletResponse,和HttpSession。对于WebFlux来讲,使用的是ServerWebExchange和WebSession。区别非常大,有兴趣的自行搜索WebFlux和反应式编程。

所以,按照网上传统的整合方案,先整合出了一版,通过@EnableWebSecurity对Spring Security进行了配置,核心配置类如下:

package com.daybreak.config;import com.daybreak.component_security.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.access.channel.ChannelProcessingFilter;/*** SpringSecurity主配置类*/
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {//security的鉴权排除的url列表private static final String[] excludedAuthPages = {"/auth/login","/auth/logout","/health","/api/socket/**","/**/*.ico"};//自定义的未登录返回JSON信息的处理器(默认httpBasic方式会有弹出框)@Autowiredprivate NotLoginHandler notLoginHandler;//自定义的登录验证器@Autowiredprivate LoginValidateProvider loginValidateProvider;//登录成功时调用的自定义处理类@Autowiredprivate LoginSuccessHandler loginSuccessHandler;//登录失败时调用的自定义处理类@Autowiredprivate LoginFailedHandler loginFailedHandler;//无权限访问被拒绝时的自定义处理器。如不自己处理,默认返回403错误@Autowiredprivate MyAccessDeniedHandler myAccessDeniedHandler;@Overrideprotected void configure(HttpSecurity http) throws Exception {//基础设置http.httpBasic()//配置HTTP基本身份验证.authenticationEntryPoint(notLoginHandler)//自定义的未登录返回JSON信息的处理器(默认httpBasic方式会有弹出框).and().authorizeRequests().antMatchers(excludedAuthPages).permitAll()  //无需进行权限过滤的请求路径.antMatchers(HttpMethod.OPTIONS).permitAll() //option 请求默认放行.anyRequest().authenticated()//其他请求都需要认证.anyRequest().access("@rbacService.hasPermission(request,authentication)")//认证通过后,需要通过鉴权才能继续访问.and().exceptionHandling().accessDeniedHandler(myAccessDeniedHandler)//访问被拒绝时自定义处理器.and().formLogin() //登录表单.usernameParameter("username") //指定前端发过来的用户名字段.passwordParameter("password") //指定前端发过来的密码字段.loginProcessingUrl("/auth/login") //指定前端登录时请求的url.loginPage("http://127.0.0.1:8848/spring_security_pages/login.html")//httpBasic方式认证时,不需要跳转页面了.successHandler(loginSuccessHandler)//成功登录处理器.failureHandler(loginFailedHandler)//失败登录处理器.permitAll();//登录成功后有权限访问所有页面//关闭csrf跨域攻击防御http.csrf().disable();//允许跨域,配置信息详见下面的corsConfigurationSource()http.cors();http.addFilterBefore(new WebSecurityCorsFilter(), ChannelProcessingFilter.class);System.out.println("配置完成!");}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {//这里要设置自定义登录认证器auth.authenticationProvider(loginValidateProvider);}
}

上面代码中所需的“自定义认证”、“自定义鉴权”、“登陆成功、失败“等注入的组件,在网上都能找到相关代码,这个不做赘述。

但是,请注意:它是不起作用的!请不要用这种方式整合!

表现是:编译正常、运行无错误,但是不起作用。

经过各种头破血流的排查(调试、研究源码、上国外网站查资料),终于发现了根本原因所在,就是上面开始所说,SpringCloud Gateway是纯WebFlux的,使用这种传统SpringMVC方式的代码不起作用(关键不报错,坑啊)。

所以,又经过一阵折腾,查看各种资料(国内、国外资料都寥寥无几),加上继续研究Spring Security的源码,终于找到了正确的整合方式。即:全部使用WebFlux的方式,主配置使用的是@EnableWebFluxSecurity。

成功的方案!!!Spring Security的主配置文件如下:

package com.daybreak.xian.gateway.config_security_webflux;import com.daybreak.xian.gateway.component_security_webflux.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;//这个注解是关键!
@EnableWebFluxSecurity
public class SecurityWebFluxConfig {//自定义的鉴权服务,通过鉴权的才能继续访问某个请求@Autowiredprivate MyRBACServiceWebFlux myRBACServiceWebFlux;//无权限访问被拒绝时的自定义处理器。如不自己处理,默认返回403错误@Autowiredprivate MyAccessDeniedHandlerWebFlux myAccessDeniedHandlerWebFlux;//登录成功时调用的自定义处理类@Autowiredprivate LoginSuccessHandlerWebFlux loginSuccessHandlerWebFlux;//登录失败时调用的自定义处理类@Autowiredprivate LoginFailedHandlerWebFlux loginFailedHandlerWebFlux;//成功登出时调用的自定义处理类@Autowiredprivate LogoutSuccessHandlerWebFlux logoutSuccessHandlerWebFlux;//未登录访问资源时的处理类,若无此处理类,前端页面会弹出登录窗口@Autowiredprivate CustomHttpBasicServerAuthenticationEntryPointWebFlux customHttpBasicServerAuthenticationEntryPoint;//security的鉴权排除列表private static final String[] excludedAuthPages = {"/auth/login","/auth/logout","/health","/api/socket/**"};@BeanSecurityWebFilterChain webFluxSecurityFilterChain(ServerHttpSecurity http) throws Exception {http
//                .authenticationManager(myReactiveAuthenticationManager)//自定义登录验证。自动扫描注入,无需手动注入.authorizeExchange().pathMatchers(excludedAuthPages).permitAll()  //无需进行权限过滤的请求路径.pathMatchers(HttpMethod.OPTIONS).permitAll() //option 请求默认放行.and().authorizeExchange().pathMatchers("/**").access(myRBACServiceWebFlux)//自定义的鉴权服务,通过鉴权的才能继续访问某个请求.anyExchange().authenticated().and().httpBasic().and().formLogin().loginPage("/auth/login")//指定登录请求路径.authenticationSuccessHandler(loginSuccessHandlerWebFlux) //认证成功.authenticationFailureHandler(loginFailedHandlerWebFlux) //登陆验证失败.and().exceptionHandling().authenticationEntryPoint(customHttpBasicServerAuthenticationEntryPoint)  //未登录访问资源时的处理类,若无此处理类,前端页面会弹出登录窗口.and().exceptionHandling().accessDeniedHandler(myAccessDeniedHandlerWebFlux)//访问被拒绝时自定义处理器.and() .csrf().disable()//必须支持跨域.logout().logoutUrl("/auth/logout").logoutSuccessHandler(logoutSuccessHandlerWebFlux);//成功登出时调用的自定义处理类return http.build();}
}

对应第一种失败的整合方案,上面代码中所需的“自定义认证”、“自定义鉴权”、“登陆成功、失败“等注入的组件,全部换为了WebFlux版本对应的各种Handler。具体细节这里也不做赘述。

希望对有相同需要的朋友们有所帮助!

 

更新:感谢同志们对我这篇文章的肯定,好多朋友都想要源码,我最近一直比较忙,而且代码在家里机子上放哪一直没找到。然后今天抽时间把整个硬盘搜索了一遍,终于找到了。。。

项目我等下会放到CSDN的资源下载里面。

下面把项目的readme贴出来:

# 项目简介:
1.本项目为SpringCloud Gateway的微服务框架,整合了SpringSecurity,微服务间使用Redis来获取登陆的用户信息。
2.由于Gateway采用的是纯Webflux方式,所以原有的Spring基于传统拦截器、过滤器的方式无法正常使用SpringSecurity。
3.因此,本项目根据WebFlux的方式,进行了整合,实现了登录和权限验证。
4.本项目采用前后端分离的方式,后端已经采用了跨域的设置。前端需在HBuilderX之类的容器中运行。
5.项目请用IDEA2018及以上的版本导入。# 1.运行环境
1.请安装mysql8,字符集设置为utf8mb4。
2.请安装redis并运行。# 2.项目配置
1.请在项目中全局搜索“修改此处”字样,找到需要修改的配置(配置数据库和redis)。
2.在拷贝出来的前端页面“login.html”中,搜索“修改此处”字样,修改所有请求的ip地址。# 3.资源文件
1.数据库sql文件在gateway模块的src/main/resources/db_files/spring_gateway_security.sql,请放入mysql8中,字符集utf8mb4。
2.前端页面在src/main/resources/pages中,请将所有文件请拷贝到HBuilderX之类的前端服务运行!# 4.运行说明 
1.启动eureka服务-开启服务注册中心
2.启动gateway服务网关(登录、权限验证和所有请求的统一入口)
3.启动base-core业务服务(内部有具体的业务方法)
4.在谷歌浏览器中打开页面login.html,登录后点击不同超链接,来观测不同角色的不同反应。登录账号、密码在数据库中查看。# 5.关键代码位置
1.gateway:src/main/java/com/daybreak/xian/gateway/config_security_webflux/:
此包中的类为SpringBoot、SpringSecurity的配置,SpringSecurity的核心入口配置类为此包中的“SecurityWebFluxConfig”。
2.gateway:src/main/java/com/daybreak/xian/gateway/component_security_webflux/:
此包中的类为SpringSecurity的WebFlux形式配置时所需注入的各个具体处理类,比如登录验证、权限验证、登陆成功、权限验证失败等处理类。
3.gateway:src/main/java/com/daybreak/xian/gateway/controller/:
此包中的类“BusinessController”为业务接口,里面放置了一些测试业务响应的接口,请自行从页面调用。
4.base-core:src/main/java/com/daybreak/xian/basecore/controler/:
此包中的类“BusinessController”也是业务接口,里面放置了一些跟角色有关的,测试业务响应的接口,请自行从页面调用。

项目资源地址:https://download.csdn.net/download/tiancao222/12658507

这篇关于SpringCloud Gateway整合Spring Security Webflux的关键点(痛点解析),及示例项目的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/871271

相关文章

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

Java判断多个时间段是否重合的方法小结

《Java判断多个时间段是否重合的方法小结》这篇文章主要为大家详细介绍了Java中判断多个时间段是否重合的方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录判断多个时间段是否有间隔判断时间段集合是否与某时间段重合判断多个时间段是否有间隔实体类内容public class D

IDEA编译报错“java: 常量字符串过长”的原因及解决方法

《IDEA编译报错“java:常量字符串过长”的原因及解决方法》今天在开发过程中,由于尝试将一个文件的Base64字符串设置为常量,结果导致IDEA编译的时候出现了如下报错java:常量字符串过长,... 目录一、问题描述二、问题原因2.1 理论角度2.2 源码角度三、解决方案解决方案①:StringBui

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

Java中ArrayList和LinkedList有什么区别举例详解

《Java中ArrayList和LinkedList有什么区别举例详解》:本文主要介绍Java中ArrayList和LinkedList区别的相关资料,包括数据结构特性、核心操作性能、内存与GC影... 目录一、底层数据结构二、核心操作性能对比三、内存与 GC 影响四、扩容机制五、线程安全与并发方案六、工程

部署Vue项目到服务器后404错误的原因及解决方案

《部署Vue项目到服务器后404错误的原因及解决方案》文章介绍了Vue项目部署步骤以及404错误的解决方案,部署步骤包括构建项目、上传文件、配置Web服务器、重启Nginx和访问域名,404错误通常是... 目录一、vue项目部署步骤二、404错误原因及解决方案错误场景原因分析解决方案一、Vue项目部署步骤

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.