fegin token

1.前端token透传

servlet线程中获取token fegin请求时封装进去 

/**
 * feign配置token
 */
@Configuration
public class FeignRequestInterceptor implements RequestInterceptor {

   @Override
   public void apply(RequestTemplate requestTemplate) {
      // 这里可以添加feign请求的全局参数

      ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
      if(attributes==null || attributes.getRequest()==null){
         return;
      }
      HttpServletRequest request = attributes.getRequest();
      requestTemplate.header("token", request.getHeader("token"));
      requestTemplate.header("feignClient", "ifaas-community-sec");
   }
}

2. 拦截403权限异常 根据用户名密码登录缓存token重试 

@Slf4j
@Configuration
public class FeignTokenAutoConfig {

    @Autowired
    private AuthorityService authorityService;

    @Bean
    public RequestInterceptor requestInterceptor() {
        return template -> {
            if (matchUrl(template.url())) {
                return;
            }
            template.header("token", authorityService.getCacheToken());
        };
    }

    private boolean matchUrl(String url){
        Pattern pattern = Pattern.compile("^*/oauth/token*$");
        Matcher matcher =pattern.matcher(url);
        return matcher.find();
    }

    @Bean
    public Retryer retryer() {
        return new Retryer(){
            int tryTimes;

            @Override
            public void continueOrPropagate(RetryableException e) {
                if (this.tryTimes++ >= 1) {
                    throw e;
                }
                if (e instanceof MyRetryException && e.getClass().equals(MyRetryException.class)) {
                    return;
                }
                throw e;
            }

            @Override
            public Retryer clone() {
                this.tryTimes = 0;
                return this;
            }
        };
    }

    @Bean
    public ErrorDecoder errorDecoder() {
        return (methodKey, response) -> {
            FeignException exception = FeignException.errorStatus(methodKey, response);
            if (response.status() == HttpStatus.UNAUTHORIZED.value()) {
                authorityService.resetToken();
                return new MyRetryException( exception.getMessage(), exception, null);
            }
            return exception;
        };
    }

    static class MyRetryException extends RetryableException {
        public MyRetryException(String message, Throwable cause, Date retryAfter) {
            super(message, cause,retryAfter);
        }
    }
}

在需要重试的客户端加配置

@FeignClient(value ="test", url = "${test.url.prefix}", configuration = FeignTokenAutoConfig.class, fallback = TestClientCall.class)

3.在servlet线程中 另起一个线程  丢失token 为线程池设置taskDecorator

@Configuration
@EnableAsync
@Slf4j
public class ThreadPoolConfig {

    /**
     * 定义线程池大小,默认是是当前核数*2+1
     */
    @Value("${service.thread.num:0}")
    private Integer threadCoreNum;

    public static final String ASYNC_EXECUTOR_NAME = "asyncExecutor";

    /**
     * 业务线程通用线程池
     * @return
     */
    @Bean(name = ASYNC_EXECUTOR_NAME)
    public ThreadPoolTaskExecutor serviceThreadPoolTaskExecutor() {
        log.info("<<<<<<<<<开始初始化AsyncService线程池");
        if(threadCoreNum==0){
            threadCoreNum = Runtime.getRuntime().availableProcessors() * 2+1;
        }
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 核心线程数
        executor.setCorePoolSize(threadCoreNum);
        // 最大线程数
        executor.setMaxPoolSize(500);
        // 任务拒绝处理策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 子线程名字前缀
        executor.setThreadNamePrefix("AsyncService");
        // 当调用shutdown时,先执行完当前任务
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setTaskDecorator(new ContextCopyingDecorator());
        executor.initialize();
        return executor;
    }
}
/**
 * 将请求上下文拷贝到子线程中
 */
public class ContextCopyingDecorator implements TaskDecorator {
    @Override
    public Runnable decorate(Runnable runnable) {
        RequestAttributes context = RequestContextHolder.currentRequestAttributes();
        return () -> {
            try {
                RequestContextHolder.setRequestAttributes(context);
                runnable.run();
            } finally {
                RequestContextHolder.resetRequestAttributes();
            }
        };
    }
}

