Spring Cloud進階之路 | 十:服務網關整合安全框架(zuul+ Spring Cloud Oauth2)


轉載請注明作者及出處:

作者:銀河架構師

原文鏈接:https://www.cnblogs.com/luas/p/12201460.html

前言

Spring Cloud進階之路 | 七:服務網關(zuul)一文中,已詳細闡述了網關的作用及重要性,其不僅僅可進行路由轉發和請求過濾,還可以進行權限校驗、審計、接口監控、限流以及日志收集等邏輯。

要完成這些額外邏輯,需集成其它框架,或者開發相應的過濾器,亦或是代碼邏輯。本文就以整合安全框架中為例,演示如何在網關進行身份認證。

前面說過,微服務架構中,把一個大型的單個應用程序和服務拆分為數個甚至數十個的支持微服務,為了保證高可用,每個微服務都可能會部署集群。

根據之前文章Spring Cloud進階之路 | 八:授權服務(Spring Cloud Oauth2)Spring Cloud進階之路 | 九:資源服務(Spring Cloud Oauth2)的闡述,拆分后的單個服務均為資源服務器,提供資源服務。

既然是對外提供資源服務,勢必會引起一個問題,即安全問題。此時,便有兩種方案:各資源服務自行處理、統一交由網關處理,各資源服務只關注業務。

如果各資源服務自行處理,微服務拆分后,每個服務均需處理相同安全邏輯,工作量嚴重重復。即便提取公共部分,也解決不了大問題。因為公共部分只能解決身份認證問題,解決不了鑒權,每個資源服務權限都不盡相同。

統一交由網關處理,網關本身作為資源服務,先期直接進行身份認證,身份認證通過之后再進行鑒權,均通過以后再執行后續邏輯。此時,各資源服務只用關注自身業務,無需處理這些繁瑣的安全策略。

至於權限問題,可開發統一的權限支撐平台,統一管理用戶及所有資源服務的權限,服務上線自動注冊資源到權限支撐平台,再由維護人員統一分配即可。

 

准備工作

 

復用之前文章Spring Cloud進階之路 | 七:服務網關(zuul)Spring Cloud進階之路 | 八:授權服務(Spring Cloud Oauth2)Spring Cloud進階之路 | 九:資源服務(Spring Cloud Oauth2)的工程。

 

改造xmall-product

 

據前文所述,各資源服務不再作為授權服務相對的資源服務而存在,所以,需要刪除Spring Cloud Oauth2相關內容。

 

刪除Spring Cloud Oauth2依賴

修改后的pom文件如下。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion><parent>
    <groupId>com.luas.cloud</groupId>
    <artifactId>java-boot-parent-2.1</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <relativePath>../../java-boot-parent-2.1</relativePath>
  </parent><groupId>com.luas.cloud</groupId>
  <artifactId>xmall-product</artifactId>
  <version>1.0.0-SNAPSHOT</version><name>xmall-product</name>
  <description>Spring Cloud Learning,nacos-client</description><dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency><!-- nacos cloud -->
    <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    </dependency><dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency><dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies><build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build></project>

 

刪除oauth2資源配置

刪除application.yml中oauth2資源配置信息,刪除后內容如下。

server:
  port: 8080

 

刪除ResourceServer相關配置

如啟動類有@EnableResourceServer注解,則刪除。

package com.luas.xmall;
​
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
​
@SpringBootApplication
public class XmallProductApplication {
​
  public static void main(String[] args) {
    SpringApplication.run(XmallProductApplication.class, args);
  }
​
}

 

如存在ResourceServerConfiguration配置類,則刪除。

package com.luas.xmall.configuration;
​
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
​
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
                .requestMatchers()
                .anyRequest()
                .and()
                .authorizeRequests()
                .antMatchers("/application").permitAll()
                .anyRequest()
                .authenticated();
    }
}

 

改造網關

添加Spring Cloud Oauth2依賴

添加spring-cloud-starter-oauth2依賴,修改后的pom文件如下。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion><parent>
        <groupId>com.luas.cloud</groupId>
        <artifactId>java-boot-parent-2.1</artifactId>
        <version>1.0.0-SNAPSHOT</version>
        <relativePath>../../java-boot-parent-2.1</relativePath>
    </parent><groupId>com.luas.xmall</groupId>
    <artifactId>xmall-zuul</artifactId>
    <version>1.0.0-SNAPSHOT</version><name>xmall-zuul</name>
    <description>網關服務</description><properties>
    </properties><dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency><dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency><!-- nacos cloud -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency><dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency><dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
​
​
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build></project>

 

修改application配置

添加授權服務器路由規則、oauth2資源配置

server:
  port: 5566
