关于Quartz远程调用服务方法失败如何解决,@Inner详细介绍

1.单独在要调用服务的controller写上相关方法(@Inner(value = true)要走aop,会检测是否有内部调用标识)具体见下述

2. 编写Feign远程调用的接口,注意加上@RequestHeader(SecurityConstants.FROM) String from。因为@inner(value = true)会走aop调用时会检测是否带有SecurityConstants.FROM_IN内部调用标识;

3.用Quartz定时任务远程调用接口,带上SecurityConstants.FROM_IN参数为内部识别 (因为注解@inner(value = true)中的value=true回走aop检测是否带有内部标识,如果没有则验证不通过。也可以让value=false,这样不走aop了,那么就可以不用在Feign远程调用的接口方法中加上注解@RequestHeader(SecurityConstants.FROM) String from。)

总而言之@inner(value = true),value=true,且要加@RequestHeader(SecurityConstants.FROM) String from 。可以理解为仅在Feign远程调用内部时有权访问方法,外面经过网关的都不可以调用,无权访问方法。相反value=false所有地方均可不鉴权使用方法

外部从Gateway访问,需要鉴权(eg.CURD操作)。这种是最常使用的,用户登录后正常访问接口,不需要我们做什么处理(可能有的接口需要加权限字段)。
外部从Gateway访问,不需要鉴权(eg.短信验证码)。需要我们将uri加入到security.oauth2.client.ignore-urls配置中,可以不需要鉴权访问
内部服务间用Feign访问,不需要鉴权(eg.Auth查询用户信息)。也是需要我们将uri加入到security.oauth2.client.ignore-urls配置中,那与第二种的区别就是这种情况下大多数都是服务可以请求另一个服务的所有数据,不受约束,那我们如果仅仅只配置ignore-url的话,外部所有人都可以通过url请求到我们内部的链接,安全达不到保障。
鉴于上述第三种情况,配置了ignore-url和Feign,此时该接口不需要鉴权,服务内部通过Feign访问,服务外部通过url也可以访问,所以Pigx中,加入了一种@RequestHeader(SecurityConstants.FROM)的处理方式。即在接口方法中,对头部进行判断,只有请求带上相应的Header参数时,才允许通过访问,否则抛出异常。那这时候其实我们在外网通过Gateway访问的时候,也可以手动带上这个Header参数,来达到这个目的。所以我们便在Gateway中设置了一个GlobalFilter过滤器:

