Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
29b5cd0
* Make changes according to checkstyle
ZhuXiaoBing-cn Nov 17, 2020
081cb82
* Make changes according to checkstyle
ZhuXiaoBing-cn Nov 17, 2020
c8a3508
* Make changes according to checkstyle
ZhuXiaoBing-cn Nov 17, 2020
b9aa067
* add JavaDoc
ZhuXiaoBing-cn Nov 17, 2020
fdfb007
* remove tenant validator
ZhuXiaoBing-cn Nov 18, 2020
bddc2e4
* add Converter,it's effect will be to store the UserPrincipal to Aut…
ZhuXiaoBing-cn Nov 19, 2020
cb82a9b
Merge branch 'master' into access_token_validate
ZhuXiaoBing-cn Nov 19, 2020
0732746
Merge branch 'master' into access_token_validate
ZhuXiaoBing-cn Nov 19, 2020
3f8dac0
* update file name
ZhuXiaoBing-cn Nov 20, 2020
4b6e974
* Make changes according to checkstyle
ZhuXiaoBing-cn Nov 23, 2020
ce65efe
Merge branch 'master' into access_token_validate
ZhuXiaoBing-cn Nov 23, 2020
05d929d
* Make changes according to checkstyle
ZhuXiaoBing-cn Nov 23, 2020
4e4d053
* Make changes according to checkstyle
ZhuXiaoBing-cn Nov 23, 2020
b362dfb
* Make changes according to checkstyle
ZhuXiaoBing-cn Nov 23, 2020
ae39fe7
* Make changes according to checkstyle
ZhuXiaoBing-cn Nov 23, 2020
acaeefa
* update AzureJwtBearerTokenAuthenticationConverter
ZhuXiaoBing-cn Nov 23, 2020
d25e9df
Merge branch 'master' into access_token_validate
ZhuXiaoBing-cn Nov 23, 2020
a31f1a6
* add AzureOAuth2AuthenticatedPrincipal
ZhuXiaoBing-cn Nov 25, 2020
f7aa628
Merge branch 'master' into access_token_validate
ZhuXiaoBing-cn Nov 25, 2020
0851fc6
* update AzureActiveDirectoryResourceServerConfiguration
ZhuXiaoBing-cn Nov 25, 2020
0ac20f9
* Make changes according to checkstyle
ZhuXiaoBing-cn Nov 25, 2020
9f80db0
* Make changes according to checkstyle.
ZhuXiaoBing-cn Nov 25, 2020
fd2155b
* update unit test
ZhuXiaoBing-cn Nov 26, 2020
7403602
* Modify invalid import
ZhuXiaoBing-cn Nov 26, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions eng/versioning/external_dependencies.txt
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ org.springframework.kafka:spring-kafka;2.5.7.RELEASE
org.springframework.security:spring-security-config;5.3.5.RELEASE
org.springframework.security:spring-security-core;5.3.5.RELEASE
org.springframework.security:spring-security-oauth2-client;5.3.5.RELEASE
org.springframework.security:spring-security-oauth2-resource-server;5.3.5.RELEASE
org.springframework.security:spring-security-oauth2-core;5.3.5.RELEASE
org.springframework.security:spring-security-oauth2-jose;5.3.5.RELEASE
org.springframework.security:spring-security-web;5.3.5.RELEASE
Expand Down
7 changes: 7 additions & 0 deletions sdk/spring/azure-spring-boot/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,12 @@
<version>5.3.5.RELEASE</version> <!-- {x-version-update;org.springframework.security:spring-security-oauth2-client;external_dependency} -->
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-resource-server</artifactId>
<version>5.3.5.RELEASE</version> <!-- {x-version-update;org.springframework.security:spring-security-oauth2-resource-server;external_dependency} -->
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-jose</artifactId>
Expand Down Expand Up @@ -300,6 +306,7 @@
<include>org.springframework.security:spring-security-config:[5.3.5.RELEASE]</include> <!-- {x-include-update;org.springframework.security:spring-security-config;external_dependency} -->
<include>org.springframework.security:spring-security-core:[5.3.5.RELEASE]</include> <!-- {x-include-update;org.springframework.security:spring-security-core;external_dependency} -->
<include>org.springframework.security:spring-security-oauth2-client:[5.3.5.RELEASE]</include> <!-- {x-include-update;org.springframework.security:spring-security-oauth2-client;external_dependency} -->
<include>org.springframework.security:spring-security-oauth2-resource-server:[5.3.5.RELEASE]</include> <!-- {x-include-update;org.springframework.security:spring-security-oauth2-resource-server;external_dependency} -->
<include>org.springframework.security:spring-security-oauth2-jose:[5.3.5.RELEASE]</include> <!-- {x-include-update;org.springframework.security:spring-security-oauth2-jose;external_dependency} -->
<include>org.springframework.security:spring-security-web:[5.3.5.RELEASE]</include> <!-- {x-include-update;org.springframework.security:spring-security-web;external_dependency} -->
<include>org.apache.qpid:qpid-jms-client:[0.53.0]</include> <!-- {x-include-update;org.apache.qpid:qpid-jms-client;external_dependency} -->
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.spring.aad.resource.server;


