关于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
	 */
	@Override
	public 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. 重写StripPrefix
		addOriginalRequestUrl(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
	@Setter
	private List<String> ignoreUrls = new ArrayList<>();

	@Override
	public 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();
	}

	@Override
	public 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
    @SneakyThrows
    public 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.mfbz.cn/a/317973.html

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

相关文章

OpenCV 基于C++图像读取及存储API函数

OpenCV可以从存储介质中读入图像&#xff0c;也可以将摄像头(Camera)抓取的图像载入内存&#xff0c;然后进行处理。而存储图像就是将内存中的图像数据写入存储介质中&#xff0c;如写入硬盘、优盘等。 OpenCV要读入图像、操作图像。首先要用到Mat类&#xff0c;并且需创建Mat对…

飞桨分子动力学模拟-论文复现第六期:复现TorchMD

飞桨分子动力学模拟-论文复现第六期&#xff1a;复现TorchMD Paddle for MD 飞桨分子动力学模拟科学计算 复现论文-TorchMD: A deep learning framework for molecular simulations 本项目可在AIStudio一键运行&#xff1a;飞桨分子动力学模拟PaddleMD-复现TorchMD 【论文复…

【STM32】HAL库的RCC复位状态判断及NVIC系统软件复位

【STM32】HAL库的RCC复位状态判断及NVIC系统软件复位 在实际开发中 有时候会遇到复位状态不同 导致结果不同的情况 比如在上电复位时 电压不稳定 可能导致一些外部芯片无法正常工作 从而导致进行了错误的操作流程 所以 可以在程序运行后 加一个复位状态判断 用来检测是否正常复…

linux 如何创建文件

我们在写一些教程的时候&#xff0c;经常会需要创建一些用于演示的文档&#xff0c;这些文档往往需要填充一些不特定的内容。那么如何快速的创建演示用的文档呢&#xff1f; docfaker.py docfaker.py是一个py脚本&#xff0c;用于创建一个简单的txt文档&#xff0c;docfaker.…

力扣每日一练(24-1-13)

如果用列表生成式&#xff0c;可以满足输出的型式&#xff0c;但是不满足题意&#xff1a; nums[:] [i for i in nums if i ! val]return len(nums) 题意要求是&#xff1a; 你需要原地修改数组&#xff0c;并且只使用O(1)的额外空间。这意味着我们不能创建新的列表&#xff…

vivado 指定顶部模块和重新排序源

指定顶部模块和重新排序源 文件夹默认情况下&#xff0c;Vivado Design Suite会自动确定设计的顶层添加到的源文件的层次结构和细化、合成和模拟的顺序项目这可以通过右键单击中的“层次更新”设置进行控制“源”窗口的菜单。请参阅中的“源”窗口中的“层次更新”命令Vivado …

从AAAI 2024看人工智能研究的最新热点

图 1 由AAAI 2024论文列表生成的词云 AAAI会议作为全球AI领域的顶级学术盛会&#xff0c;被中国计算机学会&#xff08;CCF&#xff09;评为A类会议。AAAI2024的会议论文投稿量达到了历史新高&#xff0c;主赛道收到了12100篇投稿论文&#xff0c;9862篇论文经过严格评审后共有…

基于Python的二氧化碳排放量数据分析系统

项目背景&#xff1a; 随着全球气候变化问题日益严重&#xff0c;减少温室气体排放已成为全球共识。我国作为全球最大的碳排放国&#xff0c;承担着重要的国际责任。为了更好地控制和减少碳排放&#xff0c;需要对全国的碳排放情况进行深入研究和分析。本项目旨在构建一个基于P…

腾讯云优惠券(代金券)领取方法及最新优惠活动汇总

腾讯云作为国内领先的云计算服务提供商&#xff0c;一直致力于为用户提供优质、高效、安全可靠的云计算服务。为了回馈广大用户&#xff0c;腾讯云会不定期地推出各种优惠活动&#xff0c;其中包括优惠券的发放。本文将分享腾讯云优惠券的领取方法并汇总最新优惠活动&#xff0…

uniapp小程序当页面内容超出时显示滚动条,不超出时不显示---样式自定义

使用scroll-view中的show-scrollbar属性 注意:需要搭配enhanced使用 否则无效 <scroll-view class"contentshow" scroll-y :show-scrollbartrue :enhancedtrue><view class"content" :show-scrollbartrue><text>{{vehicleCartinfo}}<…

