springcloud gateway作为oauth2客户端


通过对springcloud gateway以及oauth2的进一步了解这里的配置其实是将gateway最为了oauth2的资源服务器使用,适用场景:只需要网关做一些token验证的工作,具体的授权步骤由其他服务完成(前台或者其他后台服务),如果希望网关能够做一些授权步骤并且可以管理token的可以转战我的另一篇文章springcloud gateway作为oauth2的客户端进化篇

gateway配置application.yml

spring:
  # 加上此配置可自动配置安全验证及JWT验证
  autoconfigure:
    exclude: org.springframework.boot.actuate.autoconfigure.security.reactive.ReactiveManagementWebSecurityAutoConfiguration
  security:
    oauth2:
     # 自定义配置安全验证JWT验证时需要配置resourceserver.jwt.jwk-set-uri
      resourceserver:
        jwt.jwk-set-uri: http://zw-pc3-pc:8080/uaa/oauth/jwks
      client:
        registration:
          login-client:
            provider: uaa
            client-id: android
            client-secret: android
            authorization-grant-type: authorization_code
            redirect-uri-template: "{baseUrl}/login/oauth2/code/{registrationId}"
            scope: xx
        provider:
          uaa:
            authorization-uri: http://zw-pc3-pc:8080/uaa/oauth/authorize
            token-uri: http://zw-pc3-pc:8080/uaa/oauth/token
            user-info-uri: http://zw-pc3-pc:8080/uaa/a/user/user
            user-name-attribute: sub
            jwk-set-uri: http://zw-pc3-pc:8080/uaa/oauth/jwks

其网关的配置需要加上过滤器(加上过滤器后会走TokenRelayGatewayFilterFactory过滤器,进行token验证)

 spring:
cloud:
  gateway:
    discovery:
      locator:
        enabled: true
    defaultFilters:
      - PreserveHostHeader
    routes:
      # 消费者
      - id: EUREKA-CONSUMER
        uri: lb://EUREKA-CONSUMER
        predicates:
          - Path=/consumer/**
        filters:
          - StripPrefix=1
          - TokenRelay=

如使用默认自动方式去配置授权路径等会被拦截,因此我们自定义SecurityConfig

package com.example.config.oauth;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.oauth2.server.resource.web.server.ServerBearerTokenAuthenticationConverter;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URI;

/**
 * 安全配置
 * @ EnableWebSecurity 启用web安全配置
 */
@Configuration
public class SecurityConfig{

    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        ServerBearerTokenAuthenticationConverter bearerTokenConverter = new ServerBearerTokenAuthenticationConverter();
        http
                .authorizeExchange().pathMatchers("/uaa/oauth/**","/uaa/login","/oauth/**").permitAll()
                .anyExchange().authenticated()
                .and()
                .formLogin().loginPage("/uaa/oauth/login")
                // 禁用csrf
                .and().csrf().disable();
        http.oauth2ResourceServer().jwt();
        return http.build();
    }

    /**
     * 定义OAuth2请求匹配器
     */
    private static class OAuth2RequestedMatcher implements ServerWebExchangeMatcher {
        @Override
        public Mono<MatchResult> matches(ServerWebExchange serverWebExchange) {
            URI uri = serverWebExchange.getRequest().getURI();
            String path = uri.getPath();
            if(!path.contains("/oauth||/uaa/oauth||uaa/login")){
                return OAuth2RequestedMatcher.MatchResult.match();
            }
            return OAuth2RequestedMatcher.MatchResult.notMatch();
        }
    }
}

