• spring boot zuul集成kubernetes等第三方登录


    介绍一下,在单点登录平台集成kubernetes登录,集成其它系统的登录原理是一样的,如grafana, nacos, jenkins等。

    POM引用:

            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.58</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
            </dependency>

    系统入口类:

    @SpringBootApplication
    @EnableZuulProxy
    @Slf4j
    public class DemoApplication {
    
        public static void main(String[] args) {
    
            SpringApplication.run(DemoApplication.class, args);
    
        }
    }

    controller:

    这里要说下,kubernetes的登录逻辑:

    有两个login:

    第一个login:https://***:30000/api/v1/csrftoken/login

    返回json:

    {
      "token": "4IJufIQiwOWcaFmQdMMHKQqNEro:1573810029428"
     }

    第二个login:https://**30000/api/v1/login,使用第一个login返回的token进行登录:

    返回json:

    {
      "jweToken": "{"protected":"eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIn0","aad":"eyJleHAiOiIyMDE5LTExLTE1VDE5OjI3OjA5WiIsImlhdCI6IjIwMTktMTEtMTVUMDk6Mjc6MDlaIn0","encrypted_key":"Pq5FWwv06JXsPL8H5N3ZAlk7jTGHIGwbdjFdNUoAa6mWn-avNnPiZNjgsDFPZF8cNDgVsN_J-BmranVabxVe9VeJmuDgfY-2KTANslsKF4g2nPdZC3DDmFdLB6V-eXaBduw0ABLGaGisPX_T-3Qxls1sx9EuJ8vvH9a3Fr_iQPxiGxyG_61jSjqqtwXB-sftxULpAuDv84V4Ebba9JLFxCSJnmQ0E8hw3w5Ady1pw3dbPb8q1HnuPEd3Ry27EJYRzswQWkG9CJRxbcQtOzlneW0jBqrbbi-UpVy2vQC2zmdLKACzwkas3l9BBKHJF7xwxUoX-oc4i6NM6HqkwIbSAQ","iv":"HoG3iDTSawS7-xBS","ciphertext":"Ad696v6QmyciUmE_F3xstK6xX9CZAQw1IsKmi9oDwjoVELaBDZOPTfR-Cqub3WHj520Svdubei8QiIC5zg","tag":"8Rf53qa5hBn7jrAiuYqrLw"}",
      "errors": []
     }

    然后我们就可以用jweToken写入cookie 轻松访问k8s了。

     下面的代码显示这这三个步骤:

    package com.cis.jpa.demo.controller;
    
    import com.alibaba.fastjson.JSONObject;
    import com.cis.jpa.demo.dto.LoginResult;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.HttpEntity;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.MediaType;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;
    
    import javax.servlet.http.Cookie;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.net.URLEncoder;
    
    /**
     * @author :hkk
     * @date :Created in 2019/11/15 13:29
     */
    @RestController
    public class K8sController {
        private static final String K8S_ADDR = "https://***:30000/";
    
        @Autowired
        private RestTemplate restTemplate;
        @RequestMapping("/sso")
        public void index(HttpServletResponse response) throws IOException {
    
            String loginToken = getLoginToken();
            LoginResult body = getLoginJweToken(loginToken);
            String code = URLEncoder.encode(body.getJweToken(), "utf-8");
            Cookie cookie = new Cookie("jweToken",code);
            cookie.setPath("/");
            response.addCookie(cookie);
            response.addHeader("jweToken",body.getJweToken());
            response.sendRedirect("/");
    
        }
    
        private LoginResult getLoginJweToken(String loginToken) {
            JSONObject json = new JSONObject();
            json.put("kubeconfig", "");
            json.put("password", "admin");
            json.put("username", "admin");
            json.put("token", "");
    
            HttpHeaders headers = new HttpHeaders();
            MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
            headers.setContentType(type);
            headers.add("Accept", MediaType.APPLICATION_JSON.toString());
            headers.add("x-csrf-token", loginToken);
    
            HttpEntity<String> formEntity = new HttpEntity<>(json.toString(), headers);
    
            String loginUrl = K8S_ADDR + "api/v1/login";
            return restTemplate.postForEntity(loginUrl, formEntity, LoginResult.class).getBody();
        }
    
        private String getLoginToken() {
            String preLoginUrl = K8S_ADDR +"api/v1/csrftoken/login";
    
            LoginResult body = restTemplate.getForEntity(preLoginUrl, LoginResult.class).getBody();
    
            return body.getToken();
        }
        
    }

    这里先用两个restTemplate获取token和jweToken,但由于是https所以,需要配置restTemplate:

    这个类摘自网络,出处找不到了。。。

    package com.cis.jpa.demo.confing;
    
    import org.springframework.http.client.SimpleClientHttpRequestFactory;
    import org.springframework.stereotype.Component;
    
    import javax.net.ssl.*;
    import java.io.IOException;
    import java.net.HttpURLConnection;
    import java.net.InetAddress;
    import java.net.Socket;
    import java.security.cert.X509Certificate;
    
    /**
     * @author :hkk
     * @date :Created in 2019/11/15 11:14
     */
    @Component
    public class HttpsClientRequestFactory extends SimpleClientHttpRequestFactory {
    
        @Override
        protected void prepareConnection(HttpURLConnection connection, String httpMethod) {
            try {
                if (!(connection instanceof HttpsURLConnection)) {
                    throw new RuntimeException("An instance of HttpsURLConnection is expected");
                }
    
                HttpsURLConnection httpsConnection = (HttpsURLConnection) connection;
    
                TrustManager[] trustAllCerts = new TrustManager[]{
                        new X509TrustManager() {
                            @Override
                            public X509Certificate[] getAcceptedIssuers() {
                                return null;
                            }
                            @Override
                            public void checkClientTrusted(X509Certificate[] certs, String authType) {
                            }
                            @Override
                            public void checkServerTrusted(X509Certificate[] certs, String authType) {
                            }
    
                        }
                };
                SSLContext sslContext = SSLContext.getInstance("TLS");
                sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
                httpsConnection.setSSLSocketFactory(new MyCustomSSLSocketFactory(sslContext.getSocketFactory()));
    
                httpsConnection.setHostnameVerifier(new HostnameVerifier() {
                    @Override
                    public boolean verify(String s, SSLSession sslSession) {
                        return true;
                    }
                });
    
                super.prepareConnection(httpsConnection, httpMethod);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        /**
         * We need to invoke sslSocket.setEnabledProtocols(new String[] {"SSLv3"});
         * see http://www.oracle.com/technetwork/java/javase/documentation/cve-2014-3566-2342133.html (Java 8 section)
         */
        // SSLSocketFactory用于创建 SSLSockets
        private static class MyCustomSSLSocketFactory extends SSLSocketFactory {
    
            private final SSLSocketFactory delegate;
    
            public MyCustomSSLSocketFactory(SSLSocketFactory delegate) {
                this.delegate = delegate;
            }
    
            // 返回默认启用的密码套件。除非一个列表启用,对SSL连接的握手会使用这些密码套件。
            // 这些默认的服务的最低质量要求保密保护和服务器身份验证
            @Override
            public String[] getDefaultCipherSuites() {
                return delegate.getDefaultCipherSuites();
            }
    
            // 返回的密码套件可用于SSL连接启用的名字
            @Override
            public String[] getSupportedCipherSuites() {
                return delegate.getSupportedCipherSuites();
            }
    
    
            @Override
            public Socket createSocket(final Socket socket, final String host, final int port,
                                       final boolean autoClose) throws IOException {
                final Socket underlyingSocket = delegate.createSocket(socket, host, port, autoClose);
                return overrideProtocol(underlyingSocket);
            }
    
    
            @Override
            public Socket createSocket(final String host, final int port) throws IOException {
                final Socket underlyingSocket = delegate.createSocket(host, port);
                return overrideProtocol(underlyingSocket);
            }
    
            @Override
            public Socket createSocket(final String host, final int port, final InetAddress localAddress,
                                       final int localPort) throws
                    IOException {
                final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort);
                return overrideProtocol(underlyingSocket);
            }
    
            @Override
            public Socket createSocket(final InetAddress host, final int port) throws IOException {
                final Socket underlyingSocket = delegate.createSocket(host, port);
                return overrideProtocol(underlyingSocket);
            }
    
            @Override
            public Socket createSocket(final InetAddress host, final int port, final InetAddress localAddress,
                                       final int localPort) throws
                    IOException {
                final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort);
                return overrideProtocol(underlyingSocket);
            }
    
            private Socket overrideProtocol(final Socket socket) {
                if (!(socket instanceof SSLSocket)) {
                    throw new RuntimeException("An instance of SSLSocket is expected");
                }
                ((SSLSocket) socket).setEnabledProtocols(new String[]{"TLSv1"});
                return socket;
            }
        }
    }

    再配置restTemplate:

    @Bean
        public RestTemplate restTemplate(HttpsClientRequestFactory factory){
    
            return new RestTemplate(factory);
        }

    restTemplate就可以访问https站点了。

    controller中第三步跳转到“ / ”根目录:

    根目录在是zuul中配置跳转的:

    zuul:
      host:
        max-per-route-connections: 300000
        socket-timeout-millis: 300000
        connect-timeout-millis: 60000
      ignoredServices: '*'
      ignoredPatterns: '/sso'
      routes:
        k8s-config:
          path: /**
          url: https://***:30000/
          stripPrefix: true
          sensitiveHeaders: Cookie,Set-Cookie

    与restTemplate一样,zuul也是默认不支持访问https的,(kubernetes与其它系统的不同点是它是https登录),其他都是http协议。

    zuul网关路由https时,需要对httpclient做一些处理,否则会报错:

    Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

    解决这个问题有两个思络,一是对特定站点进行证书的导入配置,另一种是无论是哪个https站点,都可以访问,这里我们选择第二种:

        @Bean
        public CloseableHttpClient httpClient() throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException {
            SSLConnectionSocketFactory scsf = new SSLConnectionSocketFactory(
                    SSLContexts.custom().loadTrustMaterial(null, new TrustSelfSignedStrategy()).build()
                    , NoopHostnameVerifier.INSTANCE
            );
    
            return HttpClients.custom().setSSLSocketFactory(scsf).build();
        }

    这样我们就可以像访问自己的站点一样,访问k8s dashboard了。

  • 相关阅读:
    jQuery-选择器及属性修改
    jQuery 基础理论
    CSS 之 BFC(块级格式化上下文)
    H5--Web Storage
    H5 -WebWorker
    H5 --拖放
    nodejs Multer中间件
    k8s pod
    kubernetes
    优化CUDA数据传输
  • 原文地址:https://www.cnblogs.com/hankuikui/p/11868408.html
Copyright © 2020-2023  润新知