Skip to content

Instantly share code, notes, and snippets.

@Flygsand
Last active February 7, 2022 09:41
Show Gist options
  • Save Flygsand/9e3fed6e539c1e9ec4a3ff2e63c5f48b to your computer and use it in GitHub Desktop.
Save Flygsand/9e3fed6e539c1e9ec4a3ff2e63c5f48b to your computer and use it in GitHub Desktop.
package example;
import java.util.function.Supplier;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
import org.springframework.stereotype.Component;
@Component
public class ApiAuthorizationManager implements AuthorizationManager<RequestAuthorizationContext> {
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication, RequestAuthorizationContext context) {
boolean granted = authentication.get().getAuthorities().stream()
.filter(Authority.class::isInstance)
.map(a -> (Authority) a)
.anyMatch(a -> a.getExtent().contains("*"));
return new AuthorizationDecision(granted);
}
}
package example;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.nimbusds.jose.shaded.json.JSONArray;
import com.nimbusds.jose.shaded.json.JSONObject;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
import example.ThrowingFunction;
import example.Authority;
@Component
public class AuthoritiesClaimConverter implements Converter<Object, Set<Authority>> {
private final ObjectMapper objectMapper;
@Autowired
public AuthoritiesClaimConverter(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
@Override
public Set<Authority> convert(Object claim) {
return ((JSONArray) claim).stream()
.map(ThrowingFunction.unchecked(o -> objectMapper.readValue(((JSONObject) o).toJSONString(), Authority.class)))
.collect(Collectors.toSet());
}
}
package example;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.Map;
import java.util.Set;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
@Data
public class Authority implements GrantedAuthority {
@JsonProperty("cust")
private String customerId;
@JsonProperty("ext")
private Set<String> extent;
@JsonProperty("atr")
private Map<String, Object> attributes;
@Override
@JsonIgnore
public String getAuthority() {
return null;
}
}
package example;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.core.OAuth2TokenType;
import org.springframework.security.oauth2.server.authorization.JwtEncodingContext;
import org.springframework.security.oauth2.server.authorization.OAuth2TokenCustomizer;
import org.springframework.stereotype.Component;
@Component
public class JwtCustomizer implements OAuth2TokenCustomizer<JwtEncodingContext> {
private final ObjectMapper objectMapper;
@Autowired
public JwtCustomizer(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
@Override
public void customize(JwtEncodingContext context) {
if (OAuth2TokenType.ACCESS_TOKEN.equals(context.getTokenType())) {
context.getClaims().claim("auth", context.getPrincipal().getAuthorities().stream()
.map(a -> objectMapper.convertValue(a, Map.class))
.collect(Collectors.toSet()));
}
}
}
package example;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import java.time.Clock;
import java.util.List;
import java.util.Map;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
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.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtTimestampValidator;
import org.springframework.security.oauth2.jwt.MappedJwtClaimSetConverter;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.web.SecurityFilterChain;
import example.ApiAuthorizationManager;
import example.AuthoritiesClaimConverter;
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
public class SecurityConfiguration {
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE + 1)
public SecurityFilterChain apiSecurityFilterChain(HttpSecurity http, ApiAuthorizationManager authorizationManager) throws Exception {
http
.antMatcher("/api/v1/**")
.authorizeHttpRequests(authz ->
authz.anyRequest().access(authorizationManager)
)
.httpBasic().disable()
.formLogin().disable()
.csrf().disable()
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
return http.build();
}
@Bean
public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource, Clock clock, AuthoritiesClaimConverter authoritiesClaimConverter) {
JwtTimestampValidator timestampValidator = new JwtTimestampValidator();
timestampValidator.setClock(clock);
NimbusJwtDecoder jwtDecoder = (NimbusJwtDecoder) OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
jwtDecoder.setJwtValidator(new DelegatingOAuth2TokenValidator<>(List.of(timestampValidator)));
jwtDecoder.setClaimSetConverter(MappedJwtClaimSetConverter.withDefaults(Map.of("auth", authoritiesClaimConverter)));
return jwtDecoder;
}
}
{
"sub": "johndoe",
"aud": "foobar",
"nbf": 1644222746,
"iss": "http://localhost:9037",
"auth": [
{
"cust": "*",
"ext": [
"*"
],
"atr": {}
}
],
"exp": 1644223046,
"iat": 1644222746
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment