微服务通过Feign调用进行密码安全认证操作

微服务通过Feign调用进行密码安全认证

在项目中,微服务之间的通信也是通过Feign代理的HTTP客户端通信,为了保护我们的业务微服务不被其他非法未经允许的服务调用, 我们要进行访问授权配置!

Feign是客户端配置,@FeignClient注解有个configuation属性,可以配置我们自定义的配置类,在此类中注入微服务认证拦截器

  /**
     * 访问微服务需要密码
     * @return
     */
    @Bean
    public FeignBasicAuthRequestInterceptor requestInterceptor(){
        System.out.println("------>进入微服务认证拦截器");
        return new FeignBasicAuthRequestInterceptor();
    }

feign内部有自带的BasicAuthRequestInterceptor类实现了RequestInterceptor接口,接收username,password参数,并将此参数通过apply方法设置到了请求头中

public void apply(RequestTemplate template) {
        template.header("Authorization", new String[]{this.headerValue});
    }

由此我们可知,我们可以自定义类实现RequestInterceptor接口,重写apply方法,添加我们的认证逻辑,也同样通过RequestTemplate就token设置到请求头中!

启动两个微服务,打印日志信息,两个微服务各自的控制台都打印,证明认证拦截器配置成功,通过Spring容器加载成功

那么,既然feign是客户端配置,那么客户端只要知道了所需调用微服务的rest就可以不配置这个拦截器也能访问,上述代码并没有达到保护业务服务资源的作用,"调用我必须需要密码"这一行为应该是由微服务强制要求才是!那么微服务在什么地方制定这个规则呢?

仔细阅读BasicAuthRequestInterceptor源码便知客户端将密码设置到了请求头中,feign是模拟HTTP请求到微服务拿取资源,那么微服务就可以通过配置过滤器来过滤所有经过"我"的请求,微服务在过滤器拿到客户端请求的header就可以开始我们的认证逻辑了!

比较简单的认证就是各自的微服务通过application.yml配置访问所需的账号密码,将这个账号密码告诉授信用的调用方,调用方设置feign拦截器将账号密码注入到header中!也可以进行加密传输,过滤器再进行解密

微服务过滤器注意对响应设置编码,否则输出中文会乱码

 httpResponse.setCharacterEncoding("UTF-8");
 httpResponse.setContentType("application/json;charset=utf-8");
 PrintWriter print = httpResponse.getWriter();

可以将过滤器抽取在公共类中,否则每个微服务都要配一个过滤器

微服务之间调用部分Feign接口忽略认证授权

在SpringSecurity框架基础之上实现微服务之间部分接口忽略认证授权.

思路

创建忽略授权注解

获取所有被注解的类或者方法

在SpringSecurity框架中忽略授权

1. 创建忽略授权注解

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AuthIgnore {
}

2. 获取所有被注解的类或者方法

@Slf4j
@Configuration
public class AuthIgnoreConfig implements InitializingBean {
    @Autowired
    private ApplicationContext applicationContext;
    private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}");
    private static final String ASTERISK = "*";
    @Getter
    @Setter
    private List<String> ignoreUrls = new ArrayList<>();
    @Override
    public void afterPropertiesSet(){
        RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
        Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();
        map.keySet().forEach(mappingInfo -> {
            HandlerMethod handlerMethod = map.get(mappingInfo);
            AuthIgnore method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), AuthIgnore.class);
            Optional.ofNullable(method)
                    .ifPresent(authIgnore -> mappingInfo
                            .getPatternsCondition()
                            .getPatterns()
                            .forEach(url -> ignoreUrls.add(ReUtil.replaceAll(url, PATTERN, ASTERISK))));
        });
        Optional.ofNullable(applicationContext.getBeansWithAnnotation(AuthIgnore.class))
                .ifPresent(stringObjectMap -> stringObjectMap.values()
                    .forEach(object -> Arrays.asList(object.getClass().getInterfaces()[0].getDeclaredMethods()).forEach(method -> {
                        List<Annotation> annotations = Arrays.asList(method.getAnnotation(RequestMapping.class), method.getAnnotation(PostMapping.class),
                                method.getAnnotation(GetMapping.class));
                        annotations.forEach(annotation -> {
                            if (ObjectUtil.isNotEmpty(annotation)) {
                                try {
                                    Field field = Proxy.getInvocationHandler(annotation).getClass().getDeclaredField("memberValues");
                                    field.setAccessible(true);
                                    Map valueMap = (Map) field.get(Proxy.getInvocationHandler(annotation));
                                    String[] string = (String[])valueMap.get("value");
                                    ignoreUrls.add(StrUtil.SLASH.concat(ReUtil.replaceAll(string[0], PATTERN, ASTERISK)));
                                } catch (Exception e) {
                                   log.error(e.getMessage(),e);
                                }
                            }
                        });
                    })));
    }
}