/*** @author * @date 2018/10/8* <p>* 全局拦截器,作用所有的微服务* <p>* 1. 对请求头中参数进行处理 from 参数进行清洗 2. 重写StripPrefix = 1,支持全局* <p>* 支持swagger添加X-Forwarded-Prefix header (F SR2 已经支持,不需要自己维护)*/
@Component
public class ParkRequestGlobalFilter implements GlobalFilter, Ordered {/*** Process the Web request and (optionally) delegate to the next {@code WebFilter}* through the given {@link GatewayFilterChain}.* @param exchange the current server exchange* @param chain provides a way to delegate to the next filter* @return {@code Mono<Void>} to indicate when request processing is complete*/@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 1. 清洗请求头中from 参数ServerHttpRequest request = exchange.getRequest().mutate().headers(httpHeaders -> {httpHeaders.remove(SecurityConstants.FROM);// 设置请求时间httpHeaders.put(CommonConstants.REQUEST_START_TIME,Collections.singletonList(String.valueOf(System.currentTimeMillis())));}).build();// 2. 重写StripPrefixaddOriginalRequestUrl(exchange, request.getURI());String rawPath = request.getURI().getRawPath();String newPath = "/" + Arrays.stream(StringUtils.tokenizeToStringArray(rawPath, "/")).skip(1L).collect(Collectors.joining("/"));ServerHttpRequest newRequest = request.mutate().path(newPath).build();exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, newRequest.getURI());return chain.filter(exchange.mutate().request(newRequest.mutate().build()).build());}

这个过滤器在处理HttpRequest的时候,会删除从外部请求头里的SecurityConstants.FROM这个参数。此时的效果就是,这个URL从外部访问不需要鉴权,但由于Gateway的过滤,最终到达我们接口方法时,由于缺少头部信息,被拒绝访问;而服务间通过Feign访问,不经过Gateway,则可以正常访问。

那原始的处理方法和处理逻辑就是这样:首先将uri加入ingore-url,然后在接口的方法和Feign的接口参数中写上RequestHeader参数,最后在Feign-Client中带上这个SecurityConstants.FROM参数。既然这种逻辑都是相同的,那后面的pigx版本发行后,就使用AOP将此步骤抽离出来,成为了Inner。
 

# Inner的处理流程

# 统一的ignore-url处理
#统一的URL处理

首先我们来看看这个注解的代码

package com.hongtu.park.common.security.annotation;import java.lang.annotation.*;/*** 服务调用不鉴权注解** @author * @date 2020-06-14*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Inner {/*** 是否AOP统一处理(可以理解为是否仅允许Feign之间调用)* @return false, true*/boolean value() default true;/*** 需要特殊判空的字段(预留)* @return {}*/String[] field() default {};}

首先,在我们项目加载阶段,我们获取有Inner注解的类和方法,然后获取我们配置的uri,经过正则替换后面的可变参数为*,然后将此uri加入到ignore-url中。此时我们就能达到所有Inner配置的方法/类上的接口地址,都统一在项目加载阶段自动帮我们加到ignore-url中,不需要我们手动配置,免去了很多开发工作,同时也能避免我们忘记配置,而浪费开发时间。核心代码如下:
 

/*** @author * @date 2020-03-11* <p>* 资源服务器对外直接暴露URL,如果设置contex-path 要特殊处理*/
@Slf4j
@ConfigurationProperties(prefix = "security.oauth2.client")
public class PermitAllUrlProperties implements InitializingBean {private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}");private static final String[] DEFAULT_IGNORE_URLS = new String[] { "/actuator/**", "/error", "/v3/api-docs" };@Getter@Setterprivate List<String> ignoreUrls = new ArrayList<>();@Overridepublic void afterPropertiesSet() {ignoreUrls.addAll(Arrays.asList(DEFAULT_IGNORE_URLS));RequestMappingHandlerMapping mapping = SpringContextHolder.getBean("requestMappingHandlerMapping");Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();map.keySet().forEach(info -> {HandlerMethod handlerMethod = map.get(info);// 获取方法上边的注解 替代path variable 为 *Inner method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Inner.class);Optional.ofNullable(method).ifPresent(inner -> Objects.requireNonNull(info.getPathPatternsCondition()).getPatternValues().forEach(url -> ignoreUrls.add(ReUtil.replaceAll(url, PATTERN, "*"))));// 获取类上边的注解, 替代path variable 为 *Inner controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Inner.class);Optional.ofNullable(controller).ifPresent(inner -> Objects.requireNonNull(info.getPathPatternsCondition()).getPatternValues().forEach(url -> ignoreUrls.add(ReUtil.replaceAll(url, PATTERN, "*"))));});}}

# 统一的安全性处理

那上面讲到的,如果我们不希望这个url可以直接被外网调用,仅能在Feign服务中调用,改如何统一处理呢? 

我们使用一个Spring-AOP,在对所有Inner注解的方法做一个环绕增强的切点,进行统一的处理。在上面我们提到的Inner的value参数,当该参数为true时,我们对方法的入参进行判断,仅当符合我们定制的入参规则时(Pigx这里是用的@RequestHeader(SecurityConstants.FROM) 与SecurityConstants.FROM_IN做比较),我们对它进行放行,不符合时,抛出异常;当value为false时,咱不做任何处理,此时Inner仅起到了一个ignore-url的作用。

/*** @author * @date 2022-06-04** 服务间接口不鉴权处理逻辑*/
@Slf4j
@Aspect
@RequiredArgsConstructor
public class ParkSecurityInnerAspect implements Ordered {private final HttpServletRequest request;@SneakyThrows@Around("@within(inner) || @annotation(inner)")public Object around(ProceedingJoinPoint point, Inner inner) {// 实际注入的inner实体由表达式后一个注解决定,即是方法上的@Inner注解实体,若方法上无@Inner注解,则获取类上的if (inner == null) {Class<?> clazz = point.getTarget().getClass();inner = AnnotationUtils.findAnnotation(clazz, Inner.class);}String header = request.getHeader(SecurityConstants.FROM);if (inner.value() && !StrUtil.equals(SecurityConstants.FROM_IN, header)) {log.warn("访问接口 {} 没有权限", point.getSignature().getName());throw new AccessDeniedException("Access is denied");}return point.proceed();}@Overridepublic int getOrder() {return Ordered.HIGHEST_PRECEDENCE + 1;}}

通过这两步呢,我们首先是在加载时通过找到Inner注解,将相应的uri加入到ignore-url中,达到自动化配置的目的;之后我们又使用切面对Inner的方法进行环绕处理,达到安全控制。对比之前的处理方式,现在我们使用一个@Inner注解,就能很快的满足上面说的两种场景,大大节省了我们的开发时间。

# 合理的使用@Inner注解

上面提到的两种应用场景,在我们的代码中,其实都是可以使用Inner注解的,下面结合Feign做一个简单的示例,示例场景就是我们的用户密码登录中的一环:

1. 在接口上使用@Inner注解,使得url无需鉴权

 /*** 获取指定用户全部信息** @return 用户信息*/@Inner@GetMapping("/info/{username}")public R info(@PathVariable String username) {SysUser user = userService.getOne(Wrappers.<SysUser>query().lambda().eq(SysUser::getUsername, username));if (user == null) {return R.failed(null, String.format("用户信息为空 %s", username));}return R.ok(userService.findUserInfo(user));}

2. 编写Feign接口

@FeignClient(contextId = "remoteUserService", value = ServiceNameConstants.UMPS_SERVICE)
public interface RemoteUserService {/*** 通过用户名查询用户、角色信息** @param username 用户名* @param from     调用标志* @return R*/@GetMapping("/user/info/{username}")R<UserInfo> info(@PathVariable("username") String username, @RequestHeader(SecurityConstants.FROM) String from);
} 

3. Feign-Client中调用接口,带上SecurityConstants.FROM_IN参数为内部识别 

 /*** 用户密码登录** @param username 用户名* @return* @throws UsernameNotFoundException*/@Override@SneakyThrowspublic UserDetails loadUserByUsername(String username) {Cache cache = cacheManager.getCache(CacheConstants.USER_DETAILS);if (cache != null && cache.get(username) != null) {return (PigxUser) cache.get(username).get();}R<UserInfo> result = remoteUserService.info(username, SecurityConstants.FROM_IN);UserDetails userDetails = getUserDetails(result);cache.put(username, userDetails);return userDetails;} 

 

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

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

相关文章

【LabVIEW FPGA入门】LabVIEW FPGA实现I2S解码器

该示例演示了如何使用 LabVIEW FPGA 解码 IS 信号。该代码可用于大多数支持高速数字输入的LabVIEW FPGA 目标&#xff08;例如R 系列、CompactRIO&#xff09;。IS 用于对系统和组件内的数字音频数据进行编码。例如&#xff0c;MP3 播放器或 DVD 播放器内部的数字音频通常使用 …

设备树OF函数操作实验-读取设备节点backlight的status属性

一. 简介 本文学习使用 设备树操作函数&#xff0c;读取设备节点的一个字符串类型的属性值。 读取设备树文件 imx6ull-14x14-evk.dts 中一个设备节点的信息。这里读取 backlight设备节点的属性值&#xff1a;读取字符串类型的 status属性。 二. 读取 backlight设备节点的s…

【从零开始学习Java重要集合】深入解读ThreadLocal类

目录 前言&#xff1a; ThreadLocal&#xff1a; ThreadLocal的内部结构&#xff1a; ThreadLocal的常用方法&#xff1a; 1.set方法&#xff1a; 2.get方法&#xff1a; 3.setInitialValue方法 remove方法&#xff08;&#xff09;&#xff1a; ThreadLocalMap&…

MySQL数据库入门到大牛_高级_00_MySQL高级特性篇的内容简介

文章目录 一、整个MySQL的思维导图二、MySQL高级特性篇大纲1. MySQL架构篇2. 索引及调优篇3. 事务篇4. 日志与备份篇 一、整个MySQL的思维导图 下图为整个MySQL内容&#xff0c;01-05是基础篇&#xff0c;06-09是高级篇 二、MySQL高级特性篇大纲 MySQL高级特性分为4个篇章&…

mybatisplus配置

一、新建项目&#xff1a;com.saas.plusdemo 二、配置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:sch…

PHP选择题复习

1. 如何使用 PHP 输出 “hello world”&#xff1f; A. "Hello World"; B. echo "Hello World"; C. Document.Write("Hello World"); 答案&#xff1a;B 2. 下面代码执行结果是&#xff1f; <?php FUNCTION TEST() { ECHO "HE…

双向冒泡排序的数据结构实验报告

目录 实验目的&#xff1a; 实验内容&#xff08;实验题目与说明&#xff09; 算法设计&#xff08;核心代码或全部代码&#xff09; 运行与测试&#xff08;测试数据和实验结果分析&#xff09; 总结与心得&#xff1a; 实验目的&#xff1a; 理解双向冒泡排序算法的原…

2023年全国职业院校技能大赛软件测试赛题—单元测试卷⑧

单元测试 一、任务要求 题目1&#xff1a;根据下列流程图编写程序实现相应处理&#xff0c;执行j10*x-y返回文字“j1&#xff1a;”和计算值&#xff0c;执行j(x-y)*(10⁵%7)返回文字“j2&#xff1a;”和计算值&#xff0c;执行jy*log(x10)返回文字“j3&#xff1a;”和计算值…

系统学英语 — 介词 — 千变万化

目录 文章目录 目录介词&#xff08;preposition&#xff0c;prep.&#xff09;1. in2. on3. off4. at5. outby6. for7. from8. against9. of10. into11. with12. to13. about14. since14. 其他时间介词15. 其他地点介词 介词&#xff08;preposition&#xff0c;prep.&#xf…

山西电力市场日前价格预测【2024-01-13】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2024-01-13&#xff09;山西电力市场全天平均日前电价为231.81元/MWh。其中&#xff0c;最高日前电价为345.71元/MWh&#xff0c;预计出现在00:15。最低日前电价为0.00元/MWh&#xff0c;预计出…

node-sass@4.7.2 postinstall: `node scripts/build.js`

Can‘t find Python executable “D:\Python36\python.EXE“, you can set the PYTHON env variable.-CSDN博客 gyp ERR! build error gyp ERR! stack Error: C:\Windows\Microsoft.NET\Framework\v4.0.30319\msbuild.exe failed with exit code: 1 gyp ERR! stack at Chil…

uniapp怎么开发插件并发布

今天耳机坏了,暂时内卷不了,所以想开发几个插件玩玩,也好久没写博客了,就拿这个来写了 首先,发布插件时需要你有项目 这里先拿uniapp创建一个项目, 如下,创建好的项目长这样 然后根据uniapp官网上说的,我们发布插件时,需要在uni_modules里面编写和发布 ps:还需要使用uniapp…

Mysql事务的处理

1、事务&#xff0c;就是一组命令的操作。 不过这一组命令&#xff0c;我们有时候需要使用手动提交&#xff1b; 1、使用这组命令可以查询出来现在的提交方式&#xff1a;自动提交&#xff08;就是命令输入&#xff0c;点击enter后&#xff0c;会不会直接对表格产生修改&#x…

day04打卡

day04打卡 面试题 02.07. 链表相交 时间复杂度&#xff1a;O(N)&#xff0c;空间复杂度&#xff1a;O(1) 第一想法&#xff1a;求出两个链表长度&#xff0c;走差距步&#xff0c;再遍历找有没有相交 /*** Definition for singly-linked list.* struct ListNode {* int…

一篇文章让你搞懂性能测试6大类型及其关系!

性能测试是软件测试过程的一个关键环节&#xff0c;用于确定和验证应用程序或系统在各种操作条件下的性能特征。 目标是确保软件在高负载、高压力、长时间运行以及其他非标准情况下仍能保持预期的行为和效率。 一. 性能测试的主要类型 1. 基线测试&#xff08;Baseline Test…

C++ 手写堆 || 堆模版题:堆排序

输入一个长度为 n 的整数数列&#xff0c;从小到大输出前 m 小的数。 输入格式 第一行包含整数 n 和 m 。 第二行包含 n 个整数&#xff0c;表示整数数列。 输出格式 共一行&#xff0c;包含 m 个整数&#xff0c;表示整数数列中前 m 小的数。 数据范围 1≤m≤n≤105 &…

js for循环与for in循环 for of循环的区别

JavaScript中&#xff0c;for循环、for...in循环和for...of循环是用于迭代数组或对象属性的不同方式。 for循环&#xff1a; for循环是最常见的迭代方法&#xff0c;它允许你指定迭代的起始点、结束条件和每次迭代后的操作。它可以用于迭代数组和字符串。 例如&#xff0c;遍…

总结:Java程序员读书清单顺序

总结&#xff1a;Java程序员读书清单顺序&#xff0c;持续更新中。。。。。。 一经验提示&#xff1a;1.零基础不建议直接看计算机专业书籍&#xff0c;建议先去看视频教程2.本书单目录用作自学顺序记录&#xff0c;也适用于有Java开发基础的同志3.看计算机书籍可以完善自己的技…

用通俗易懂的方式讲解:Stable Diffusion WebUI 从零基础到入门

本文主要介绍 Stable Diffusion WebUI 的实际操作方法&#xff0c;涵盖prompt推导、lora模型、vae模型和controlNet应用等内容&#xff0c;并给出了可操作的文生图、图生图实战示例。适合对Stable Diffusion感兴趣&#xff0c;但又对Stable Diffusion WebUI使用感到困惑的同学。…

GPUMD分子动力学模拟-学习与实践

GPUMD分子动力学模拟-学习与实践 【20220813-樊哲勇 |基于GPUMD程序包的机器学习势和分子动力学模拟】 https://www.bilibili.com/video/BV1cd4y1Z7zi?share_sourcecopy_web 纯GPU下的MD分子模型系统软件 https://github.com/brucefan1983/GPUMD 跟GPUMD对接的一些python程…