阿里云云服务器u1实例和e实例有什么区别?

阿里云服务器u1和e实例有什么区别&#xff1f;ECS通用算力型u1实例是企业级独享型云服务器&#xff0c;ECS经济型e实例是共享型云服务器&#xff0c;所以相比较e实例&#xff0c;云服务器u1性能更好一些。e实例为共享型云服务器&#xff0c;共享型实例采用非绑定CPU调度模式&am…

【python入门】day26: 模拟高铁售票系统

界面 代码 #-*- coding:utf-8 -*- import prettytable as pt#---------导入漂亮表格 import os.path filename ticket.txt#更新座位状态 def update(row_num):#------更新购票状态with open(filename,w,encodingutf-8) as wfile:for i in range(row_num):lst1 [f{i1},有票,有…

代码随想录算法训练营第四天| 24. 两两交换链表中的节点、19.删除链表的倒数第N个节点面试题 02.07. 链表相交、142.环形链表II

文档讲解&#xff1a;虚拟头节点&#xff0c;三指针&#xff0c;快慢指针&#xff0c;链表相交&#xff0c;环形链表&#xff0c; 技巧&#xff1a; 1、对于指针的操作要画图&#xff0c;明确步骤后好做了 2、使用虚拟头节点可以避免对头节点单独讨论&#xff0c;且方便对头节点…

Linux 系统之部署 ZFile 在线网盘服务

一、ZFile 介绍 1&#xff09;ZFile 简介 官网&#xff1a;https://www.zfile.vip/ GitHub&#xff1a;https://github.com/zfile-dev/zfile ZFile 是一款基于 Java 的在线网盘程序&#xff0c;支持对接 S3、OneDrive、SharePoint、又拍云、本地存储、FTP 等存储源&#xff0…

oracle 19c容器数据库data dump数据泵传输数据(3)---完全传输

目录 查看pdb1 创建pdb2 从pdb1 中导出元数据 在pdb2中导入元数据&#xff08;dmp文件&#xff09; Full Transportable Export/Import: Example 只传输除了system&#xff0c;sysaux&#xff0c;temp&#xff0c;undo以外的用户表空间&#xff0c;這種方式傳輸的是用戶自定…

xtu oj 1329 连分式

题目描述 连分式是形如下面的分式&#xff0c;已知a,b和迭代的次数n&#xff0c;求连分式的值。 输入 第一行是一个整数T(1≤T≤1000)&#xff0c;表示样例的个数。 每行一个样例&#xff0c;为a,b,n(1≤a,b,n≤9) 输出 每行输出一个样例的结果&#xff0c;使用x/y分式表达…

计算机网络NCEPU复习资料

目录 一&#xff0e;概述&#xff1a; 计算机网络组成&#xff1a; 计算机网络分类&#xff1a; 计算机网络体系结构&#xff1a; C/S架构与P2P架构区别&#xff1a; OSI开放式系统互连参考模型&#xff1a; OSI开放式系统互连参考模型 相关协议&#xff1a; 五层协议网…

UML-类图和类图转化为代码

提示&#xff1a;文章详细的讲解了类图的四种关系&#xff0c;以及每种关系如何转化为对应的代码。 UML-类图和类图转化为代码 一、类于类之间的关系1.依赖关系2.关联关系(1) 单向关联(2) 双向关联(3) 自关联(4) 聚合关联(has-a)(5) 组合关联&#xff08;contains-a&#xff09…

Windows Server 2019配置多用户远程桌面登录服务器

正文共&#xff1a;1234 字 21 图&#xff0c;预估阅读时间&#xff1a;2 分钟 很久之前&#xff0c;在介绍显卡直通的时候我们简单介绍过RDP&#xff08;Remote Desktop Protocol&#xff0c;远程桌面协议&#xff09;&#xff08;前人栽树&#xff1a;失败的服务器显卡操作&a…

day17 平衡二叉树 二叉树的所有路径 左叶子之和

题目1&#xff1a;110 平衡二叉树 题目链接&#xff1a;110 平衡二叉树 题意 判断二叉树是否为平衡二叉树&#xff08;每个节点的左右两个子树的高度差绝对值不超过1&#xff09; 递归遍历 递归三部曲 1&#xff09;确定递归函数的参数和返回值 2&#xff09;确定终止条…
最新文章