环境:Springboot2.7.7依赖管理org.springframework.bootspring-boot-starter-data-r2dbcorg.springframework.bootspring-boot-starter-securityorg.springframework.bootspring-boot-starter-webfluxdev.mikur2dbc-mysql0.8.2.RELEASE配置管理spring:r2dbc:url:r2dbc:mysql://localhost:3306/testjpa?serverZoneId=GMT%2B8用户名:root密码:123123池:initialSize:100maxSize:200maxCreateConnectionTime:30s---logging:level:'[org.springframework.r2dbc]':DEBUG实体对象,Repository,Service@Table("t_users")publicclassUsersimplementsUserDetails{@IdprivateIntegerid;私有字符串用户名;privateStringpassword;}这里实体对象实现了UserDetials,后续配置中ReactiveUserDetailsS??ervice配置的返回值必须是UserDetailsRepository接口publicinterfaceUsersRepositoryextendsReactiveSortingRepository{MonofindByUsername(Stringusername);}服务类@ServicepublicclassUsersService{@ResourceprivateR2dbcEntityTemplate模板;@Resource私人UsersRepository你;publicMonoqueryUserByUsername)(String{returnur.findByUsername(username);}publicMonoqueryUserByUsernameAndPassword(Stringusername,Stringpassword){returnur.findByUsernameAndPassword(username,password);}publicFluxqueryUsers(){returntemplate.select(Users.class).all();}}向数据库中插入几条数据单元测试@SpringBootTestclassSpringBootWebfluxSecurity2ApplicationTests{@Resource私人用户服务用户服务;@TestpublicvoidtestQueryUserByUsername()throwsException{usersService.queryUserByUsername("admin").doOnNext(System.out::println).subscribe();系统.in.read();}}输出2023-01-1217:43:48.863DEBUG16612---[main]o.s.w.r.r.m.a.ControllerMethodResolver:ControllerAdvicebean:none2023-01-1217:43:48.896DEBUG16612---[main]o.s.w.r.r.m.a.ControllerMethodResolver:ControllerAdvicebeans:none2023-01-1217:43:48.896DEBUG16612---[main]o.s.w.r.r.m.a.ControllerMethodResolver:ControllerAdvicebeans:none2023-01-1217:43:48.896DEBUG16612---[main]o.s.w.r.r.m.a.ControllerMethodResolver:enableLoggingRequestDetails='false':表单数据和标题将被屏蔽以防止不安全地记录潜在敏感数据2023-01-1217:43:49.147INFO16612---[main]ringBootWebfluxSecurity2ApplicationTests:在1.778秒内启动SpringBootWebfluxSecurity2ApplicationTests(JVM运行2.356)2023-01-1217:43:50.141DEBUG16612---[actor-tcp-nio-2]o.s.r2dbc.core.DefaultDatabaseClient:执行SQL语句[SELECTt_users.id,t_users.username,t_users.passwordFROMt_usersWHEREt_users.username=?]Users[id=3,username=admin,password=123123]是正常的,下一步就是配置Security@Configuration@EnableReactiveMethodSecuritypublicclassReactiveSecurityConfig{//根据username查询用户信息@BeanpublicReactiveUserDetailsS??ervicereactiveUserDetailsS??ervice(UsersServiceusersService){returnusername->usersService.queryUserByUsername(username).map(user->{return(UserDetails)user;});}//根据查询的用户执行密码比较,没有密码执行简单的等号比较加密@BeanpublicPasswordEncoderpasswordEncoder(){returnnewPasswordEncoder(){@OverridepublicStringencode(CharSequencerawPassword){returnrawPassword.toString();}@Overridepublicbooleanmatches(CharSequencerawPassword,StringencodedPassword){returnrawPassword.toString().equals(encodedPassword);}};}@Order(Ordered.HIGHEST_PRECEDENCE+1)@BeanpublicSecurityWebFilterChainspringWebFilterChain(ServerHttpSecurityhttp){http.authorizeExchange((authorize)->authorize//配置资源访问权限,直接释放静态资源pathMatchers("/resources/**","/favicon.ico").permitAll()//接口以/users开头的必须具有ROLE_ADMINrole.pathMatchers("/users/**").hasRole("ADMIN")//以/api开头的接口由自定义身份验证逻辑控制。pathMatchers("/api/**").access((authentication,context)->{returnauthentication.map(auth->{if("user1".equals(auth.getName())){returnnewAuthorizationDecision(false);}MultiValueMapparams=context.getExchange().getRequest().getQueryParams();Listsk=params.get("sk");if(sk==null||sk.get(0).equals("u")){returnnewAuthorizationDecision(false);}returnnewAuthorizationDecision(true);});}).anyExchange().authenticated());//异常处理http.exceptionHandling(execSpec->{//无权限时的响应策略execSpec.accessDeniedHandler((exchange,denied)->{ServerHttpResponseresponse=exchange.getResponse();response.getHeaders().add("Content-Type","text/plain;charset=UTF-8");DataBufferbody=response.bufferFactory().allocateBuffer();body.write("Nopermission-"+denied.getMessage(),StandardCharsets.UTF_8);returnresponse.writeWith(Mono.just(body));});//没有登录配置统一认证入口,这里是系统提供的默认登录页面execSpec.authenticationEntryPoint((exchange,ex)->{ServerHttpResponseresponse=exchange.getResponse();response.getHeaders().add("Content-Type","text/html;charset=UTF-8");DataBufferbody=response.bufferFactory().allocateBuffer();body.write("请先登录-"+ex.getMessage(),StandardCharsets.UTF_8);returnresponse.writeWith(Mono.just(body));});});http.csrf(csrf-&g吨;csrf.disable());http.formLogin();返回http.build();}}以上是安全配置。这里不测试原理。这里简单介绍一下实现原理。主要核心是WebFilter自动配置类中的publicclassReactiveSecurityAutoConfiguration{//这里通过注解开启Security功能@EnableWebFluxSecuritystaticclassEnableWebFluxSecurityConfiguration{}}EnableWebFluxSecurity@Import({ServerHttpSecurityConfiguration.class,WebFluxSecurityConfiguration.class,ReactiveOAuth2ClientImportSelector.class})@Configurationpublic@interfaceEnableWebFluxSecurity{}这里有一个重要的ServerHttpSecurityConfiguration和WebFluxSecurityConfiguration配置类@Configuration(proxyBeanMethods=false)classServerHttpSecurityConfiguration{//该类使用来自定义配置我们在Security上面定制的信息//通过ServerHttpSecurity构建SecurityWebFilterChain@Bean(HTTPSECURITY_BEAN_NAME)@Scope("prototype")ServerHttpSecurityhttpSecurity(){ContextAwareServerHttpSecurityhttp=newContextAwareServerMuthuricentAgSecurity().anager()).headers().and().logout().and();}}WebFluxSecurityConfiguration配置类主要用来配置一个WebFilter@Configuration(proxyBeanMethods=false)classWebFluxSecurityConfiguration{privateListsecurityWebFilterChains;//注入当前系统中的所有SecurityWebFilterChain,主要就是我们自定义现实的该类型@Autowired(required=false)voidsetSecurityWebFilterChains(ListsecurityWebFilterChains){this.securityWebFilterChains=WebFilterChains}@Bean(SPRING_SECURITY_WEBFILTERCHAINFILTER_BEAN_NAME)@Order(WEB_FILTER_CHAIN_FILTER_ORDER)WebFilterChainProxyspringSecurityWebFilterChainFilter(){返回新的WebFilterChainProxy(getSecurityWebFilterChains());}privateListgetSecurityWebFilterChains(){Listresult=this.securityWebFilterChains;if(ObjectUtils.isEmpty(result)){返回数组.asList(springSecurityFilterChain());}返回结果;}}WebFilterChainProxy核心过滤器publicclassWebFilterChainProxyimplementsWebFilter{privatefinalListfilters;@OverridepublicMonofilter(ServerWebExchangeexchange,WebFilterChainchain){返回Flux.fromIterable(this.filters).filterWhen((securityWebFilterChain)->securityWebFilterChain.matches(exchange)).next().switchIfEmpty(chain.filter(exchange).then(Mono.empty())).flatMap((securityWebFilterChain)->securityWebFilterChain.getWebFilters().collectList()).map((filters)->newFilteringWebHandler(chain::filter,filters)).map(DefaultWebFilterChain::new).flatMap((securedChain)->securedChain.filter(exchange));}}以上就是WebFlux中应用安全的原理