4.使用threadLocal 跟线程绑定 使用完销毁

public abstract class CommonThreadPool {

    /** 
    * token 
    */
   public static ThreadLocal<String> tokenThreadLocal = new ThreadLocal<>();

   /**
    * userInfo 
    */
   private static final ThreadLocal<UserInfo> USER_INFO = new NamedThreadLocal<>("userInfo");
   /**
     * 线程池
     */
   private static ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("service-threadpool-%d").build();
    public static final ExecutorService executorService = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors() + 1
         ,2*Runtime.getRuntime().availableProcessors() + 1,10,TimeUnit.MINUTES
         , new LinkedBlockingQueue<>(GlobalConsts.threadBlockingQueue),threadFactory);

   /**
    * 等待线程池中单个线程结束
    */
   public static Object waitTillThreadFinish(Future<?> thread) {
      try {
         return thread.get();
      } catch (InterruptedException | ExecutionException e) {
         e.printStackTrace();
      }
      return Optional.ofNullable(null);
   }


   public static UserInfo getUserInfo() {
      return USER_INFO.get();
   }
   public static void setUserInfo(UserInfo userInfo){
      USER_INFO.set(userInfo);
   }
   public static void removeUserInfo(){
      USER_INFO.remove();
   }

}
@Configuration
public class CommonConfigurerAdapter extends WebMvcConfigurerAdapter {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new SearchContextHandlerInterceptor());
    }
    public static class SearchContextHandlerInterceptor extends HandlerInterceptorAdapter {
        /**
         * 在DispatcherServlet完全处理完请求后被调用,可用于清理资源等
         */
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
            //清除userInfo信息
            CommonThreadPool.removeUserInfo();
            //清除token信息
            tokenThreadLocal.remove();
        }
    }
}

使用方式

1.设置跟线程绑定的变量 

 tokenThreadLocal.set("token")  或者 CommonThreadPool.setUserInfo(userInfo)  

2.取出和线程相关变量使用

CommonThreadPool.executorService .submit(() -> {

     tokenThreadLocal.get("token") ; 或 getUserInfo();

});

3.清除threadLocal变量

tokenThreadLocal.remove()或CommonThreadPool.removeUserInfo();

Fegin Token统一认证是一种将微服务架构中的认证流程整合和统一管理的解决方案。在微服务架构中,每个服务都需要进行认证和授权操作,传统的方式是在每个服务中都嵌入认证流程,这样会导致代码重复和维护困难。Fegin Token统一认证提供了一种解决方案,通过将认证和授权流程独立出来,让每个微服务只需要调用认证服务获取令牌即可。 Fegin Token统一认证的核心概念是令牌。在整个认证流程中,首先用户提供用户名和密码,认证服务对用户进行验证,并生成一个令牌。然后,其他微服务在调用时只需要携带该令牌,认证服务会对令牌进行验证,并返回结果。这样,每个微服务都可以通过令牌来确认用户的身份和权限。 Fegin Token统一认证的优点首先是减少了重复代码的编写和维护工作量。由于整个认证流程被独立出来,每个微服务不再需要自己实现认证逻辑,只需要调用认证服务即可。其次,这种统一的认证方式提高了系统的灵活性和可扩展性。如果有新的认证方式需要添加,只需要在认证服务中进行修改,而不需要修改每个微服务。 当然,Fegin Token统一认证也存在一些挑战和注意事项。首先,认证服务的性能和可用性对整个系统至关重要,如果认证服务出现故障或性能瓶颈,将影响到所有微服务的正常运行。因此,认证服务的设计和部署需要特别关注。另外,令牌的生成和验证过程需要进行一定程度的加密和安全保护,以防止令牌被窃取或篡改。 综上所述,Fegin Token统一认证是一种将微服务架构中认证流程统一管理的解决方案,通过将认证和授权流程独立出来,提供了代码复用和灵活性的优势。但是,在实际应用中需要注意保证认证服务的性能和安全性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值