He visto en algunas implementaciones de oauth2 información adicional sobre la respuesta devuelta por el servidor de autorización cuando emite tokens de acceso. Me pregunto si hay una manera de lograr esto usando spring-security-oauth2. Me encantaría poder incluir algunas autoridades de usuario en la respuesta del token de acceso para que mis aplicaciones consumidoras no necesiten administrar las autoridades de usuario, pero aún así puedan configurar al usuario en sus propios contextos de seguridad y aplicar cualquiera de su propia seguridad de primavera cheques.

  1. ¿Cómo obtendría esa información en la respuesta del token de acceso?
  2. ¿Cómo interceptaría esa información en el lado del cliente oauth2 y la establecería en el contexto de seguridad?

Supongo que otra opción sería usar tokens JWT y compartir la información apropiada con las aplicaciones cliente para que puedan analizar el usuario / autoridades fuera del token y configurarlo en el contexto. Esto me hace sentir más incómodo, ya que preferiría tener el control de qué aplicaciones cliente podrían tener acceso a esta información (solo aplicaciones confiables) y AFAIK solo el servidor de autorización y el servidor de recursos deberían saber cómo analizar los tokens JWT.

35
RutledgePaulV 13 feb. 2015 a las 06:43

5 respuestas

La mejor respuesta

Deberá implementar un TokenEnhancer personalizado así:

public class CustomTokenEnhancer implements TokenEnhancer {

    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
        User user = (User) authentication.getPrincipal();
        final Map<String, Object> additionalInfo = new HashMap<>();

        additionalInfo.put("customInfo", "some_stuff_here");
        additionalInfo.put("authorities", user.getAuthorities());

        ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);

        return accessToken;
    }

}

Y agréguelo a su AuthorizationServerConfigurerAdapter como un bean con los setters correspondientes

@Configuration
@EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

    // Some autowired stuff here

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        // @formatter:off
        endpoints
            // ...
            .tokenEnhancer(tokenEnhancer());
        // @formatter:on
    }

    @Bean
    @Primary
    public AuthorizationServerTokenServices tokenServices() {
        DefaultTokenServices tokenServices = new DefaultTokenServices();
        // ...
        tokenServices.setTokenEnhancer(tokenEnhancer());
        return tokenServices;
    }

    // Some @Bean here like tokenStore

    @Bean
    public TokenEnhancer tokenEnhancer() {
        return new CustomTokenEnhancer();
    }

}

Luego en un controlador (por ejemplo)

@RestController
public class MyController {

    @Autowired
    private AuthorizationServerTokenServices tokenServices;

    @RequestMapping(value = "/getSomething", method = RequestMethod.GET)
    public String getSection(OAuth2Authentication authentication) {
        Map<String, Object> additionalInfo = tokenServices.getAccessToken(authentication).getAdditionalInformation();

        String customInfo = (String) additionalInfo.get("customInfo");
        Collection<? extends GrantedAuthority> authorities = (Collection<? extends GrantedAuthority>) additionalInfo.get("authorities");

        // Play with authorities

        return customInfo;
    }

}

Personalmente estoy usando un TokenStore JDBC, por lo que mis "Algunas cosas conectadas automáticamente aquí" corresponden a alguna fuente de datos @Autowired, PasswordEncoder y otras cosas.

Espero que esto haya ayudado!

65
Philippe 7 ago. 2017 a las 13:42

Resuelvo este problema cuando excluyo UserDetailsServiceAutoConfiguration. Como esto. Quizás sea útil en los servidores de recursos OAuth2.

@SpringBootApplication(exclude = [UserDetailsServiceAutoConfiguration::class])
class Application

fun main(args: Array<String>) {
    runApplication<Application>(*args)
}
0
Linolium 17 jun. 2019 a las 11:36
package com.security;

import java.util.HashMap;
import java.util.Map;

import org.springframework.security.core.userdetails.User;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.stereotype.Component;

@Component
public class CustomTokenEnhancer implements TokenEnhancer {

	@Override
	public OAuth2AccessToken enhance(OAuth2AccessToken accessToken,
			OAuth2Authentication authentication) {
		// TODO Auto-generated method stub
		User user = (User) authentication.getPrincipal();
        final Map<String, Object> additionalInfo = new HashMap<>();

        additionalInfo.put("customInfo", "some_stuff_here");
        additionalInfo.put("authorities", user.getAuthorities());

        ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);

        return accessToken;
	}

}

A continuación se muestra la configuración xml:

<bean id="tokenEnhancer" class="com.security.CustomTokenEnhancer" />

<!-- Used to create token and and every thing about them except for their persistence that is reposibility of TokenStore (Given here is a default implementation) -->
<bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
  <property name="tokenStore" ref="tokenStore" />
  <property name="accessTokenValiditySeconds" value="30000000"></property>
  <property name="refreshTokenValiditySeconds" value="300000000"></property>
  <property name="supportRefreshToken" value="true"></property>
  <property name="clientDetailsService" ref="clientDetails"></property>
  <property name="tokenEnhancer" ref="tokenEnhancer" />
</bean>

Así es como pude agregar información adicional al Token.

3
Wojciech Wirzbicki 30 ago. 2017 a las 07:26

Juntos con:

@Bean
public TokenEnhancer tokenEnhancer() {
   return new CustomTokenEnhancer();
}

Usted tiene que incluir

@Bean
public DefaultAccessTokenConverter accessTokenConverter() {
    return new DefaultAccessTokenConverter();
}

Y agregue todo a la configuración de los puntos finales:

@Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {

        endpoints
                .tokenStore(tokenStore)
                .tokenEnhancer(tokenEnhancer())
                .accessTokenConverter(accessTokenConverter())
                .authorizationCodeServices(codeServices)
                .authenticationManager(authenticationManager)
        ;
    }

Sin él, su CustomTokenEnhancer no funcionará.

3
Yaroslav 5 dic. 2016 a las 21:23

Si está utilizando Spring's JwtAccessTokenConverter o DefaultAccessTokenConverter, puede agregar su CustomTokenEnhancer personalizado (vea la primera respuesta) y aplicarlo usando un TokenEnhancerChain como este:

@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {

    TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
    enhancerChain.setTokenEnhancers(Arrays.asList(customTokenEnhancer(), accessTokenConverter()));

    endpoints.tokenStore(tokenStore())
            .tokenEnhancer(enhancerChain)
            .authenticationManager(authenticationManager);
}

@Bean
protected JwtAccessTokenConverter jwtTokenEnhancer() {
    JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
    converter.setSigningKey("my_signing_key");
    return converter;
}

@Bean public TokenEnhancer customTokenEnhancer() {
    return new CustomTokenEnhancer();
}

Otra solución es crear un TokenConverter personalizado que amplíe el JwtAccessTokenConverter de Spring y anule el método hance () con sus reclamos personalizados.

public class CustomTokenConverter extends JwtAccessTokenConverter {

@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {

    final Map<String, Object> additionalInfo = new HashMap<>();
    additionalInfo.put("customized", "true");
    User user = (User) authentication.getPrincipal();
    additionalInfo.put("isAdmin", user.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()).contains("BASF_ADMIN"));
    ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);

    return super.enhance(accessToken, authentication);
    }
} 

Y entonces:

@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {

    endpoints.tokenStore(tokenStore())
            .tokenEnhancer(customTokenEnhancer())
            .authenticationManager(authenticationManager);
}

@Bean public CustomTokenConverter customTokenEnhancer() {
    return new CustomTokenConverter();
}
21
jchrbrt 19 jul. 2017 a las 15:30