【发布时间】:2020-08-30 13:09:20
【问题描述】:
我正在将我的 Spring Boot 应用程序从 ADAL 迁移到 MSAL。
我收到如下所示的错误:
java.util.concurrent.ExecutionException: com.microsoft.aad.msal4j.MsalServiceException:
AADSTS50194: Application 'fd0ac989-0246-4999-b562-6d42d3636c22'(primadollardev_solanapi) is not configured as a multi-tenant application.
Usage of the /common endpoint is not supported for such applications created after '10/15/2018'.
Use a tenant-specific endpoint or configure the application to be multi-tenant.
Trace ID: 49905cdc-df8e-4bbb-9f48-daa2fe893000
Correlation ID: d6957f88-b418-4351-b141-4a5fbb6c6a99
Timestamp: 2020-08-30 06:05:40Z
ADAL 代码
/**
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See LICENSE in the project root for
* license information.
*/
package com.microsoft.azure.spring.autoconfigure.aad;
import com.microsoft.aad.adal4j.ClientCredential;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.proc.BadJOSEException;
import com.nimbusds.jose.util.ResourceRetriever;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.naming.ServiceUnavailableException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.MalformedURLException;
import java.text.ParseException;
import java.util.concurrent.ExecutionException;
public class AADAuthenticationFilter extends OncePerRequestFilter {
private static final Logger log = LoggerFactory.getLogger(AADAuthenticationFilter.class);
private static final String CURRENT_USER_PRINCIPAL = "CURRENT_USER_PRINCIPAL";
private static final String CURRENT_USER_PRINCIPAL_GRAPHAPI_TOKEN = "CURRENT_USER_PRINCIPAL_GRAPHAPI_TOKEN";
private static final String CURRENT_USER_PRINCIPAL_JWT_TOKEN = "CURRENT_USER_PRINCIPAL_JWT_TOKEN";
private static final String TOKEN_HEADER = "Authorization";
private static final String TOKEN_TYPE = "Bearer ";
private AADAuthenticationProperties aadAuthProps;
private ServiceEndpointsProperties serviceEndpointsProps;
private UserPrincipalManager principalManager;
public AADAuthenticationFilter(AADAuthenticationProperties aadAuthProps,
ServiceEndpointsProperties serviceEndpointsProps,
ResourceRetriever resourceRetriever) {
this.aadAuthProps = aadAuthProps;
this.serviceEndpointsProps = serviceEndpointsProps;
this.principalManager = new UserPrincipalManager(serviceEndpointsProps, aadAuthProps, resourceRetriever, false);
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
final String authHeader = request.getHeader(TOKEN_HEADER);
if (authHeader != null && authHeader.startsWith(TOKEN_TYPE)) {
try {
final String idToken = authHeader.replace(TOKEN_TYPE, "");
UserPrincipal principal = (UserPrincipal) request
.getSession().getAttribute(CURRENT_USER_PRINCIPAL);
String graphApiToken = (String) request
.getSession().getAttribute(CURRENT_USER_PRINCIPAL_GRAPHAPI_TOKEN);
final String currentToken = (String) request
.getSession().getAttribute(CURRENT_USER_PRINCIPAL_JWT_TOKEN);
final ClientCredential credential =
new ClientCredential(aadAuthProps.getClientId(), aadAuthProps.getClientSecret());
final AzureADGraphClient client =
new AzureADGraphClient(credential, aadAuthProps, serviceEndpointsProps);
if (principal == null ||
graphApiToken == null ||
graphApiToken.isEmpty() ||
!idToken.equals(currentToken)
) {
principal = principalManager.buildUserPrincipal(idToken);
final String tenantId = principal.getClaim().toString();
graphApiToken = client.acquireTokenForGraphApi(idToken, tenantId).getAccessToken();
principal.setUserGroups(client.getGroups(graphApiToken));
request.getSession().setAttribute(CURRENT_USER_PRINCIPAL, principal);
request.getSession().setAttribute(CURRENT_USER_PRINCIPAL_GRAPHAPI_TOKEN, graphApiToken);
request.getSession().setAttribute(CURRENT_USER_PRINCIPAL_JWT_TOKEN, idToken);
}
final Authentication authentication = new PreAuthenticatedAuthenticationToken(
principal, null, client.convertGroupsToGrantedAuthorities(principal.getUserGroups()));
authentication.setAuthenticated(true);
log.info("Request token verification success. {}", authentication);
SecurityContextHolder.getContext().setAuthentication(authentication);
} catch (MalformedURLException | ParseException | BadJOSEException | JOSEException ex) {
log.error("Failed to initialize UserPrincipal.", ex);
throw new ServletException(ex);
} catch (ServiceUnavailableException | InterruptedException | ExecutionException ex) {
log.error("Failed to acquire graph api token.", ex);
throw new ServletException(ex);
}
}
filterChain.doFilter(request, response);
}
}
MSAL 代码
public class MSALAuthenticationFilter extends OncePerRequestFilter {
private static final Logger log = LoggerFactory.getLogger(MSALAuthenticationFilter.class);
private static final String TOKEN_HEADER = "Authorization";
private static final String TOKEN_TYPE = "Bearer ";
// Properties from the application.properties file like clientId, tenant and stuff.
private static final String CURRENT_USER_PRINCIPAL = "CURRENT_USER_PRINCIPAL";
private static final String CURRENT_USER_ACCESS_TOKEN = "CURRENT_USER_ACCESS_TOKEN";
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
final String authHeader = request.getHeader(TOKEN_HEADER);
UserPrincipal principal = (UserPrincipal) request.getSession().getAttribute(CURRENT_USER_PRINCIPAL);
if (authHeader != null && authHeader.startsWith(TOKEN_TYPE)) {
try {
final String idToken = authHeader.replace(TOKEN_TYPE, "");
ConfidentialClientApplication clientApplication = ConfidentialClientApplication.builder(
clientId,
ClientCredentialFactory.create(clientSecret))
.authority(authority)
.build();
Set<String> scopes = new HashSet<>(Arrays.asList(scope.split(" ")));
UserAssertion assertion = new UserAssertion(idToken);
OnBehalfOfParameters params = OnBehalfOfParameters.builder(scopes, assertion).build();
CompletableFuture<AuthenticationResult> future = clientApplication.acquireToken(params);
AuthenticationResult accessToken = future.get();
if (principal == null) {
principal = principalManager.buildUserPrincipal(idToken, accessToken);
request.getSession().setAttribute(CURRENT_USER_PRINCIPAL, principal);
request.getSession().setAttribute(CURRENT_USER_ACCESS_TOKEN, accessToken);
}
final Authentication authentication = new PreAuthenticatedAuthenticationToken(
principal,
null,
convertGroupsToGrantedAuthorities(principal.getUserGroups()));
authentication.setAuthenticated(true);
log.info("Request token verification success. {}", authentication);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
catch (MalformedURLException | InterruptedException | ExecutionException ex) {
log.error("Failed to authenticate", ex);
throw new ServletException(ex);
}
}
filterChain.doFilter(request, response);
}
}
请给我一个解决方案......
【问题讨论】:
-
好像是版本问题,请参考GitHub issue。如果对您没有帮助,请告诉我们。
-
Sruthi 应该是正确的。请更新到 2.2.4 版本。
标签: spring-boot adal msal