介紹一下,在單點登陸平臺集成kubernetes登陸,集成其它系統的登陸原理是同樣的,如grafana, nacos, jenkins等。html
POM引用:java
<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>
系統入口類:web
@SpringBootApplication @EnableZuulProxy @Slf4j public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
controller:spring
這裏要說下,kubernetes的登陸邏輯:json
有兩個login:api
第一個login:https://***:30000/api/v1/csrftoken/login服務器
返回json:cookie
{ "token": "4IJufIQiwOWcaFmQdMMHKQqNEro:1573810029428" }
第二個login:https://**30000/api/v1/login,使用第一個login返回的token進行登陸:網絡
返回json:oracle
{ "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了。