实现InitializingBean接口后,该类初始化的时候会调用afterPropertiesSet方法

代码中的工具类统一使用的hutool工具类

3. 在SpringSecurity框架中忽略授权

@Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/**/api/**","/v2/**","/actuator/**","doc.html")
                .antMatchers(authIgnoreConfig.getIgnoreUrls().stream().distinct().toArray(String[]::new));
    }

authIgnoreConfig变量为第二步的类,使用@Autowired注解注入进来即可

最后

服务启动后自动加载所有的@AuthIgnore标注的URL给资源服务设置为忽略认证

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Feign调用服务各种坑的处理方案

    1.编写被调用服务 @RefreshScope @RestController public class XXXController extends BaseController implements IndicatorsFeignApi{ @Resource private XXXService xxx; @Override public Wrapper<CommonVo> getXXXX(@RequestBody CommonDto commonDto) { try { CommonVo

  • spring cloud中微服务之间的调用以及eureka的自我保护机制详解

    上篇讲了spring cloud注册中心及客户端的注册,所以这篇主要讲一下服务和服务之间是怎样调用的 不会搭建的小伙伴请参考我上一篇博客:idea快速搭建spring cloud-注册中心与注册 基于上一篇的搭建我又自己搭建了一个客户端微服务: 所以现在有两个微服务,我们所实现的就是微服务1和微服务2之间的调用 注册中心就不用多说了,具体看一下两个微服务 application.yml配置也不用说了,不知道怎么配置的请参考我上篇博客 在project-solr中的constroller中: @R

  • Spring Cloud Feign统一设置验证token实现方法解析

    我们也在zuul中通过前置过滤器来统一设置token, 其实还漏掉了一种,那就是业务服务调用业务服务的时候,是没有zuul这种前置过滤器的,那么我们该如何设置呢? 其实也挺简单的,因为我们服务之前的调用是依赖于Feign的,我们可以从Feign上来做文章. 如果你仔细看过Feign的文档的话,肯定会注意到下面一段代码: static class DynamicAuthTokenTarget<T> implements Target<T> { public DynamicAuthTo

  • SpringCloud feign微服务调用之间的异常处理方式

    如何优雅地处理微服务间调用的异常 现在微服务架构盛行,其中spring cloud方案就很具有代表. 那么在微服务之间进行调用,如果被调用的服务挂了,调用方如何感知呢? 一.加上hystrix熔断 在定义feignClient的地方指定熔断,如下图 当被调用服务不可用或者被调用方发生错误的时候,会触发熔断,但是,如果被调用方抛出异常,调用方怎么知道究竟是出了什么问题呢? 那,这就出现了 二.feign全局异常处理 我们不得不提到feign提供的一个接口叫做ErrorDecoder, 是用来处理f

  • 微服务通过Feign调用进行密码安全认证操作

    微服务通过Feign调用进行密码安全认证 在项目中,微服务之间的通信也是通过Feign代理的HTTP客户端通信,为了保护我们的业务微服务不被其他非法未经允许的服务调用, 我们要进行访问授权配置! Feign是客户端配置,@FeignClient注解有个configuation属性,可以配置我们自定义的配置类,在此类中注入微服务认证拦截器 /** * 访问微服务需要密码 * @return */ @Bean public FeignBasicAuthRequestInterceptor reque

  • spring cloud eureka微服务之间的调用详解

    微服务之间的调用如何实现 首先 你需要两个或以上的微服务模块 至于怎么创建可以参考我上一篇博客 spring cloud eureka注册中心 如果想在页面显示 那么需要先加上 compile 'org.springframework.boot:spring-boot-starter-thymeleaf' 这个thymeleaf依赖 springboot推荐使用thymeleaf模板 它的最大好处就是原型即是模板 后缀是html html文件 需要放在resources/templates文件夹

  • 微服务之Feign的介绍与使用小结

    目录 前言: Feign的简介 Feign的优点 Feign如何使用 Feign的使用总结 前言: 最近在学习微服务相关的知识,看了黑马的相关课程,将关于Feign的知识又总结了一些,希望能帮到各位小伙儿们以及加深下自己的印象 Feign的简介 Feign 是一个声明式的伪RPC的REST客户端,它用了基于接口的注解方式,很方便的客户端配置,Spring Cloud 给 Feign 添加了支持Spring MVC注解,并整合Ribbon及Eureka进行支持负载均衡. Feign 是⼀个 HTT

  • 基于springboot服务间Feign调用超时的解决方案

    解决springboot服务间Feign调用超时问题概述 1.起因 在完成项目功能需求的开发,经过自己测试以及通过测试组测试通过后,昨晚正式部署到线上环境进行正式运行前的最后一次的测试.但是在测试中,由A服务调用B服务接口时,***通过Feign调用(其实就是http请求,当A服务调用B服务时,如果不配置超时时间,那么A发出请求后,B应该立即响应,否则A服务会认为B已经断开连接)出现***连接超时的错误,错误信息:Read timed out- 2.原因 用idea开发debug模式调试代码时,

  • 使用FeignClient进行微服务交互方式(微服务接口互相调用)

    目录 使用FeignClient进行微服务交互 先写一个公共方法 然后写一个Feign调用 被Feign调用的方法如下 @FeignClient调用微服务注意事项 FeignClient接口不能使用@GettingMapping之类的组合注解 FeignClient接口中如果使用到@PathVariable FeignClient多参数的构造 使用FeignClient进行微服务交互 先写一个公共方法   public String getSettingValue(String name) {

  • 浅析SpringBoot微服务中异步调用数据提交数据库的问题

    目录 前言: 一: 同步&异步 1.同步与异步的概念 2.同步方法调用&异步方法调用 2.1:同步方法调用 2.2:异步方法调用 二:问题引入 1.功能需求 2.问题引出 3.问题剖析 三:问题解决 前言: 1.前面基于Springboot的单体项目介绍已经完结了,至于项目中的其他功能实现我这里就不打算介绍了,因为涉及的知识点不难,而且都是简单的CRUD操作,假如有兴趣的话可以私信我我再看看要不要写几篇文章做个介绍. 2.完成上一阶段的学习,我就投入到了微服务的学习当中,所用教程为B站上面

  • 微服务架构之服务注册与发现功能详解

    目录 微服务的注册与发现 1.服务注册 2.服务发现 3.注册中心 4.现下的主流注册中心 4.1 Eureka 4.1.1 介绍 4.1.2 整体架构 4.1.3 接入Spring Cloud 4.2 ZooKeeper 4.2.1 介绍 4.2.2 整体架构 4.2.3 接入Dubbo生态 4.3 Consul 4.3.1 介绍 4.3.2 整体架构 4.3.3 生态对接 4.4 总结对比 详解微服务架构及其演进史 微服务全景架构全面瓦解 微服务架构拆分策略详解 微服务的注册与发现 我们前面

  • 解决微服务feign调用添加token的问题

    微服务feign调用添加token 1.一般情况是这么配置的 具体的怎么调用就不说了 如下配置,就可以在请求头中添加需要的请求头信息. package localdate; import feign.RequestInterceptor; import feign.RequestTemplate; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; imp

随机推荐