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);
}
}
4 Réponses :
Avec Spring Boot 1.5.x, vous pouvez implémenter un PrincipalExtractor et remplacer son Object extractPrincipal (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:
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
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;
}
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 ();
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;
}
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, .....);
getPrincipalpeut renvoyer ce qu'il veut. Donc, ce qui est retourné dépend de l'implémentation de l 'Authentificationutilisée et l'OAuth2Authenticationrenvoie uneString. 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?