import com.azure.spring.aad.implementation.AuthorizationServerEndpoints;
import com.azure.spring.aad.resource.server.validator.AzureJwtAudienceValidator;
import com.azure.spring.aad.resource.server.validator.AzureJwtIssuerValidator;
import com.azure.spring.autoconfigure.aad.AADAuthenticationProperties;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtTimestampValidator;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
import org.springframework.util.StringUtils;

/**
* <p>
* The configuration will not be activated if no {@link BearerTokenAuthenticationToken} class provided.
* <p>
* By default, creating a JwtDecoder through JwkKeySetUri will be auto-configured.
*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties({AADAuthenticationProperties.class})
@ConditionalOnClass(BearerTokenAuthenticationToken.class)
public class AzureActiveDirectoryResourceServerConfiguration {

@Autowired
private AADAuthenticationProperties aadAuthenticationProperties;

/**
* Use JwkKeySetUri to create JwtDecoder
*
* @return JwtDecoder bean
*/
@Bean
@ConditionalOnMissingBean(JwtDecoder.class)
public JwtDecoder jwtDecoderByJwkKeySetUri() {
if (StringUtils.isEmpty(aadAuthenticationProperties.getTenantId())) {
aadAuthenticationProperties.setTenantId("common");
}
AuthorizationServerEndpoints identityEndpoints = new AuthorizationServerEndpoints(
aadAuthenticationProperties.getAuthorizationServerUri());
NimbusJwtDecoder nimbusJwtDecoder = NimbusJwtDecoder
.withJwkSetUri(identityEndpoints.jwkSetEndpoint(aadAuthenticationProperties.getTenantId())).build();
List<OAuth2TokenValidator<Jwt>> validators = createDefaultValidator();
nimbusJwtDecoder.setJwtValidator(new DelegatingOAuth2TokenValidator<>(validators));
return nimbusJwtDecoder;
}

public List<OAuth2TokenValidator<Jwt>> createDefaultValidator() {
List<OAuth2TokenValidator<Jwt>> validators = new ArrayList<>();
if (!StringUtils.isEmpty(aadAuthenticationProperties.getAppIdUri())) {
List<String> validAudiences = new ArrayList<>();
validAudiences.add(aadAuthenticationProperties.getAppIdUri());
validators.add(new AzureJwtAudienceValidator(validAudiences));
}
validators.add(new AzureJwtIssuerValidator());
validators.add(new JwtTimestampValidator());
return validators;
}

/**
* Default configuration class for using AAD authentication and authorization. User can write another configuration
* bean to override it.
*/
@Configuration
@ConditionalOnMissingBean(WebSecurityConfigurerAdapter.class)
@EnableWebSecurity
public static class DefaultAzureOAuth2ResourceServerWebSecurityConfigurerAdapter extends
WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests((requests) -> requests.anyRequest().authenticated())
.oauth2ResourceServer()
.jwt()
.jwtAuthenticationConverter(new AzureJwtBearerTokenAuthenticationConverter());
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.spring.aad.resource.server;