【注】:
1、http.oauth2ResourceServer().jwt();一行加了JWT验证需要的过滤器
2、如果没有配置authenticationManager,则会使用默认的JwtReactiveAuthenticationManager进行token验证,authenticationManager中使用的ReactiveJwtDecoder是NimbusReactiveJwtDecoder,NimbusReactiveJwtDecoder默认使用RS256(RSA)加密算法,因之前授权服务器使用的JWT加密算法为HS256(HS256),两者不匹配,因此为了适应gateway,将授权服务器的加密算法也换成了RSA,使用RSA需要生成证书(可使用jdk的keytool生成),授权服务器需提供其证书keys,如:
{
“keys”: [{
“alg”: “RS256”,
“kty”: “RSA”,
“use”: “sig”,
“n”: “AKh0Ckt9phaUql8w1WeqzGiMec1S8aGjwFBBtdinJiGs7Vp2sI7mNtg4RQesHhz7AKAdBKh3JyP6OwgzuDWAtcKA8DZx58GfBUe7lkFwF0OizIqoT/wvnoAeII27G/18vYSakXalz58O2+9d0dWFag4jH8yG0V2v1JrM+GmFxx3JK9o8Im4wvI8ssikV9ZgqT60CDeSCV/3ayICiIyBVxZ11TsxUKdKcmaqTjqjZJZQhRq0uU/Y2Z2Zvq5a7OjNOqyOaDmquxzVeikEK036VErbuc2vjEXA4Vbzoxrn8xSI1rB8p+Wu9qbArk1MQOnPgaruNyA7uKwYL4/nvBl7UDWs=”,
“e”: “AQAB”,
“kid”: “Y2VzaGk=”,
“x5t”: “MIIDpTCCAo2gAwIBAgIEcW54sjANBgkqhkiG9w0BAQsFADCBgjEPMA0GA1UEBgwG5Lit5Zu9MQ8wDQYDVQQIDAbmsrPljZcxDzANBgNVBAcMBumDkeW3njEQMA4GA1UEChMHVW5rbm93bjEtMCsGA1UECwwk5rKz5Y2X5Lit57u055S15a2Q6IKh5Lu95pyJ6ZmQ5YWs5Y+4MQwwCgYDVQQDDAPliJgwHhcNMTkxMTEzMDY1NzEyWhcNMjAxMjE3MDY1NzEyWjCBgjEPMA0GA1UEBgwG5Lit5Zu9MQ8wDQYDVQQIDAbmsrPljZcxDzANBgNVBAcMBumDkeW3njEQMA4GA1UEChMHVW5rbm93bjEtMCsGA1UECwwk5rKz5Y2X5Lit57u055S15a2Q6IKh5Lu95pyJ6ZmQ5YWs5Y+4MQwwCgYDVQQDDAPliJgwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCodApLfaYWlKpfMNVnqsxojHnNUvGho8BQQbXYpyYhrO1adrCO5jbYOEUHrB4c+wCgHQSodycj+jsIM7g1gLXCgPA2cefBnwVHu5ZBcBdDosyKqE/8L56AHiCNuxv9fL2EmpF2pc+fDtvvXdHVhWoOIx/MhtFdr9SazPhphccdySvaPCJuMLyPLLIpFfWYKk+tAg3kglf92siAoiMgVcWddU7MVCnSnJmqk46o2SWUIUatLlP2Nmdmb6uWuzozTqsjmg5qrsc1XopBCtN+lRK27nNr4xFwOFW86Ma5/MUiNawfKflrvamwK5NTEDpz4Gq7jcgO7isGC+P57wZe1A1rAgMBAAGjITAfMB0GA1UdDgQWBBRjfM4BsO63e7UEkI8t3usv2WQ+cDANBgkqhkiG9w0BAQsFAAOCAQEAUGmcVwYRO3gCTOmwL+xu86+lY7xQp5q7nxS9fvOFz1i3Sy2fbg1crKUQ+TL57syDUEMqrnyn928aOv5yWCj4Fejag5rxc5PCzSuZyY3Ac7Up00H41+TcJNq2E/ZUBjEvq85sEPQFxZAsSxg12B4SwOxKIvlTtjUrfCywKSh8hI9IRYrpfxU0lg7CnCoNCFMY811z2HiqgMR7FJmupjpl62ESI+76ut9diCm9gv5kR10qoPpLwSxIVbbMoW4K9TI8AWDQ2fK0V2kPRLjj1ae1w9hKr5I4SN5LDOBl6zMjlaAllnSmW73aBsXyB53+94ws3Bsi2RqMcVpsuSPX29q/Yg==”
}]
}