​
zuul:
  prefix: /gateway
  sensitive-headers:
  routes:
    auth:
      path: /auth/**
      service-id: xmall-auth
      strip-prefix: true
    product:
      path: /product/**
      service-id: xmall-product
      strip-prefix: true
​
security:
  oauth2:
    resource:
      user-info-uri: http://localhost:7777/oauth/user
      prefer-token-info: false

 

添加ResourceServerConfiguration配置類

此處有一個點,比較重要:為什么不直接在啟動類添加@EnableResourceServer注解開啟資源服務,而要特殊定義資源服務配置?

原因就在於,默認資源服務器安全策略,所以請求均需身份認證,那么獲取token、token校驗、token key獲取等這些無需身份認證即可訪問的端點,也會被攔截。所以,需要特殊定義資源服務安全策略,放開這些公共端點

ResourceServerConfiguration類如下。

package com.luas.xmall.gateway.configuration;
​
import com.luas.xmall.gateway.filter.PreAuthenticationFilter;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
​
@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
​
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
                .requestMatchers()
                .anyRequest()
                .and()
                .authorizeRequests()
                .antMatchers("/application").permitAll()
                // 放開token授權、token key獲取、token驗證端點
                .antMatchers(PreAuthenticationFilter.TOKEN_ENDPOINT).permitAll()
                .antMatchers(PreAuthenticationFilter.TOKEN_KEY_ENDPOINT).permitAll()
                .antMatchers(PreAuthenticationFilter.CHECK_TOKEN_ENDPOINT).permitAll()
                .anyRequest()
                .authenticated();
    }
}

 

改造PreAuthenticationFilter

同ResourceServerConfiguration,前置安全校驗也需要放開公共端點。另外,此過濾器還可進行相關權限攔截、用戶信息解析並傳遞等邏輯。至於其它的,如接口審計,可單獨創建過濾器處理,不過需要注意執行順序及過濾器類型

package com.luas.xmall.gateway.filter;
​
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.provider.authentication.BearerTokenExtractor;
import org.springframework.security.oauth2.provider.authentication.TokenExtractor;
import org.springframework.stereotype.Component;
​
import javax.servlet.http.HttpServletRequest;
​
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;
​
@Component
public class PreAuthenticationFilter extends ZuulFilter {
​
    public static final String TOKEN_ENDPOINT = "/gateway/auth/oauth/token";
​
    public static final String TOKEN_KEY_ENDPOINT = "/gateway/auth/oauth/token_key";
​
    public static final String CHECK_TOKEN_ENDPOINT = "/gateway/auth/oauth/check_token";
​
    private Logger logger = LoggerFactory.getLogger(getClass());
​
    private TokenExtractor tokenExtractor = new BearerTokenExtractor();
​
    @Override
    public String filterType() {
        return PRE_TYPE;
    }
​
    @Override
    public int filterOrder() {
        return 0;
    }
​
    @Override
    public boolean shouldFilter() {
        RequestContext requestContext = RequestContext.getCurrentContext();
​
        HttpServletRequest request = requestContext.getRequest();
​
        String requestURI = request.getRequestURI();
​
        return !(requestURI.equals(TOKEN_ENDPOINT) && requestURI.equals(TOKEN_KEY_ENDPOINT) && requestURI.equals(CHECK_TOKEN_ENDPOINT));
    }
​
    @Override
    public Object run() throws ZuulException {
        RequestContext requestContext = RequestContext.getCurrentContext();
​
        HttpServletRequest request = requestContext.getRequest();
​
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("uri {}", request.getRequestURI());
        }
​
        Authentication authentication = this.tokenExtractor.extract(request);
​
        if (authentication == null || authentication.getPrincipal() == null) {
            //不會繼續往下執行 不會調用服務接口了 網關直接響應給客戶了
            requestContext.setSendZuulResponse(false);
            requestContext.setResponseBody("Full authentication is required to access this resource");
            requestContext.setResponseStatusCode(401);
            return null;
        }
​
        String accessToken = (String) authentication.getPrincipal();
​
        this.logger.info("token {}", accessToken);
​
        // todo 解析token,調用權限支撐平台,獲取權限信息,組織到用戶信息中傳遞給下游微服務
return null;
    }
}

 

驗證

依次啟動xmall-auth、xmall-product、xmall-zuul,端口分別為7777、8080、5566。

先直接訪問http://localhost:8080/sku/1122,可正常訪問。

通過網關訪問http://localhost:5566/gateway/product/sku/1122,發現已不能正常訪問,需要先進行身份認證。

通過網關調用授權服務,進行授權,地址為:

http://localhost:5566/gateway/auth/oauth/token?client_id=client_1&client_secret=123456&username=user_1&password=123456&scope=server&grant_type=password。

請求之后,正常返回授權結果。

此時,說明網關改造成功,已正常發揮資源服務器作用,針對所請求資源聯合授權服務器進行身份認證。

header方式攜帶授權,重新訪問http://localhost:8080/sku/1122,可正常訪問。

網關整合安全框架以在網關進行身份認證完成,其它功能如日志收集、接口審計、鑒權等,讀者可自行去參整合實現。后續也會出相關文章,對部分功能進行整合說明。

 

源碼

github

https://github.com/liuminglei/SpringCloudLearning/tree/master/10/

gitee

https://gitee.com/xbd521/SpringCloudLearning/tree/master/10/

 

 

微信搜索【銀河架構師】,發現更多精彩內容。

技術資料領取方法:關注公眾號,回復微服務,領取微服務相關電子書;回復MK精講,領取MK精講系列電子書;回復JAVA 進階,領取JAVA進階知識相關電子書;回復JAVA面試,領取JAVA面試相關電子書,回復JAVA WEB領取JAVA WEB相關電子書。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM