Initial import
This commit is contained in:
Родитель
809a7a0234
Коммит
084f9cb671
|
@ -0,0 +1,32 @@
|
|||
# README
|
||||
|
||||
## How to run the test?
|
||||
|
||||
You can either run the test using its main method and then you pass in the JDBC URL directly, or if you are running the test using JUnit integration you will have to pass the JDBC URL using the -Durl=xxx syntax.
|
||||
|
||||
## What does the JDBC URL look like?
|
||||
|
||||
```
|
||||
jdbc:mysql://hostname:portNumber/databaseName?sslMode=REQUIRED&defaultAuthenticationPlugin=com.azure.mysql.auth.plugin.AzureMySqlMSIAuthenticationPlugin&authenticationPlugins=com.azure.mysql.auth.plugin.AzureMySqlMSIAuthenticationPlugin&user=username
|
||||
```
|
||||
|
||||
### Required modifiable properties
|
||||
|
||||
1. `hostname` is the hostname of the MySQL instance
|
||||
2. `portNumber` is the port number of the MySQL instance
|
||||
3. `databaseName` is the name of the MySQL database to connect to
|
||||
4. `username` is the username to connect with (format should be `user@tenant@hostname-only`)
|
||||
|
||||
Note `hostname-only` is the first part of the FQDN hostname.
|
||||
|
||||
### Required fixed properties
|
||||
|
||||
* `sslMode` needs to be set to REQUIRED.
|
||||
* `defaultAuthenticationPlugin` needs to be set to the implementing class name in this case `com.azure.mysql.auth.plugin.AzureMySqlMSIAuthenticationPlugin`
|
||||
* `authenticationPlugins` needs to be set to `com.azure.mysql.auth.plugin.AzureMySqlMSIAuthenticationPlugin`
|
||||
|
||||
## Background information
|
||||
|
||||
* https://docs.microsoft.com/en-us/azure/mysql/howto-connect-with-managed-identity
|
||||
* https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-connp-props-authentication.html
|
||||
* https://techcommunity.microsoft.com/t5/azure-database-for-mysql-blog/how-to-connect-to-azure-database-for-mysql-using-managed/ba-p/1518196#:~:text=%20How%20to%20connect%20to%20Azure%20Database%20for,the%20Managed%20Identity%20GUID%20and%20then...%20More%20
|
|
@ -0,0 +1,45 @@
|
|||
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.azure.mysql</groupId>
|
||||
<artifactId>azure-mysql-auth-plugin</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
<name>azure-mysql-auth-plugin</name>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.10.0</version>
|
||||
<configuration>
|
||||
<release>8</release>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.azure</groupId>
|
||||
<artifactId>azure-identity</artifactId>
|
||||
<version>1.4.5</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>8.0.28</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.13.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
</project>
|
|
@ -0,0 +1,150 @@
|
|||
package com.azure.mysql.auth.plugin;
|
||||
|
||||
import com.azure.core.credential.AccessToken;
|
||||
import com.azure.core.credential.TokenCredential;
|
||||
import com.azure.core.credential.TokenRequestContext;
|
||||
import com.azure.identity.AzureCliCredentialBuilder;
|
||||
import com.azure.identity.ChainedTokenCredentialBuilder;
|
||||
import com.azure.identity.ManagedIdentityCredential;
|
||||
import com.azure.identity.ManagedIdentityCredentialBuilder;
|
||||
import com.mysql.cj.callback.MysqlCallbackHandler;
|
||||
import com.mysql.cj.callback.UsernameCallback;
|
||||
import com.mysql.cj.protocol.AuthenticationPlugin;
|
||||
import com.mysql.cj.protocol.Protocol;
|
||||
import com.mysql.cj.protocol.a.NativeConstants;
|
||||
import com.mysql.cj.protocol.a.NativePacketPayload;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The Authentication plugin that enables Azure AD managed identity support.
|
||||
*/
|
||||
public class AzureMySqlMSIAuthenticationPlugin implements AuthenticationPlugin<NativePacketPayload> {
|
||||
|
||||
/**
|
||||
* Stores the access token.
|
||||
*/
|
||||
private AccessToken accessToken;
|
||||
|
||||
/**
|
||||
* Stores the callback handler.
|
||||
*/
|
||||
private MysqlCallbackHandler callbackHandler;
|
||||
|
||||
/**
|
||||
* Stores the protocol.
|
||||
*/
|
||||
private Protocol<NativePacketPayload> protocol;
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProtocolPluginName() {
|
||||
return "azure_mysql_msi";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Protocol<NativePacketPayload> protocol) {
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Protocol<NativePacketPayload> protocol, MysqlCallbackHandler callbackHandler) {
|
||||
this.init(protocol);
|
||||
this.callbackHandler = callbackHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReusable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean nextAuthenticationStep(NativePacketPayload fromServer,
|
||||
List<NativePacketPayload> toServer) {
|
||||
|
||||
/*
|
||||
* See com.mysql.cj.protocol.a.authentication.MysqlClearPasswordPlugin
|
||||
*/
|
||||
toServer.clear();
|
||||
NativePacketPayload response;
|
||||
|
||||
if (fromServer == null || accessToken == null || accessToken.isExpired()) {
|
||||
response = new NativePacketPayload(new byte[0]);
|
||||
} else if (protocol.getSocketConnection().isSSLEstablished()) {
|
||||
try {
|
||||
response = new NativePacketPayload(
|
||||
accessToken.getToken().getBytes(
|
||||
protocol.getServerSession()
|
||||
.getCharsetSettings()
|
||||
.getPasswordCharacterEncoding()));
|
||||
response.setPosition(response.getPayloadLength());
|
||||
response.writeInteger(NativeConstants.IntegerDataType.INT1, 0);
|
||||
response.setPosition(0);
|
||||
} catch (UnsupportedEncodingException uee) {
|
||||
response = new NativePacketPayload(new byte[0]);
|
||||
}
|
||||
} else {
|
||||
response = new NativePacketPayload(new byte[0]);
|
||||
}
|
||||
|
||||
toServer.add(response);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresConfidentiality() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
accessToken = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAuthenticationParameters(String username, String password) {
|
||||
|
||||
/*
|
||||
* If username is specified use it as a managed identity (and if it
|
||||
* fails let the AzureCliCredential have a chance), otherwise assume
|
||||
* system assigned managed identity.
|
||||
*/
|
||||
TokenCredential credential;
|
||||
|
||||
if (username != null) {
|
||||
ArrayList<TokenCredential> credentials = new ArrayList<>();
|
||||
credentials.add(new ManagedIdentityCredentialBuilder()
|
||||
.clientId(username).build());
|
||||
credentials.add(new AzureCliCredentialBuilder().build());
|
||||
credential = new ChainedTokenCredentialBuilder().addAll(credentials).build();
|
||||
} else {
|
||||
credential = new ManagedIdentityCredentialBuilder().build();
|
||||
username = ((ManagedIdentityCredential) credential).getClientId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup the username callback.
|
||||
*/
|
||||
callbackHandler.handle(new UsernameCallback(username));
|
||||
|
||||
/*
|
||||
* Setup the access token.
|
||||
*/
|
||||
if (username != null) {
|
||||
TokenRequestContext request = new TokenRequestContext();
|
||||
ArrayList<String> scopes = new ArrayList<>();
|
||||
scopes.add("https://ossrdbms-aad.database.windows.net");
|
||||
request.setScopes(scopes);
|
||||
accessToken = credential.getToken(request).block(Duration.ofSeconds(30));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSourceOfAuthData(String sourceOfAuthData) {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package test;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* A test that can be used to verify that managed identity is working with your
|
||||
* MySQL JDBC connection.
|
||||
*/
|
||||
public class JDBCTest {
|
||||
|
||||
/**
|
||||
* Test connection.
|
||||
*/
|
||||
@Test
|
||||
public void testConnection() {
|
||||
main(new String[]{System.getProperty("url")});
|
||||
}
|
||||
|
||||
/**
|
||||
* Main method.
|
||||
*
|
||||
* @param arguments the arguments.
|
||||
*/
|
||||
public static void main(String[] arguments) {
|
||||
Connection connection;
|
||||
try {
|
||||
connection = DriverManager.getConnection(arguments[0]);
|
||||
|
||||
if (connection != null) {
|
||||
System.out.println("Successfully connected.");
|
||||
System.out.println(connection.isValid(10));
|
||||
} else {
|
||||
System.out.println("Failed to connect.");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче