3
votes

Méthode getPrincipal () renvoyant le nom d'utilisateur au lieu de UserDetails

J'ai un projet utilisant spring boot, spring security avec oauth2. Quand j'utilise SecurityContextHolder.getContext (). GetAuthentication (). GetPrincipal ()

cette méthode ne retourne que le nom d'utilisateur à la fin des exemples que je vois cette méthode retourne l'implentation UserDetails.

Suivez les paramètres

OAuthSecurityConfig.java:

package br.com.altha.api.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler;
import org.springframework.security.oauth2.provider.expression.OAuth2MethodSecurityExpressionHandler;

import br.com.altha.api.handler.RestExceptionHandler;

@Configuration
@Import(Encoders.class)
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Bean
    public RestExceptionHandler handlerError() {
        return new RestExceptionHandler();
    }

    @Bean
    public MethodSecurityExpressionHandler createExpressionHandler() {
        return new OAuth2MethodSecurityExpressionHandler();
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/public/**").permitAll()
            .antMatchers("/private/**").authenticated()
            .and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
    }

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.stateless(true);
    }
}

AuthorizationServerConfig.java:

package br.com.altha.api.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

import br.com.altha.api.security.CustomUserDetailsService;

@Configuration
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    private static final String SECRET = "PASSWORD";

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private PasswordEncoder oauthClientPasswordEncoder;

    @Autowired
    private CustomUserDetailsService userDetailsService;

    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
        oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()").passwordEncoder(oauthClientPasswordEncoder);
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
            .withClient("altha-adms")
            .secret(oauthClientPasswordEncoder.encode(SECRET))
            .scopes("write", "read")
            .authorizedGrantTypes("password", "refresh_token")
            .accessTokenValiditySeconds(60/*1800*/)
            .refreshTokenValiditySeconds(1800);
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
            .tokenStore(tokenStore())
            .accessTokenConverter(accessTokenConverter())
            .authenticationManager(authenticationManager)
            .reuseRefreshTokens(false)
            .userDetailsService(userDetailsService);
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey(SECRET);
        return converter;
    }

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }
}

ResourceServerConfig.java:

package br.com.altha.api.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;

import br.com.altha.api.security.CustomUserDetailsService;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
@EnableAuthorizationServer
@EnableResourceServer
@Order(SecurityProperties.BASIC_AUTH_ORDER-2)
@Import(Encoders.class)
public class OAuthSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private PasswordEncoder userPasswordEncoder;

    @Autowired
    private CustomUserDetailsService userDetailsService;

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(userPasswordEncoder);
    }

}


2 commentaires

getPrincipal peut renvoyer ce qu'il veut. Donc, ce qui est retourné dépend de l'implémentation de l ' Authentification utilisée et l' OAuth2Authentication renvoie une String . Donc, à moins que vous ne réimplémentiez toute la partie oauth2, c'est ce avec quoi vous devez vivre. Donc, en bref, cela fonctionne comme il se doit / a été conçu pour fonctionner.


Alors, qu'est-ce que je fais pour implémenter toute la partie oauth2? Pouvez-vous me montrer un exemple?


4 Réponses :


0
votes

Avec Spring Boot 1.5.x, vous pouvez implémenter un PrincipalExtractor et remplacer son Object extractPrincipal (Map map) . L'exemple de composant suivant a un UserdetailsService autowired pour rechercher l'objet UserDetails basé sur le nom d'utilisateur.

@Component
public class MyPrincipalExtractor implements PrincipalExtractor {

    private UserDetailsService userDetailsService;

    @Value("${security.oauth2.client.principal-attribute}")
    private String principalAttribute;

