You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shardingsphere.apache.org by zh...@apache.org on 2020/08/24 11:58:08 UTC

[shardingsphere] branch master updated: Add AuthenticationResult to refactor AuthenticationEngine (#7037)

This is an automated email from the ASF dual-hosted git repository.

zhangyonglun pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere.git


The following commit(s) were added to refs/heads/master by this push:
     new 5ee3490  Add AuthenticationResult to refactor AuthenticationEngine (#7037)
5ee3490 is described below

commit 5ee3490c688d0174387ed5951ae42e08480b20df
Author: Liang Zhang <te...@163.com>
AuthorDate: Mon Aug 24 19:57:53 2020 +0800

    Add AuthenticationResult to refactor AuthenticationEngine (#7037)
    
    * Refactor MySQLAuthenticationEngine
    
    * Refactor CommandExecutorTask
    
    * Refactor MySQLAuthenticationEngine
    
    * Add AuthenticationResult to refactor AuthenticationEngine
---
 .../frontend/command/CommandExecutorTask.java      | 11 +--
 .../netty/FrontendChannelInboundHandler.java       |  8 ++-
 .../mysql/auth/MySQLAuthenticationEngine.java      | 78 +++++++++++++---------
 .../mysql/MySQLProtocolFrontendEngineTest.java     | 19 +++++-
 .../auth/PostgreSQLAuthenticationEngine.java       | 20 +++---
 .../frontend/engine/AuthenticationEngine.java      |  4 +-
 .../frontend/engine/AuthenticationResult.java      | 67 +++++++++++++++++++
 7 files changed, 151 insertions(+), 56 deletions(-)

diff --git a/shardingsphere-proxy/shardingsphere-proxy-frontend/shardingsphere-proxy-frontend-core/src/main/java/org/apache/shardingsphere/proxy/frontend/command/CommandExecutorTask.java b/shardingsphere-proxy/shardingsphere-proxy-frontend/shardingsphere-proxy-frontend-core/src/main/java/org/apache/shardingsphere/proxy/frontend/command/CommandExecutorTask.java
index fbac5e7..e77bf3f 100644
--- a/shardingsphere-proxy/shardingsphere-proxy-frontend/shardingsphere-proxy-frontend-core/src/main/java/org/apache/shardingsphere/proxy/frontend/command/CommandExecutorTask.java
+++ b/shardingsphere-proxy/shardingsphere-proxy-frontend/shardingsphere-proxy-frontend-core/src/main/java/org/apache/shardingsphere/proxy/frontend/command/CommandExecutorTask.java
@@ -22,7 +22,6 @@ import io.netty.channel.ChannelHandlerContext;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.shardingsphere.control.panel.spi.engine.SingletonFacadeEngine;
-import org.apache.shardingsphere.control.panel.spi.metrics.MetricsHandlerFacade;
 import org.apache.shardingsphere.db.protocol.packet.CommandPacket;
 import org.apache.shardingsphere.db.protocol.packet.CommandPacketType;
 import org.apache.shardingsphere.db.protocol.packet.DatabasePacket;
@@ -66,11 +65,7 @@ public final class CommandExecutorTask implements Runnable {
     public void run() {
         RootInvokeHook rootInvokeHook = new SPIRootInvokeHook();
         rootInvokeHook.start();
-        Supplier<Boolean> histogramSupplier = null;
-        Optional<MetricsHandlerFacade> handlerFacade = SingletonFacadeEngine.buildMetrics();
-        if (handlerFacade.isPresent()) {
-            histogramSupplier = handlerFacade.get().histogramStartTimer(MetricsLabelEnum.REQUEST_LATENCY.getName());
-        }
+        Optional<Supplier<Boolean>> histogramSupplier = SingletonFacadeEngine.buildMetrics().map(facade -> facade.histogramStartTimer(MetricsLabelEnum.REQUEST_LATENCY.getName()));
         int connectionSize = 0;
         boolean isNeedFlush = false;
         try (BackendConnection backendConnection = this.backendConnection;
@@ -91,9 +86,7 @@ public final class CommandExecutorTask implements Runnable {
                 context.flush();
             }
             rootInvokeHook.finish(connectionSize);
-            if (null != histogramSupplier) {
-                histogramSupplier.get();
-            }
+            histogramSupplier.ifPresent(Supplier::get);
         }
     }
     
diff --git a/shardingsphere-proxy/shardingsphere-proxy-frontend/shardingsphere-proxy-frontend-core/src/main/java/org/apache/shardingsphere/proxy/frontend/netty/FrontendChannelInboundHandler.java b/shardingsphere-proxy/shardingsphere-proxy-frontend/shardingsphere-proxy-frontend-core/src/main/java/org/apache/shardingsphere/proxy/frontend/netty/FrontendChannelInboundHandler.java
index e3637d0..792921a 100644
--- a/shardingsphere-proxy/shardingsphere-proxy-frontend/shardingsphere-proxy-frontend-core/src/main/java/org/apache/shardingsphere/proxy/frontend/netty/FrontendChannelInboundHandler.java
+++ b/shardingsphere-proxy/shardingsphere-proxy-frontend/shardingsphere-proxy-frontend-core/src/main/java/org/apache/shardingsphere/proxy/frontend/netty/FrontendChannelInboundHandler.java
@@ -28,6 +28,7 @@ import org.apache.shardingsphere.metrics.enums.MetricsLabelEnum;
 import org.apache.shardingsphere.proxy.backend.communication.jdbc.connection.BackendConnection;
 import org.apache.shardingsphere.proxy.backend.schema.ProxySchemaContexts;
 import org.apache.shardingsphere.proxy.frontend.command.CommandExecutorTask;
+import org.apache.shardingsphere.proxy.frontend.engine.AuthenticationResult;
 import org.apache.shardingsphere.proxy.frontend.executor.ChannelThreadExecutorGroup;
 import org.apache.shardingsphere.proxy.frontend.executor.CommandExecutorSelector;
 import org.apache.shardingsphere.proxy.frontend.spi.DatabaseProtocolFrontendEngine;
@@ -77,7 +78,12 @@ public final class FrontendChannelInboundHandler extends ChannelInboundHandlerAd
     
     private boolean auth(final ChannelHandlerContext context, final ByteBuf message) {
         try (PacketPayload payload = databaseProtocolFrontendEngine.getCodecEngine().createPacketPayload(message)) {
-            return databaseProtocolFrontendEngine.getAuthEngine().auth(context, payload, backendConnection);
+            AuthenticationResult authResult = databaseProtocolFrontendEngine.getAuthEngine().auth(context, payload, backendConnection);
+            if (authResult.isFinished()) {
+                backendConnection.setUserName(authResult.getUsername());
+                backendConnection.setCurrentSchema(authResult.getDatabase());
+            }
+            return authResult.isFinished();
             // CHECKSTYLE:OFF
         } catch (final Exception ex) {
             // CHECKSTYLE:ON
diff --git a/shardingsphere-proxy/shardingsphere-proxy-frontend/shardingsphere-proxy-frontend-mysql/src/main/java/org/apache/shardingsphere/proxy/frontend/mysql/auth/MySQLAuthenticationEngine.java b/shardingsphere-proxy/shardingsphere-proxy-frontend/shardingsphere-proxy-frontend-mysql/src/main/java/org/apache/shardingsphere/proxy/frontend/mysql/auth/MySQLAuthenticationEngine.java
index 5521531..5eef035 100644
--- a/shardingsphere-proxy/shardingsphere-proxy-frontend/shardingsphere-proxy-frontend-mysql/src/main/java/org/apache/shardingsphere/proxy/frontend/mysql/auth/MySQLAuthenticationEngine.java
+++ b/shardingsphere-proxy/shardingsphere-proxy-frontend/shardingsphere-proxy-frontend-mysql/src/main/java/org/apache/shardingsphere/proxy/frontend/mysql/auth/MySQLAuthenticationEngine.java
@@ -35,6 +35,7 @@ import org.apache.shardingsphere.proxy.backend.communication.jdbc.connection.Bac
 import org.apache.shardingsphere.proxy.backend.schema.ProxySchemaContexts;
 import org.apache.shardingsphere.proxy.frontend.ConnectionIdGenerator;
 import org.apache.shardingsphere.proxy.frontend.engine.AuthenticationEngine;
+import org.apache.shardingsphere.proxy.frontend.engine.AuthenticationResult;
 
 import java.net.InetSocketAddress;
 import java.net.SocketAddress;
@@ -51,11 +52,9 @@ public final class MySQLAuthenticationEngine implements AuthenticationEngine {
     
     private int sequenceId;
     
-    private String username;
-    
     private byte[] authResponse;
     
-    private String database;
+    private AuthenticationResult currentAuthResult;
     
     @Override
     public int handshake(final ChannelHandlerContext context) {
@@ -66,47 +65,60 @@ public final class MySQLAuthenticationEngine implements AuthenticationEngine {
     }
     
     @Override
-    public boolean auth(final ChannelHandlerContext context, final PacketPayload payload, final BackendConnection backendConnection) {
+    public AuthenticationResult auth(final ChannelHandlerContext context, final PacketPayload payload, final BackendConnection backendConnection) {
         if (MySQLConnectionPhase.AUTH_PHASE_FAST_PATH == connectionPhase) {
-            MySQLHandshakeResponse41Packet response41 = new MySQLHandshakeResponse41Packet((MySQLPacketPayload) payload);
-            username = response41.getUsername();
-            authResponse = response41.getAuthResponse();
-            database = response41.getDatabase();
-            sequenceId = response41.getSequenceId();
-            if (!Strings.isNullOrEmpty(database) && !ProxySchemaContexts.getInstance().schemaExists(database)) {
-                context.writeAndFlush(new MySQLErrPacket(++sequenceId, MySQLServerErrorCode.ER_BAD_DB_ERROR, database));
-                return false;
-            }
-            if (0 != (response41.getCapabilityFlags() & MySQLCapabilityFlag.CLIENT_PLUGIN_AUTH.getValue())
-                && !MySQLAuthenticationMethod.SECURE_PASSWORD_AUTHENTICATION.getMethodName().equals(response41.getAuthPluginName())) {
-                connectionPhase = MySQLConnectionPhase.AUTHENTICATION_METHOD_MISMATCH;
-                context.writeAndFlush(new MySQLAuthSwitchRequestPacket(++sequenceId,
-                    MySQLAuthenticationMethod.SECURE_PASSWORD_AUTHENTICATION.getMethodName(), authenticationHandler.getAuthPluginData()));
-                return false;
+            currentAuthResult = authPhaseFastPath(context, payload);
+            if (!currentAuthResult.isFinished()) {
+                return currentAuthResult;
             }
         } else if (MySQLConnectionPhase.AUTHENTICATION_METHOD_MISMATCH == connectionPhase) {
-            MySQLAuthSwitchResponsePacket authSwitchResponsePacket = new MySQLAuthSwitchResponsePacket((MySQLPacketPayload) payload);
-            sequenceId = authSwitchResponsePacket.getSequenceId();
-            authResponse = authSwitchResponsePacket.getAuthPluginResponse();
+            authenticationMethodMismatch((MySQLPacketPayload) payload);
         }
-        Optional<MySQLServerErrorCode> errorCode = authenticationHandler.login(username, authResponse, database);
+        Optional<MySQLServerErrorCode> errorCode = authenticationHandler.login(currentAuthResult.getUsername(), authResponse, currentAuthResult.getDatabase());
         if (errorCode.isPresent()) {
-            context.writeAndFlush(getMySQLErrPacket(errorCode.get(), context));
+            context.writeAndFlush(createMySQLErrPacket(errorCode.get(), context));
         } else {
-            backendConnection.setCurrentSchema(database);
-            backendConnection.setUserName(username);
+            backendConnection.setCurrentSchema(currentAuthResult.getDatabase());
+            backendConnection.setUserName(currentAuthResult.getUsername());
             context.writeAndFlush(new MySQLOKPacket(++sequenceId));
         }
-        return true;
+        return AuthenticationResult.finished(currentAuthResult.getUsername(), currentAuthResult.getDatabase());
     }
     
-    private MySQLErrPacket getMySQLErrPacket(final MySQLServerErrorCode errorCode, final ChannelHandlerContext context) {
-        if (MySQLServerErrorCode.ER_DBACCESS_DENIED_ERROR == errorCode) {
-            return new MySQLErrPacket(++sequenceId, MySQLServerErrorCode.ER_DBACCESS_DENIED_ERROR, username, getHostAddress(context), database);
-        } else {
-            return new MySQLErrPacket(++sequenceId, MySQLServerErrorCode.ER_ACCESS_DENIED_ERROR, username, getHostAddress(context),
-                    0 == authResponse.length ? "NO" : "YES");
+    private AuthenticationResult authPhaseFastPath(final ChannelHandlerContext context, final PacketPayload payload) {
+        MySQLHandshakeResponse41Packet packet = new MySQLHandshakeResponse41Packet((MySQLPacketPayload) payload);
+        authResponse = packet.getAuthResponse();
+        sequenceId = packet.getSequenceId();
+        if (!Strings.isNullOrEmpty(packet.getDatabase()) && !ProxySchemaContexts.getInstance().schemaExists(packet.getDatabase())) {
+            context.writeAndFlush(new MySQLErrPacket(++sequenceId, MySQLServerErrorCode.ER_BAD_DB_ERROR, packet.getDatabase()));
+            return AuthenticationResult.continued();
         }
+        if (isClientPluginAuth(packet) && !MySQLAuthenticationMethod.SECURE_PASSWORD_AUTHENTICATION.getMethodName().equals(packet.getAuthPluginName())) {
+            connectionPhase = MySQLConnectionPhase.AUTHENTICATION_METHOD_MISMATCH;
+            context.writeAndFlush(new MySQLAuthSwitchRequestPacket(++sequenceId, MySQLAuthenticationMethod.SECURE_PASSWORD_AUTHENTICATION.getMethodName(), authenticationHandler.getAuthPluginData()));
+            return AuthenticationResult.continued();
+        }
+        return AuthenticationResult.finished(packet.getUsername(), packet.getDatabase());
+    }
+    
+    private boolean isClientPluginAuth(final MySQLHandshakeResponse41Packet packet) {
+        return 0 != (packet.getCapabilityFlags() & MySQLCapabilityFlag.CLIENT_PLUGIN_AUTH.getValue());
+    }
+    
+    private void authenticationMethodMismatch(final MySQLPacketPayload payload) {
+        MySQLAuthSwitchResponsePacket packet = new MySQLAuthSwitchResponsePacket(payload);
+        sequenceId = packet.getSequenceId();
+        authResponse = packet.getAuthPluginResponse();
+    }
+    
+    private MySQLErrPacket createMySQLErrPacket(final MySQLServerErrorCode errorCode, final ChannelHandlerContext context) {
+        return MySQLServerErrorCode.ER_DBACCESS_DENIED_ERROR == errorCode
+                ? new MySQLErrPacket(++sequenceId, MySQLServerErrorCode.ER_DBACCESS_DENIED_ERROR, currentAuthResult.getUsername(), getHostAddress(context), currentAuthResult.getDatabase())
+                : new MySQLErrPacket(++sequenceId, MySQLServerErrorCode.ER_ACCESS_DENIED_ERROR, currentAuthResult.getUsername(), getHostAddress(context), getErrorMessage());
+    }
+    
+    private String getErrorMessage() {
+        return 0 == authResponse.length ? "NO" : "YES";
     }
     
     private String getHostAddress(final ChannelHandlerContext context) {
diff --git a/shardingsphere-proxy/shardingsphere-proxy-frontend/shardingsphere-proxy-frontend-mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/MySQLProtocolFrontendEngineTest.java b/shardingsphere-proxy/shardingsphere-proxy-frontend/shardingsphere-proxy-frontend-mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/MySQLProtocolFrontendEngineTest.java
index 826b385..5357a6a 100644
--- a/shardingsphere-proxy/shardingsphere-proxy-frontend/shardingsphere-proxy-frontend-mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/MySQLProtocolFrontendEngineTest.java
+++ b/shardingsphere-proxy/shardingsphere-proxy-frontend/shardingsphere-proxy-frontend-mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/MySQLProtocolFrontendEngineTest.java
@@ -37,6 +37,7 @@ import org.apache.shardingsphere.kernel.context.schema.ShardingSphereSchema;
 import org.apache.shardingsphere.proxy.backend.communication.jdbc.connection.BackendConnection;
 import org.apache.shardingsphere.proxy.backend.schema.ProxySchemaContexts;
 import org.apache.shardingsphere.proxy.frontend.ConnectionIdGenerator;
+import org.apache.shardingsphere.proxy.frontend.engine.AuthenticationResult;
 import org.apache.shardingsphere.proxy.frontend.mysql.auth.MySQLAuthenticationEngine;
 import org.junit.Before;
 import org.junit.Test;
@@ -53,6 +54,9 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.Properties;
 
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.isA;
@@ -101,7 +105,10 @@ public final class MySQLProtocolFrontendEngineTest {
         ProxyUser proxyUser = new ProxyUser("", Collections.singleton("db1"));
         setAuthentication(proxyUser);
         when(payload.readStringNul()).thenReturn("root");
-        assertTrue(mysqlProtocolFrontendEngine.getAuthEngine().auth(context, payload, mock(BackendConnection.class)));
+        AuthenticationResult actual = mysqlProtocolFrontendEngine.getAuthEngine().auth(context, payload, mock(BackendConnection.class));
+        assertThat(actual.getUsername(), is("root"));
+        assertNull(actual.getDatabase());
+        assertTrue(actual.isFinished());
         verify(context).writeAndFlush(isA(MySQLOKPacket.class));
     }
     
@@ -114,7 +121,10 @@ public final class MySQLProtocolFrontendEngineTest {
         when(payload.readStringNulByBytes()).thenReturn("root".getBytes());
         when(channel.remoteAddress()).thenReturn(new InetSocketAddress("localhost", 3307));
         when(context.channel()).thenReturn(channel);
-        assertTrue(mysqlProtocolFrontendEngine.getAuthEngine().auth(context, payload, mock(BackendConnection.class)));
+        AuthenticationResult actual = mysqlProtocolFrontendEngine.getAuthEngine().auth(context, payload, mock(BackendConnection.class));
+        assertThat(actual.getUsername(), is("root"));
+        assertNull(actual.getDatabase());
+        assertTrue(actual.isFinished());
         verify(context).writeAndFlush(isA(MySQLErrPacket.class));
     }
 
@@ -128,7 +138,10 @@ public final class MySQLProtocolFrontendEngineTest {
         when(payload.readStringNulByBytes()).thenReturn("root".getBytes());
         when(context.channel()).thenReturn(channel);
         when(channel.remoteAddress()).thenReturn(new InetSocketAddress(InetAddress.getByAddress(new byte[] {(byte) 192, (byte) 168, (byte) 0, (byte) 102}), 3307));
-        assertTrue(mysqlProtocolFrontendEngine.getAuthEngine().auth(context, payload, mock(BackendConnection.class)));
+        AuthenticationResult actual = mysqlProtocolFrontendEngine.getAuthEngine().auth(context, payload, mock(BackendConnection.class));
+        assertThat(actual.getUsername(), is("root"));
+        assertNull(actual.getDatabase());
+        assertTrue(actual.isFinished());
         verify(context).writeAndFlush(argThat(
                 (ArgumentMatcher<MySQLErrPacket>) argument -> "Access denied for user 'root'@'192.168.0.102' (using password: YES)".equals(argument.getErrorMessage())));
     }
diff --git a/shardingsphere-proxy/shardingsphere-proxy-frontend/shardingsphere-proxy-frontend-postgresql/src/main/java/org/apache/shardingsphere/proxy/frontend/postgresql/auth/PostgreSQLAuthenticationEngine.java b/shardingsphere-proxy/shardingsphere-proxy-frontend/shardingsphere-proxy-frontend-postgresql/src/main/java/org/apache/shardingsphere/proxy/frontend/postgresql/auth/PostgreSQLAuthenticationEngine.java
index 17131af..ce3aa8b 100644
--- a/shardingsphere-proxy/shardingsphere-proxy-frontend/shardingsphere-proxy-frontend-postgresql/src/main/java/org/apache/shardingsphere/proxy/frontend/postgresql/auth/PostgreSQLAuthenticationEngine.java
+++ b/shardingsphere-proxy/shardingsphere-proxy-frontend/shardingsphere-proxy-frontend-postgresql/src/main/java/org/apache/shardingsphere/proxy/frontend/postgresql/auth/PostgreSQLAuthenticationEngine.java
@@ -36,6 +36,7 @@ import org.apache.shardingsphere.proxy.backend.communication.jdbc.connection.Bac
 import org.apache.shardingsphere.proxy.backend.schema.ProxySchemaContexts;
 import org.apache.shardingsphere.proxy.frontend.ConnectionIdGenerator;
 import org.apache.shardingsphere.proxy.frontend.engine.AuthenticationEngine;
+import org.apache.shardingsphere.proxy.frontend.engine.AuthenticationResult;
 
 import java.util.concurrent.atomic.AtomicBoolean;
 
@@ -56,6 +57,8 @@ public final class PostgreSQLAuthenticationEngine implements AuthenticationEngin
     
     private volatile byte[] md5Salt;
     
+    private AuthenticationResult currentAuthResult;
+    
     @Override
     public int handshake(final ChannelHandlerContext context) {
         int result = ConnectionIdGenerator.getInstance().nextId();
@@ -64,10 +67,10 @@ public final class PostgreSQLAuthenticationEngine implements AuthenticationEngin
     }
     
     @Override
-    public boolean auth(final ChannelHandlerContext context, final PacketPayload payload, final BackendConnection backendConnection) {
+    public AuthenticationResult auth(final ChannelHandlerContext context, final PacketPayload payload, final BackendConnection backendConnection) {
         if (SSL_REQUEST_PAYLOAD_LENGTH == payload.getByteBuf().markReaderIndex().readInt() && SSL_REQUEST_CODE == payload.getByteBuf().readInt()) {
             context.writeAndFlush(new PostgreSQLSSLNegativePacket());
-            return false;
+            return AuthenticationResult.continued();
         }
         payload.getByteBuf().resetReaderIndex();
         if (!startupMessageReceived.get()) {
@@ -79,7 +82,7 @@ public final class PostgreSQLAuthenticationEngine implements AuthenticationEngin
                     String.format("database \"%s\" does not exist", databaseName));
                 context.writeAndFlush(responsePacket);
                 context.close();
-                return false;
+                return AuthenticationResult.continued();
             }
             backendConnection.setCurrentSchema(databaseName);
             String userName = comStartupPacket.getParametersMap().get(USER_NAME_KEYWORD);
@@ -88,12 +91,12 @@ public final class PostgreSQLAuthenticationEngine implements AuthenticationEngin
                     "user not set in StartupMessage");
                 context.writeAndFlush(responsePacket);
                 context.close();
-                return false;
+                return AuthenticationResult.continued();
             }
             backendConnection.setUserName(userName);
             md5Salt = PostgreSQLRandomGenerator.getInstance().generateRandomBytes(4);
             context.writeAndFlush(new PostgreSQLAuthenticationMD5PasswordPacket(md5Salt));
-            return false;
+            return AuthenticationResult.continued(userName, databaseName);
         } else {
             char messageType = (char) ((PostgreSQLPacketPayload) payload).readInt1();
             if ('p' != messageType) {
@@ -101,7 +104,8 @@ public final class PostgreSQLAuthenticationEngine implements AuthenticationEngin
                     "PasswordMessage is expected, message type 'p', but not '" + messageType + "'");
                 context.writeAndFlush(responsePacket);
                 context.close();
-                return false;
+                currentAuthResult = AuthenticationResult.continued();
+                return currentAuthResult;
             }
             PostgreSQLPasswordMessagePacket passwordMessagePacket = new PostgreSQLPasswordMessagePacket((PostgreSQLPacketPayload) payload);
             PostgreSQLLoginResult loginResult = PostgreSQLAuthenticationHandler.loginWithMd5Password(
@@ -111,7 +115,7 @@ public final class PostgreSQLAuthenticationEngine implements AuthenticationEngin
                     loginResult.getErrorMessage());
                 context.writeAndFlush(responsePacket);
                 context.close();
-                return false;
+                return AuthenticationResult.continued();
             } else {
                 // TODO implement PostgreSQLServerInfo like MySQLServerInfo
                 context.write(new PostgreSQLAuthenticationOKPacket(true));
@@ -119,7 +123,7 @@ public final class PostgreSQLAuthenticationEngine implements AuthenticationEngin
                 context.write(new PostgreSQLParameterStatusPacket("client_encoding", "UTF8"));
                 context.write(new PostgreSQLParameterStatusPacket("server_encoding", "UTF8"));
                 context.writeAndFlush(new PostgreSQLReadyForQueryPacket());
-                return true;
+                return AuthenticationResult.finished(currentAuthResult.getUsername(), currentAuthResult.getDatabase());
             }
         }
     }
diff --git a/shardingsphere-proxy/shardingsphere-proxy-frontend/shardingsphere-proxy-frontend-spi/src/main/java/org/apache/shardingsphere/proxy/frontend/engine/AuthenticationEngine.java b/shardingsphere-proxy/shardingsphere-proxy-frontend/shardingsphere-proxy-frontend-spi/src/main/java/org/apache/shardingsphere/proxy/frontend/engine/AuthenticationEngine.java
index 75df941..dfa1f70 100644
--- a/shardingsphere-proxy/shardingsphere-proxy-frontend/shardingsphere-proxy-frontend-spi/src/main/java/org/apache/shardingsphere/proxy/frontend/engine/AuthenticationEngine.java
+++ b/shardingsphere-proxy/shardingsphere-proxy-frontend/shardingsphere-proxy-frontend-spi/src/main/java/org/apache/shardingsphere/proxy/frontend/engine/AuthenticationEngine.java
@@ -40,7 +40,7 @@ public interface AuthenticationEngine {
      * @param context channel handler context
      * @param payload packet payload
      * @param backendConnection backend connection
-     * @return auth finish or not
+     * @return authentication result
      */
-    boolean auth(ChannelHandlerContext context, PacketPayload payload, BackendConnection backendConnection);
+    AuthenticationResult auth(ChannelHandlerContext context, PacketPayload payload, BackendConnection backendConnection);
 }
diff --git a/shardingsphere-proxy/shardingsphere-proxy-frontend/shardingsphere-proxy-frontend-spi/src/main/java/org/apache/shardingsphere/proxy/frontend/engine/AuthenticationResult.java b/shardingsphere-proxy/shardingsphere-proxy-frontend/shardingsphere-proxy-frontend-spi/src/main/java/org/apache/shardingsphere/proxy/frontend/engine/AuthenticationResult.java
new file mode 100644
index 0000000..1b6edab
--- /dev/null
+++ b/shardingsphere-proxy/shardingsphere-proxy-frontend/shardingsphere-proxy-frontend-spi/src/main/java/org/apache/shardingsphere/proxy/frontend/engine/AuthenticationResult.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shardingsphere.proxy.frontend.engine;
+
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+/**
+ * Authentication result.
+ */
+@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+@Getter
+public final class AuthenticationResult {
+    
+    private final String username;
+    
+    private final String database;
+    
+    private final boolean finished;
+    
+    /**
+     * Create finished authentication result.
+     * 
+     * @param username username
+     * @param database database
+     * @return finished authentication result
+     */
+    public static AuthenticationResult finished(final String username, final String database) {
+        return new AuthenticationResult(username, database, true);
+    }
+    
+    /**
+     * Create continued authentication result.
+     *
+     * @return continued authentication result
+     */
+    public static AuthenticationResult continued() {
+        return new AuthenticationResult(null, null, false);
+    }
+    
+    /**
+     * Create continued authentication result.
+     * 
+     * @param username username
+     * @param database database
+     * @return continued authentication result
+     */
+    public static AuthenticationResult continued(final String username, final String database) {
+        return new AuthenticationResult(username, database, false);
+    }
+}