当前位置:

首页 > 编程开发 > 如何在 Java 接口中添加参数?

如何在 Java 接口中添加参数?

一、背景一般在微服务架构中我们都会使用springsecurityoauth3来进行权限控制,我们将资源服务全部放在内网环境中,将API网关暴露在公网上,公网如果想要访问我们的资源必须经过API网关进行鉴权,鉴权通过后再访问我们的资源服务。我们根据如下图片来分析一下问题。现在我们有三个服务:分别是用户服务、订单服务和产品服务。用户如果购买产品,则需要调用产品服务生成订单,那么我们在这个调用过程中有必要鉴权吗?答案是否定的,因为这些资源服务放在内网环境中,完全不用考虑安全问题。二、思路如果要想实现这个功能,

本文目录

    一、背景

    一般在微服务架构中我们都会使用spring security oauth3来进行权限控制,我们将资源服务全部放在内网环境中,将API网关暴露在公网上,公网如果想要访问我们的资源必须经过API网关进行鉴权,鉴权通过后再访问我们的资源服务。我们根据如下图片来分析一下问题。

    如何在 Java 接口中添加参数?

    现在我们有三个服务:分别是用户服务、订单服务和产品服务。用户如果购买产品,则需要调用产品服务生成订单,那么我们在这个调用过程中有必要鉴权吗?答案是否定的,因为这些资源服务放在内网环境中,完全不用考虑安全问题。 

    二、思路

    如果要想实现这个功能,我们则需要来区分这两种请求,来自网关的请求进行鉴权,而服务间的请求则直接调用。

    是否可以给接口增加一个参数来标记它是服务间调用的请求?

    这样虽然可以实现两种请求的区分,但是实际中不会这么做。一般情况下服务间调用和网关请求的数据接口是同一个接口,如果写成两个接口来分别给两种请求调用,这样无疑增加了大量重复代码。也就是说我们一般不会通过改变请求参数的个数来实现这两种服务的区分。

    虽然不能增加请求的参数个数来区分,但是我们可以给请求的header中添加一个参数用来区分。这样完全可以避免上面提到的问题。 

    三、实现 

    3.1 自定义注解

    我们自定义一个Inner的注解,然后利用aop对这个注解进行处理

    1@Target(ElementType.METHOD)
    2@Retention(RetentionPolicy.RUNTIME)
    3@Documented
    4public @interface Inner {
    5    /**
    6     * 是否AOP统一处理
    7     */
    8    boolean value() default true;
    9}
     
     1@Aspect
    2@Component
    3public class InnerAspect implements Ordered {
    4
    5    private final Logger log = LoggerFactory.getLogger(InnerAspect.class);
    6
    7    @Around("@annotation(inner)")
    8    public Object around(ProceedingJoinPoint point, Inner inner) throws Throwable {
    9        String header = ServletUtils.getRequest().getHeader(SecurityConstants.FROM);
    10        if (inner.value() && !StringUtils.equals(SecurityConstants.FROM_IN, header)){
    11            log.warn("访问接口 {} 没有权限", point.getSignature().getName());
    12            throw new AccessDeniedException("Access is denied");
    13        }
    14        return point.proceed();
    15    }
    16
    17    @Override
    18    public int getOrder() {
    19        return Ordered.HIGHEST_PRECEDENCE + 1;
    20    }
    21}
     

    上面这段代码就是获取所有加了@Inner注解的方法或类,判断请求头中是否有我们规定的参数,如果没有,则不允许访问接口。 

    3.2 暴露url

    将所有注解了@Inner的方法和类暴露出来,允许不鉴权可以方法,这里需要注意的点是如果方法使用pathVariable 传参的,则需要将这个参数转换为*。如果不转换,当成接口的访问路径,则找不到此接口。

     1@Configuration
    2public class PermitAllUrlProperties implements InitializingBean, ApplicationContextAware{
    3
    4    private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}");
    5    private ApplicationContext applicationContext;
    6    private List urls = new ArrayList<>();
    7    public static final String ASTERISK = "*";
    8
    9    @Override
    10    public void afterPropertiesSet() {
    11        RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
    12        Map map = mapping.getHandlerMethods();
    13        map.keySet().forEach(info -> {
    14            HandlerMethod handlerMethod = map.get(info);
    15            // 获取方法上边的注解 替代path variable 为 *
    16            Inner method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Inner.class);
    17            Optional.ofNullable(method).ifPresent(inner -> info.getPatternsCondition().getPatterns()
    18                    .forEach(url -> urls.add(ReUtil.replaceAll(url, PATTERN, ASTERISK))));
    19            // 获取类上边的注解, 替代path variable 为 *
    20            Inner controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Inner.class);
    21            Optional.ofNullable(controller).ifPresent(inner -> info.getPatternsCondition().getPatterns()
    22                    .forEach(url -> urls.add(ReUtil.replaceAll(url, PATTERN, ASTERISK))));
    23        });
    24    }
    25
    26    @Override
    27    public void setApplicationContext(ApplicationContext context) {
    28        this.applicationContext = context;
    29    }
    30
    31    public List getUrls() {
    32        return urls;
    33    }
    34
    35    public void setUrls(List urls) {
    36        this.urls = urls;
    37    }
    38}
     

    在资源服务器中,将请求暴露出来

     1public void configure(HttpSecurity httpSecurity) throws Exception {
    2    //允许使用iframe 嵌套,避免swagger-ui 不被加载的问题
    3    httpSecurity.headers().frameOptions().disable();
    4    ExpressionUrlAuthorizationConfigurer
    5        .ExpressionInterceptUrlRegistry registry = httpSecurity
    6        .authorizeRequests();
    7    // 将上面获取到的请求,暴露出来
    8    permitAllUrl.getUrls()
    9        .forEach(url -> registry.antMatchers(url).permitAll());
    10    registry.anyRequest().authenticated()
    11        .and().csrf().disable();
    12}
       

    3.3 如何去请求

    定义一个接口:

    1@PostMapping("test")
    2@Inner
    3public String test(@RequestParam String id){
    4    return id;
    5}
     

    定义feign远程调用接口

    1@PostMapping("test")
    2MediaFodderBean test(@RequestParam("id") String id,@RequestHeader(SecurityConstants.FROM) String from);

    服务间进行调用,传请求头

    1 String id = testService.test(id, SecurityConstants.FROM_IN);  

    四、思考 

    4.1 安全性

    上面虽然实现了服务间调用,但是我们将@Inner的请求暴露出去了,也就是说不用鉴权既可以访问到,那么我们是不是可以模拟一个请求头,然后在其他地方通过网关来调用呢?

    答案是可以,那么,这时候我们就需要对网关中分发的请求进行处理,在网关中写一个全局拦截器,将请求头的form参数清洗。

     1@Component
    2public class RequestGlobalFilter implements GlobalFilter, Ordered {
    3
    4    @Override
    5    public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    6        // 清洗请求头中from 参数
    7        ServerHttpRequest request = exchange.getRequest().mutate()
    8            .headers(httpHeaders -> httpHeaders.remove(SecurityConstants.FROM))
    9            .build();
    10        addOriginalRequestUrl(exchange, request.getURI());
    11        String rawPath = request.getURI().getRawPath();
    12        ServerHttpRequest newRequest = request.mutate()
    13            .path(rawPath)
    14            .build();
    15        exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, newRequest.getURI());
    16        return chain.filter(exchange.mutate()
    17            .request(newRequest.mutate()
    18                .build()).build());
    19    }
    20
    21    @Override
    22    public int getOrder() {
    23        return -1000;
    24    }
    25}
       

    4.2 扩展性

    我们自定义@Inner注解的时候,放了一个boolean类型的value(),默认为true。如果我们想让这个请求可以通过网关访问的话,将value赋值为false即可。

    1@PostMapping("test")
    2@Inner(value=false)
    3public String test(@RequestParam String id){
    4    return id;
    5}

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发 Java
    相关文章 更多
    Java单元测试入门:使用JUnit 5编写第一个测试用例
    Java单元测试入门:使用JUnit 5编写第一个测试用例

    面向初学者的JUnit 5实用入门指南。从测试概念、Maven依赖配置,到手把手编写首个单元测试用例及断言验证,助你掌握Java单元测试的核心流程。

    Java变量未初始化报错解析:局部变量与成员变量默认值差异
    Java变量未初始化报错解析:局部变量与成员变量默认值差异

    深入解析Java中局部变量未初始化编译报错与成员变量自动赋予默认值的机制差异,涵盖JVM内存分配、编译期确定性赋值检查与实际排查修复方案。

    Java获取当前时间并格式化输出:DateTimeFormatter与SimpleDateFormat
    Java获取当前时间并格式化输出:DateTimeFormatter与SimpleDateFormat

    详细讲解在Java中获取当前系统时间并进行格式化输出的两种核心方案:基于Java 8全新日期时间API的DateTimeFormatter与传统遗留的SimpleDateFormat,包含完整代码示例、线程安全机制对比及生产选型建议。

    Java中final与static final常量的定义及初始化规则
    Java中final与static final常量的定义及初始化规则

    深入解析Java中final变量与static final常量的定义、内存语义、初始化时机及合法赋值路径,涵盖编译期常量优化与实战避坑指南。

    Java抽象方法与接口方法的区别及选型建议
    Java抽象方法与接口方法的区别及选型建议

    深入对比Java中抽象类抽象方法与接口方法的语法演进、设计意图及运行期差异,并提供清晰的架构选型决策流程与实战范例。

    Java实现HTTP请求与JSON解析的完整代码示例
    Java实现HTTP请求与JSON解析的完整代码示例

    本文提供Java标准库HttpClient发起HTTP GET/POST请求并结合Jackson进行JSON数据反序列化与序列化的完整代码示例与工程实践指南。

    Java递归出现StackOverflowError的原因分析及栈空间优化方案
    Java递归出现StackOverflowError的原因分析及栈空间优化方案

    详细分析Java递归调用出现StackOverflowError的原因,结合代码说明线程栈、栈帧和递归深度之间的关系,并介绍终止条件排查、迭代改写、减少栈帧开销以及通过-Xss调整JVM栈空间的方法。

    Java空指针异常(NPE)的代码排查与修复实例
    Java空指针异常(NPE)的代码排查与修复实例

    针对Java中常见的NullPointerException(NPE),本文从堆栈日志定位、典型代码触发场景、具体修复代码对比到防御体系进行系统讲解,提供实用且易上手的排查与修复方案。

    selenium上传文件教程本地文件路径设置方法
    selenium上传文件教程本地文件路径设置方法

    深入解析Selenium处理文件上传的技术细节,涵盖标准HTML控件的路径直接注入法与非标准控件的系统级模拟方案,提供Windows与Linux环境下的路径设置最佳实践及常见异常排查。

    大数据开发语言选型:Java对比Scala与Python的优缺点及适用场景
    大数据开发语言选型:Java对比Scala与Python的优缺点及适用场景

    针对大数据工程场景,系统对比Java、Scala与Python在执行性能、生态结合、开发效率和维护成本上的核心差异,给出清晰的架构选型依据与落地方案。

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程
    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    Aperty macOS版
    Aperty macOS版
    macOS

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    PixTV 网页版
    PixTV 网页版
    Windows

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。