如何使用Java调用Azure AD的AWS单账户访问企业应用?
解决Java程序化获取Azure AD SAML断言用于AWS SSO
核心思路
要程序化实现Azure AD认证并获取SAML断言,本质是模拟Azure AD的OAuth2授权流程,获取包含SAML断言的响应——因为针对AWS企业应用的SSO配置,Azure AD会在授权成功后返回SAML断言作为access_token的内容。
步骤1:准备Azure AD配置参数
先从Azure AD门户提取以下关键参数:
tenant-id: Azure AD租户IDclient-id: AWS单账户访问企业应用的客户端IDclient-secret: 企业应用的客户端密钥redirect-uri: 已在Azure AD应用中配置的重定向地址(授权码流程用,比如http://localhost:8080/callback)resource: AWS应用的标识符,固定为https://signin.aws.amazon.com/saml
步骤2:选择适合的认证流程
根据场景需求,可选两种主流实现方式:
方式一:授权码流程(需用户交互)
适合需要用户手动登录的场景,流程为:引导用户登录获取授权码 → 用授权码交换令牌和SAML断言。
代码示例
import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; public class AzureAdSamlClient { private static final String TENANT_ID = "你的租户ID"; private static final String CLIENT_ID = "你的客户端ID"; private static final String CLIENT_SECRET = "你的客户端密钥"; private static final String REDIRECT_URI = "http://localhost:8080/callback"; private static final String RESOURCE = "https://signin.aws.amazon.com/saml"; public static void main(String[] args) throws Exception { // 1. 生成授权登录URL,引导用户获取授权码 String authUrl = String.format( "https://login.microsoftonline.com/%s/oauth2/authorize?client_id=%s&response_type=code&redirect_uri=%s&resource=%s&response_mode=query", TENANT_ID, CLIENT_ID, REDIRECT_URI, RESOURCE ); System.out.println("访问以下URL登录并复制回调中的code参数:"); System.out.println(authUrl); // 2. 替换为用户实际获取的授权码 String authCode = "用户登录后得到的code"; // 3. 用授权码交换SAML断言 try (CloseableHttpClient httpClient = HttpClients.createDefault()) { HttpPost tokenPost = new HttpPost(String.format("https://login.microsoftonline.com/%s/oauth2/token", TENANT_ID)); String body = String.format( "grant_type=authorization_code&client_id=%s&client_secret=%s&code=%s&redirect_uri=%s&resource=%s", CLIENT_ID, CLIENT_SECRET, authCode, REDIRECT_URI, RESOURCE ); tokenPost.setEntity(new StringEntity(body)); tokenPost.setHeader("Content-Type", "application/x-www-form-urlencoded"); String response = EntityUtils.toString(httpClient.execute(tokenPost).getEntity()); ObjectMapper mapper = new ObjectMapper(); JsonNode jsonNode = mapper.readTree(response); // 提取Base64编码的SAML断言 String samlAssertion = jsonNode.get("access_token").asText(); System.out.println("\n获取到的SAML断言:"); System.out.println(samlAssertion); } } }
方式二:客户端凭证流程(无用户交互)
适合后台服务自动执行的场景,无需用户登录,但需确保Azure AD企业应用已配置对应权限并授予管理员同意。
代码示例
import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; public class AzureAdSamlClient { private static final String TENANT_ID = "你的租户ID"; private static final String CLIENT_ID = "你的客户端ID"; private static final String CLIENT_SECRET = "你的客户端密钥"; private static final String RESOURCE = "https://signin.aws.amazon.com/saml"; public static void main(String[] args) throws Exception { try (CloseableHttpClient httpClient = HttpClients.createDefault()) { HttpPost tokenPost = new HttpPost(String.format("https://login.microsoftonline.com/%s/oauth2/token", TENANT_ID)); String body = String.format( "grant_type=client_credentials&client_id=%s&client_secret=%s&resource=%s", CLIENT_ID, CLIENT_SECRET, RESOURCE ); tokenPost.setEntity(new StringEntity(body)); tokenPost.setHeader("Content-Type", "application/x-www-form-urlencoded"); String response = EntityUtils.toString(httpClient.execute(tokenPost).getEntity()); ObjectMapper mapper = new ObjectMapper(); JsonNode jsonNode = mapper.readTree(response); String samlAssertion = jsonNode.get("access_token").asText(); System.out.println("获取到的SAML断言:"); System.out.println(samlAssertion); } } }
关键注意事项
- 授权码流程必须确保Azure AD应用中已配置对应的
redirect-uri,否则授权码交换会失败。 - 客户端凭证流程需在Azure AD应用的API权限中添加对AWS应用的权限,并完成管理员同意操作。
- 获取到的
access_token即为Base64编码的SAML断言,可直接用于调用AWS STS的AssumeRoleWithSAML接口完成角色切换。 - 推荐使用微软官方的MSAL4J库简化流程,避免手动处理HTTP请求的细节。
使用MSAL4J简化实现(推荐)
MSAL4J是微软官方Java认证库,能更安全高效地处理Azure AD认证流程:
Maven依赖
<dependency> <groupId>com.microsoft.azure</groupId> <artifactId>msal4j</artifactId> <version>1.23.0</version> </dependency>
授权码流程示例
import com.microsoft.aad.msal4j.*; import java.net.URI; import java.util.Collections; import java.util.concurrent.CompletableFuture; public class MsalSamlExample { private static final String TENANT_ID = "你的租户ID"; private static final String CLIENT_ID = "你的客户端ID"; private static final String CLIENT_SECRET = "你的客户端密钥"; private static final String REDIRECT_URI = "http://localhost:8080/callback"; private static final String RESOURCE = "https://signin.aws.amazon.com/saml"; public static void main(String[] args) throws Exception { // 构造客户端实例 ConfidentialClientApplication app = ConfidentialClientApplication.builder( CLIENT_ID, ClientCredentialFactory.createFromSecret(CLIENT_SECRET)) .authority(String.format("https://login.microsoftonline.com/%s", TENANT_ID)) .build(); // 生成授权登录URL AuthorizationRequestUrlParameters authParams = AuthorizationRequestUrlParameters.builder( new URI(REDIRECT_URI), Collections.singleton(RESOURCE)) .responseMode(ResponseMode.QUERY) .build(); String authUrl = app.getAuthorizationRequestUrl(authParams).toString(); System.out.println("访问以下URL登录并复制code参数:"); System.out.println(authUrl); // 替换为实际授权码 String authCode = "用户获取的code"; // 交换令牌获取SAML断言 CompletableFuture<IAuthenticationResult> future = app.acquireToken(AuthorizationCodeParameters.builder( authCode, new URI(REDIRECT_URI)).build()); IAuthenticationResult result = future.get(); String samlAssertion = result.accessToken(); System.out.println("\nSAML断言:"); System.out.println(samlAssertion); } }
客户端凭证流程示例
import com.microsoft.aad.msal4j.*; import java.util.Collections; import java.util.concurrent.CompletableFuture; public class MsalClientCredentialExample { private static final String TENANT_ID = "你的租户ID"; private static final String CLIENT_ID = "你的客户端ID"; private static final String CLIENT_SECRET = "你的客户端密钥"; private static final String RESOURCE = "https://signin.aws.amazon.com/saml"; public static void main(String[] args) throws Exception { ConfidentialClientApplication app = ConfidentialClientApplication.builder( CLIENT_ID, ClientCredentialFactory.createFromSecret(CLIENT_SECRET)) .authority(String.format("https://login.microsoftonline.com/%s", TENANT_ID)) .build(); ClientCredentialParameters params = ClientCredentialParameters.builder( Collections.singleton(RESOURCE)) .build(); CompletableFuture<IAuthenticationResult> future = app.acquireToken(params); IAuthenticationResult result = future.get(); String samlAssertion = result.accessToken(); System.out.println("SAML断言:"); System.out.println(samlAssertion); } }
内容的提问来源于stack exchange,提问作者chetan007
相关产品推荐
相关产品推荐

