Spring Cloud Open Feign系列【23】OAuth2FeignRequestInterceptor、BasicAuthRequestInterceptor拦截器解析

有道无术,术尚可求,有术无道,止于术。

资料整理来自网络

BasicAuthRequestInterceptor

BasicAuthRequestInterceptor翻译过来就是 Basic 认证请求拦截器。

Basic 认证

Basic认证是一种较为简单的HTTP认证方式,客户端通过明文(Base64编码格式)传输用户名和密码到服务端进行认证,通常需要配合HTTPS来保证信息传输的安全。

比如Security 就支持这种方式,在发送认证请求时,按照以下格式:

// 请求头Authorization 添加Basic 认证信息
AuthorizationBasic 用户名:密码Base64编码格式

使用案例

首先注入一个BasicAuthRequestInterceptor

    @Bean
    BasicAuthRequestInterceptor basicAuthRequestInterceptor(){
        return new BasicAuthRequestInterceptor("zhangsan","123456");
    }

然后使用Feign调用远程服务,可以在日志中看到在消息头中添加了Basic认证信息:

在这里插入图片描述

源码

源码也很简单,就是将用户名密码,经过编码后放入到消息头中:

public class BasicAuthRequestInterceptor implements RequestInterceptor {
    private final String headerValue;

    public BasicAuthRequestInterceptor(String username, String password) {
        this(username, password, Util.ISO_8859_1);
    }

    public BasicAuthRequestInterceptor(String username, String password, Charset charset) {
        Util.checkNotNull(username, "username", new Object[0]);
        Util.checkNotNull(password, "password", new Object[0]);
        this.headerValue = "Basic " + base64Encode((username + ":" + password).getBytes(charset));
    }

    private static String base64Encode(byte[] bytes) {
        return Base64.encode(bytes);
    }

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

OAuth2FeignRequestInterceptor

OAuth2FeignRequestInterceptor属于spring-cloud-security包,可以看到都标记为了过时,这是因为security-oauth2已经快停止维护,换了新的项目,已经正式发布。
在这里插入图片描述

源码分析

先按照顺序看下这个拦截器的处理逻辑,可以看到就是使用一个OAuth2 客户端上下文对象来存储令牌信息,每次请求时,会去校验当前上下文中令牌是否过期或不存在,不可用时,调用配置的认证中心获取令牌,然后将令牌放入到请求头中。

public class OAuth2FeignRequestInterceptor implements RequestInterceptor {
    public static final String BEARER = "Bearer";
    public static final String AUTHORIZATION = "Authorization";
    private final OAuth2ClientContext oAuth2ClientContext;
    private final OAuth2ProtectedResourceDetails resource;
    private final String tokenType;
    private final String header;
    private AccessTokenProvider accessTokenProvider;

    public OAuth2FeignRequestInterceptor(OAuth2ClientContext oAuth2ClientContext, OAuth2ProtectedResourceDetails resource) {
        this(oAuth2ClientContext, resource, "Bearer", "Authorization");
    }

    public OAuth2FeignRequestInterceptor(OAuth2ClientContext oAuth2ClientContext, OAuth2ProtectedResourceDetails resource, String tokenType, String header) {
        this.accessTokenProvider = new AccessTokenProviderChain(Arrays.asList(new AuthorizationCodeAccessTokenProvider(), new ImplicitAccessTokenProvider(), new ResourceOwnerPasswordAccessTokenProvider(), new ClientCredentialsAccessTokenProvider()));
        this.oAuth2ClientContext = oAuth2ClientContext;
        this.resource = resource;
        this.tokenType = tokenType;
        this.header = header;
    }

    public void apply(RequestTemplate template) {
    	// 5. 将Oauth2 令牌放入到消息头中。
        template.header(this.header, new String[0]);
        template.header(this.header, new String[]{this.extract(this.tokenType)});
    }

    protected String extract(String tokenType) {
        OAuth2AccessToken accessToken = this.getToken();
        return String.format("%s %s", tokenType, accessToken.getValue());
    }

    public OAuth2AccessToken getToken() {
    	// 1. 获取 OAuth2令牌 
        OAuth2AccessToken accessToken = this.oAuth2ClientContext.getAccessToken();
        if (accessToken == null || accessToken.isExpired()) {
        	// 2. 如果令牌不存在或者过期,重新获取一个令牌
            try {
                accessToken = this.acquireAccessToken();
            } catch (UserRedirectRequiredException var5) {
                this.oAuth2ClientContext.setAccessToken((OAuth2AccessToken)null);
                String stateKey = var5.getStateKey();
                if (stateKey != null) {
                    Object stateToPreserve = var5.getStateToPreserve();
                    if (stateToPreserve == null) {
                        stateToPreserve = "NONE";
                    }

                    this.oAuth2ClientContext.setPreservedState(stateKey, stateToPreserve);
                }

                throw var5;
            }
        }

        return accessToken;
    }

    protected OAuth2AccessToken acquireAccessToken() throws UserRedirectRequiredException {
    	// 3. 创建一个获取令牌的请求对象
        AccessTokenRequest tokenRequest = this.oAuth2ClientContext.getAccessTokenRequest();
        if (tokenRequest == null) {
            throw new AccessTokenRequiredException("Cannot find valid context on request for resource '" + this.resource.getId() + "'.", this.resource);
        } else {
            String stateKey = tokenRequest.getStateKey();
            if (stateKey != null) {
                tokenRequest.setPreservedState(this.oAuth2ClientContext.removePreservedState(stateKey));
            }

            OAuth2AccessToken existingToken = this.oAuth2ClientContext.getAccessToken();
            if (existingToken != null) {
                this.oAuth2ClientContext.setAccessToken(existingToken);
            }
			// 4. 使用令牌提供者获取一个令牌
            OAuth2AccessToken obtainableAccessToken = this.accessTokenProvider.obtainAccessToken(this.resource, tokenRequest);
            if (obtainableAccessToken != null && obtainableAccessToken.getValue() != null) {
                this.oAuth2ClientContext.setAccessToken(obtainableAccessToken);
                return obtainableAccessToken;
            } else {
                throw new IllegalStateException(" Access token provider returned a null token, which is illegal according to the contract.");
            }
        }
    }

    public void setAccessTokenProvider(AccessTokenProvider accessTokenProvider) {
        this.accessTokenProvider = accessTokenProvider;
    }
}

应用场景

当某个服务是Oauth 2的资源服务器,第三方使用Feign 去访问时,需要携带Oauth 2令牌去访问,这个时候就可以使用当前拦截器添加Oauth 2认证。

使用案例

场景:现在我们有一个微服务项目,采用Oauth 2认证方式,这个时候有个第三方,想通过Feign 调用我们的资源服务器,这个时候就可以让第三方集成我们的 Oauth 2客户端。
在这里插入图片描述
首先需要引入依赖:

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-security</artifactId>
        </dependency>

然后注入 一个 Oauth 2客户端,这里演示使用密码模式。

    @Bean
    OAuth2ClientContext oAuth2ClientContext(){
        return new DefaultOAuth2ClientContext();
    }
    @Bean
    OAuth2FeignRequestInterceptor oAuth2FeignRequestInterceptor(OAuth2ClientContext oAuth2ClientContext, ResourceOwnerPasswordResourceDetails resourceOwnerPasswordResourceDetails){
        return new OAuth2FeignRequestInterceptor(oAuth2ClientContext,resourceOwnerPasswordResourceDetails);
    }
    @Bean
    ResourceOwnerPasswordResourceDetails resourceOwnerPasswordResourceDetails(){
        ResourceOwnerPasswordResourceDetails resourceOwnerPasswordResourceDetails = new ResourceOwnerPasswordResourceDetails();
        resourceOwnerPasswordResourceDetails.setUsername("hnmqet");
        resourceOwnerPasswordResourceDetails.setPassword("123456");
        resourceOwnerPasswordResourceDetails.setClientId("ZD");
        resourceOwnerPasswordResourceDetails.setClientSecret("123456");
        resourceOwnerPasswordResourceDetails.setAccessTokenUri("http://192.168.58.1:21101/oauth/token");
        return resourceOwnerPasswordResourceDetails;
    }

测试,可以看到该拦截器,最终会调用OAuth2AccessTokenSupport去远程获取令牌,然后放在消息头中去发送Feign 请求。

在这里插入图片描述

### 回答1: Qt5是一种流行的跨平台应用程序框架,能够帮助开发者创建高质量、高效率、易于维护的软件。它不仅能够在多个平台上运行,还拥有广泛的功能,使其成为开发者的首选工具之一。 如果您正在学习和使用Qt5,其中一个重要资源是Qt5教程。Qt5教程是一种开发者使用Qt5时的指南,它涵盖了框架的各个方面、核心概念以及实际应用。 目前,Qt5教程 完整版可以在多个网站进行下载,其中一些网站是由Qt开发者社区或其他第三方机构维护的。一些常见的网站包括qt.io、GitHub、official-tutorials.com等。 下载完整版的Qt5教程可为开发人员提供丰富、系统的学习体验。不仅可以快速地掌握框架的基础知识,还可以更好地理解和运用其高级功能。 此外,Qt5教程还包括实践步骤和示例代码,开发者可以使用这些资源加速应用程序的开发和测试过程。 总而言之,Qt5教程完整版提供了一个非常有价值的资源库,可以帮助开发者理解这个强大的应用程序框架,帮助他们为各种不同的目的创建出色的应用程序。 ### 回答2: qt5是一个功能强大的桌面应用程序开发框架,如果您想学习它,您可以搜索到各种Qt5教程。其中最好的一些教程可能会涵盖以下主题: 1. Qt5基础知识:您需要了解Qt5的基本概念、类和对象、信号和槽等基本知识。 2. Qt5 GUI编程:您需要了解如何使用Qt Creator创建GUI应用程序并使用Qt Designer创建UI界面。 3. Qt5高级主题:这可能涉及到Qt5数据库编程、网络编程、多线程编程、OpenGL编程等更高级主题。 如果您需要一个完整的Qt5教程,您可以在互联网上找到很多不错的资源。其中一些资源可能会提供视频教程、在线课程或电子书等。您还可以搜索完整版Qt5教程下载链接,以便离线阅读。 值得注意的是,学习Qt5需要时间和精力,并且可能需要一些编程背景和计算机科学知识。如果您是初学者,您可能需要花费更多的时间来了解基本概念和技术。但无论您的技能水平如何,通过学习Qt5可以帮助您创建高质量的应用程序并加强自己的编程技能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

墨 禹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值