    @Autowired
    public MyPrincipalExtractor(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Override
    public Object extractPrincipal(Map<String, Object> map) {
        if (!map.containsKey(principalAttribute)) {
            return null;
        }

        final String username = (String) map.get(principalAttribute);
        try {
            return userDetailsService.loadUserByUsername(username);
        } catch (UsernameNotFoundException e) {
            // This may be the first time this user is accessing the system, 
            // maybe you want to extract some other attributes from the map 
            // and return a different type of user object that can be used to 
            // create a new user. 
        }
    }
}

Maintenant, le SecurityContextHolder.getContext (). GetAuthentication (). GetPrincipal () code> contiendra un objet UserDetails.

Pour un didacticiel plus détaillé, voir:


4 commentaires

Cela fait référence à Spring OAuth2 (et plus spécifiquement au support Spring Boot). Ce projet est en mode maintenance car à partir de Spring Security 5 OAuth2 est pris en charge par défaut pour Spring Security. Il n'y a aucune garantie que cela fonctionnera avec le plus récent Spring Security ni Spring Boot 2.0. C'est aussi pour Spring Boot 1.5 et non Spring Security 1.5.


@ M.Deinum oui je voulais dire Spring Boot 1.5.x. Mettra à jour la réponse. Et oui, cela peut ne pas fonctionner avec le nouveau Spring Security 5.


Cette méthode ne fonctionne pas avec moi, probablement parce que j'utilise Spring Boot 2x


C'est correct @PauloCarvalho cette méthode ne fonctionne que pour Spring Boot 1.5.x et Spring Security 4.2.x



5
votes

J'ai pu résoudre ce problème avec ce code:

J'ai ajouté un bean à UserAuthenticationConverter

@Bean
public JwtAccessTokenConverter accessTokenConverter() {
    final JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
    jwtAccessTokenConverter.setSigningKey(SECRET);
    ((DefaultAccessTokenConverter) jwtAccessTokenConverter.getAccessTokenConverter())
            .setUserTokenConverter(userAuthenticationConverter());
    return jwtAccessTokenConverter;
}

Après cela, j'ai défini ce bean dans le JwtAccessTokenConverter p>

@Bean
public UserAuthenticationConverter userAuthenticationConverter() {
    DefaultUserAuthenticationConverter defaultUserAuthenticationConverter = new DefaultUserAuthenticationConverter();
    defaultUserAuthenticationConverter.setUserDetailsService(userDetailsService);
    return defaultUserAuthenticationConverter;
}


2 commentaires

C'est génial, j'en aurai besoin lorsque nous passerons à Spring Boot 2


merci, cela fonctionne dans mon serveur de ressources pour obtenir l'objet UserDetails personnalisé via: authentification finale authentification = SecurityContextHolder.getContext (). getAuthentication (); if (authentication! = null && authentication instanceof OAuth2Authentication) {final Object principal = ((OAuth2Authentication) authentication) .getUserAuthentication‌ () .getPrincipal ();



0
votes

Cela fonctionne pour moi. Merci.

@Bean
public UserAuthenticationConverter userAuthenticationConverter() {
    DefaultUserAuthenticationConverter defaultUserAuthenticationConverter = new DefaultUserAuthenticationConverter();
    defaultUserAuthenticationConverter.setUserDetailsService(userDetailsService);
    return defaultUserAuthenticationConverter;
}

@Bean
public JwtAccessTokenConverter accessTokenConverter() {
    final JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
    jwtAccessTokenConverter.setSigningKey(SECRET);
    ((DefaultAccessTokenConverter) jwtAccessTokenConverter.getAccessTokenConverter())
            .setUserTokenConverter(userAuthenticationConverter());
    return jwtAccessTokenConverter;
}


0 commentaires

1
votes

La cause varierait en fonction de la technique d'authentification / d'autorisation, mais dans mon cas, j'ai 2 filtres d'authentification / autorisation, le problème était de passer le username au lieu de l'ensemble de l'objet utilisateur au UsernamePasswordAuthenticationToken < / code> dans le filtre d'autorisation:

renvoie un nouveau UsernamePasswordAuthenticationToken (userDetails.getUsername (), .....);

Le premier argument du constructeur est défini sur l'objet Principe .

La solution était donc de transmettre tout l'objet userDetails

renvoie un nouveau UsernamePasswordAuthenticationToken (userDetails, .....);


0 commentaires