授权服务器读取证书并配置JWT的JwtAccessTokenConverter

/**
 * token生成处理:指定签名
 */
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
    JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
    try {
        KeyStore keyStore = KeyStore.getInstance("JKS");
        FileInputStream in = new FileInputStream("D:\\keystores\\mykeystore");
        keyStore.load(in,"123456".toCharArray());
        Enumeration aliasEnum = keyStore.aliases();
        String keyAlias = "" ;
        while (aliasEnum.hasMoreElements()) {
            keyAlias = (String) aliasEnum.nextElement();
        }
        PublicKey publicKey = keyStore.getCertificate(keyAlias).getPublicKey();
        //加载私钥,这里填私钥密码
        PrivateKey privateKey = ((KeyStore.PrivateKeyEntry) keyStore.getEntry(keyAlias,
                new KeyStore.PasswordProtection("123456".toCharArray()))).getPrivateKey();
        String _privateKey = Base64.encodeBase64String(privateKey.getEncoded());
        KeyPair keyPair = new KeyPair(publicKey,privateKey);
        accessTokenConverter.setKeyPair(keyPair);
    }catch (Exception e){
        throw new IllegalStateException("Cannot load keys from store: " , e);
    }

    return accessTokenConverter;
}

授权服务器提供jwks.json接口( http://zw-pc3-pc:8080/uaa/oauth/jwks),其中:“n”:公钥模数,“e”:公钥指数,"kid"以及x5t可通过以下代码获取

@Test
public void test() throws Exception {
    KeyStore keyStore = KeyStore.getInstance("JKS");
    FileInputStream in = new FileInputStream("D:\\keystores\\mykeystore");
    keyStore.load(in,"123456".toCharArray());
    Enumeration aliasEnum = keyStore.aliases();
    String keyAlias = "" ;
    while (aliasEnum.hasMoreElements()) {
        keyAlias = (String) aliasEnum.nextElement();
    }
    System.out.println("别名"+keyAlias);

    Certificate cert = keyStore.getCertificate(keyAlias);
    RSAPublicKey publicKey = (RSAPublicKey)cert.getPublicKey();
    String publick = Base64.encodeBase64String(publicKey.getEncoded());
    BigInteger publicModulus = publicKey.getModulus();
    BigInteger publicExponent = publicKey.getPublicExponent();
    System.out.println("publicKey:"+publick);
    System.out.println("publicModulus:"+publicModulus.toString());
    System.out.println("publicExponent:"+publicExponent.toString());
    //加载私钥,这里填私钥密码
    RSAPrivateKey privateKey = (RSAPrivateKey)((KeyStore.PrivateKeyEntry) keyStore.getEntry(keyAlias,
            new KeyStore.PasswordProtection("123456".toCharArray()))).getPrivateKey();
    String _privateKey = Base64.encodeBase64String(privateKey.getEncoded());
    System.out.println("privateKey:"+_privateKey);
    System.out.println("privateModulus:"+privateKey.getModulus());
    System.out.println("n:"+Base64.encodeBase64String(publicModulus.toByteArray()));
    System.out.println("e:"+Base64.encodeBase64String(publicExponent.toByteArray()));
    System.out.println("x5t:"+Base64.encodeBase64String(cert.getEncoded()));
    System.out.println("kid:"+Base64.encodeBase64String(keyAlias.getBytes()));
}

gateway还可以通过其他方式实现oauth客户端

以上使用的是在本地通过授权服务器的/oauth/jwks接口获取公钥在网关本地进行token验证,还可以通过授权服务器的oauth/check_token接口进行token验证,这就需要自定义authenticationManager,暂未实现后面这种方法。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

a_sunny_a

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

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

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

打赏作者

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

抵扣说明:

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

余额充值