import java.util.Collection;
import org.springframework.core.convert.converter.Converter;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthentication;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
import org.springframework.util.Assert;

/**
* A {@link Converter} that takes a {@link Jwt} and converts it into a {@link BearerTokenAuthentication}.
*/
public class AzureJwtBearerTokenAuthenticationConverter implements Converter<Jwt, AbstractAuthenticationToken> {

private static final String DEFAULT_AUTHORITY_PREFIX = "SCOPE_";

private Converter<Jwt, Collection<GrantedAuthority>> jwtGrantedConverter
= new JwtGrantedAuthoritiesConverter();

public AzureJwtBearerTokenAuthenticationConverter() {
}

public AzureJwtBearerTokenAuthenticationConverter(String authoritiesClaimName) {
this(authoritiesClaimName, DEFAULT_AUTHORITY_PREFIX);
}

public AzureJwtBearerTokenAuthenticationConverter(String authoritiesClaimName, String authorityPrefix) {
Assert.notNull(authoritiesClaimName, "authoritiesClaimName cannot be null");
Assert.notNull(authorityPrefix, "authorityPrefix cannot be null");
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
jwtGrantedAuthoritiesConverter.setAuthoritiesClaimName(authoritiesClaimName);
jwtGrantedAuthoritiesConverter.setAuthorityPrefix(authorityPrefix);
this.jwtGrantedConverter = jwtGrantedAuthoritiesConverter;
}

protected Collection<GrantedAuthority> extractAuthorities(Jwt jwt) {
return this.jwtGrantedConverter.convert(jwt);
}

@Override
public AbstractAuthenticationToken convert(Jwt jwt) {
OAuth2AccessToken accessToken = new OAuth2AccessToken(
OAuth2AccessToken.TokenType.BEARER, jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt());
Collection<GrantedAuthority> authorities = extractAuthorities(jwt);
AzureOAuth2AuthenticatedPrincipal principal = new AzureOAuth2AuthenticatedPrincipal(
jwt.getHeaders(), jwt.getClaims(), authorities, jwt.getTokenValue());
return new BearerTokenAuthentication(principal, accessToken, authorities);
}

public void setJwtGrantedAuthoritiesConverter(
Converter<Jwt, Collection<GrantedAuthority>> jwtGrantedAuthoritiesConverter) {
this.jwtGrantedConverter = jwtGrantedAuthoritiesConverter;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.spring.aad.resource.server;

import static org.springframework.security.core.authority.AuthorityUtils.NO_AUTHORITIES;

import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.JWTClaimsSet.Builder;
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Map.Entry;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
import org.springframework.util.Assert;

/**
* entity class of AzureOAuth2AuthenticatedPrincipal
*/
public class AzureOAuth2AuthenticatedPrincipal implements OAuth2AuthenticatedPrincipal, Serializable {

private static final long serialVersionUID = -3625690847771476854L;

private final Collection<GrantedAuthority> authorities;

private final Map<String, Object> headers;

private final Map<String, Object> attributes;

private final String tokenValue;

private JWTClaimsSet jwtClaimsSet;

public AzureOAuth2AuthenticatedPrincipal(Map<String, Object> headers, Map<String, Object> attributes,
Collection<GrantedAuthority> authorities, String tokenValue) {
Assert.notEmpty(attributes, "attributes cannot be empty");
Assert.notEmpty(headers, "headers cannot be empty");
this.headers = headers;
this.tokenValue = tokenValue;
this.attributes = Collections.unmodifiableMap(attributes);
this.authorities = authorities == null ? NO_AUTHORITIES : Collections.unmodifiableCollection(authorities);
toJwtClaimsSet(attributes);
}

private void toJwtClaimsSet(Map<String, Object> attributes) {
JWTClaimsSet.Builder builder = new Builder();
for (Entry<String, Object> entry : attributes.entrySet()) {
builder.claim(entry.getKey(), entry.getValue());
}
this.jwtClaimsSet = builder.build();
}

@Override
public Map<String, Object> getAttributes() {
return attributes;
}

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}

@Override
public String getName() {
return jwtClaimsSet == null ? null : (String) jwtClaimsSet.getClaim("name");
}

public String getTokenValue() {
return tokenValue;
}

public Map<String, Object> getHeaders() {
return headers;
}

public JWTClaimsSet getJwtClaimsSet() {
return jwtClaimsSet;
}

public String getIssuer() {
return jwtClaimsSet == null ? null : jwtClaimsSet.getIssuer();
}

public String getSubject() {
return jwtClaimsSet == null ? null : jwtClaimsSet.getSubject();
}

public Map<String, Object> getClaims() {
return jwtClaimsSet == null ? null : jwtClaimsSet.getClaims();
}

public Object getClaim(String name) {
return jwtClaimsSet == null ? null : jwtClaimsSet.getClaim(name);
}

public String getTenantId() {
return jwtClaimsSet == null ? null : (String) jwtClaimsSet.getClaim("tid");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
/**
* Package com.azure.spring.aad.resource.server
*/
package com.azure.spring.aad.resource.server;
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.spring.aad.resource.server.validator;

import com.azure.spring.autoconfigure.aad.AADTokenClaim;
import java.util.List;
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtClaimValidator;
import org.springframework.util.Assert;

/**
* Validates the "aud" claim in a {@link Jwt}, that is matches a configured value
*/
public class AzureJwtAudienceValidator implements OAuth2TokenValidator<Jwt> {

private final JwtClaimValidator<List<String>> validator;

/**
* Constructs a {@link AzureJwtAudienceValidator} using the provided parameters
*
* @param audiences - The audience that each {@link Jwt} should have.
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public AzureJwtAudienceValidator(List<String> audiences) {
Assert.notNull(audiences, "audiences cannot be null");
this.validator = new JwtClaimValidator(AADTokenClaim.AUD, aud -> audiences.containsAll((List<String>) aud));
}

/**
* {@inheritDoc}
*/
@Override
public OAuth2TokenValidatorResult validate(Jwt token) {
Assert.notNull(token, "token cannot be null");
return this.validator.validate(token);
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.spring.aad.resource.server.validator;

import com.azure.spring.autoconfigure.aad.AADTokenClaim;
import java.util.function.Predicate;
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtClaimValidator;
import org.springframework.util.Assert;

/**
* Validates the "iss" claim in a {@link Jwt}, that is matches a configured value
*/
public class AzureJwtIssuerValidator implements OAuth2TokenValidator<Jwt> {

private static final String LOGIN_MICROSOFT_ONLINE_ISSUER = "https://login.microsoftonline.com/";
private static final String STS_WINDOWS_ISSUER = "https://sts.windows.net/";
private static final String STS_CHINA_CLOUD_API_ISSUER = "https://sts.chinacloudapi.cn/";
private final JwtClaimValidator<String> validator;

/**
* Constructs a {@link AzureJwtIssuerValidator} using the provided parameters
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public AzureJwtIssuerValidator() {
this.validator = new JwtClaimValidator(AADTokenClaim.ISS, validIssuer());
}

private Predicate<String> validIssuer() {
return iss -> {
if (iss == null) {
return false;
}
return iss.startsWith(LOGIN_MICROSOFT_ONLINE_ISSUER)
|| iss.startsWith(STS_WINDOWS_ISSUER)
|| iss.startsWith(STS_CHINA_CLOUD_API_ISSUER);
};
}

/**
* {@inheritDoc}
*/
@Override
public OAuth2TokenValidatorResult validate(Jwt token) {
Assert.notNull(token, "token cannot be null");
return this.validator.validate(token);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
/**
* Package com.azure.spring.aad.resource.server.validator
*/
package com.azure.spring.aad.resource.server.validator;
Loading