2015-08-04 11 views
5

Abbiamo un'API di riposo che utilizza Spring OAuth2. Dopo che l'utente è autenticato, tutte le risposte JSON sono nel seguente formato:Come avvolgere un'eccezione OAuth2?

{"code" : 12345, "data" : "..." } 

Ma la risposta JSON per errori di autenticazione non è in linea con il formato sopra descritto, in quanto tale è gestito da Spring.

Ad esempio in caso di credenziali non corrette, i clienti ottenere il codice di stato HTTP 400 con risposta JSON come segue:

{"error": "invalid_grant", "error_description": "Bad credentials" } 

Nel caso in cui l'account utente è bloccato, i clienti ottengono il codice di stato HTTP 400 con JSON risposta come segue

{"error":"invalid_grant","error_description":"User account is locked"} 

Tutto ciò è causa Spring TokenEndpoint.handleException() gestisce le eccezioni associati/oauth/gettone

Vorrei modificare la risposta JSON per gli errori OAuth2 per seguire il primo formato.

Questo è quello che ho provato finora senza successo:

  1. Uso ControllerAdvice con alto ordine precendenza & uso @ExceptionHandler come descritto here
  2. attuazione OAuth2ExceptionRenderer come descritto here
  3. attuare ExceptionMapper
  4. aggiunto un nuovo ObjectMapper con l'estensione di StdSerializer. Sebbene il mio objectmapper sia inizializzato, non viene usato per serializzare le eccezioni. Forse perché Spring chiama direttamente MappingJackson2HttpMessageConverter e sembra che ci siano diverse istanze di questa classe nella mia app.

Qualsiasi aiuto in uno degli approcci sopra o uno nuovo sarebbe molto apprezzato.

Non ho provato l'approccio this poiché non riesco a modificare il contextpath per i client esistenti.

risposta

0

Se si desidera gestire il processo di autenticazione, è possibile impostare il proprio gestore di autenticazione personalizzato

<oauth:authorization-server 
    client-details-service-ref="clientDetails" token-services-ref="tokenServices" 
    user-approval-handler-ref="userApprovalHandler"> 
    <oauth:authorization-code /> 
    <oauth:implicit /> 
    <oauth:refresh-token /> 
    <oauth:client-credentials /> 
    <oauth:password authentication-manager-ref="customAuthenticationManager" /> 
</oauth:authorization-server> 

<authentication-manager id="customAuthenticationManager" 
    xmlns="http://www.springframework.org/schema/security"> 
    <authentication-provider ref="customAuthenticationProvider" /> 
</authentication-manager> 

<bean id="customAuthenticationProvider" 
    class="com.any.CustomAuthenticationProvider"> 
</bean> 

creano provider di autenticazione personalizzato che implementa AuthenticationProvider

public class UserAuthenticationProvider implements AuthenticationProvider { 

    @Override 
    public Authentication authenticate(Authentication authentication) throws AuthenticationException { 

     UsernamePasswordAuthenticationToken auth = (UsernamePasswordAuthenticationToken) authentication; 
     String username = auth.getName(); 
     String password = token.getCredentials().toString(); 
     User user = userService.loadByUsername(username); 
     if(user.isLocked){ 
      throw new UserLockedException("User is locked"); 
     } 
     if(another.something.bad.happened){ 
      throw new AnotherSomethingBadHappenedException("Error"); 
     } 

     // setup authorities 
     //... 

     return new UsernamePasswordAuthenticationToken(user, password, authorities); 
    } 


} 

Ora avete il vostro eccezioni, e utilizzando ExceptionMapper è possibile tradurre l'eccezione generata nel processo di autenticazione nel messaggio di risposta personalizzato.

Un'altra personalizzazione è possibile creare è in processo di autorizzazione con la creazione di una classe personalizzata che si estende ApprovalStoreUserApprovalHandler

public class CustomUserApprovalHandler extends ApprovalStoreUserApprovalHandler { 

    // stripped 

    @Override 
    public AuthorizationRequest checkForPreApproval(AuthorizationRequest authorizationRequest, 
      Authentication userAuthentication) { 

     ClientDetails client = clientDetailsService 
          .loadClientByClientId(authorizationRequest.getClientId()); 
     // here, you have the client and the user 
     // you can do any checking here and throw any exception 
     authorizationRequest.setApproved(approved); 
     return authorizationRequest; 
    } 
} 

Crea definizione di fagioli per quella classe

<bean id="userApprovalHandler" 
    class="com.any.CustomUserApprovalHandler"> 
     <property name="approvalStore" ref="approvalStore" /> 
     <property name="requestFactory" ref="oAuth2RequestFactory" /> 
     <property name="clientDetailsService" ref="clientDetails" /> 
     <property name="useApprovalStore" value="true" /> 
    </bean> 
+0

come posso usare ExceptionMapper da tradurre eccezione? –

+0

@FerasOdeh ExceptionMapper è la funzione JAX-WS, ci sono molti esempi su Internet. Meglio creare una nuova domanda se necessario – MangEngkus

+0

@MangEngkus Come posso risolvere 'approvalStore' in quanto sono le variabili di livello di classe di ApprovalStoreUserApprovalHandler e sto ottenendo sotto l'eccezione, Causato da: org.springframework.beans.factory.NoSuchBeanDefinitionException: Nessun bean chiamato 'approvalStore' è definito – Soumyaansh

1

ho affrontato esattamente lo stesso problema e allenamento una soluzione infine. Uso una classe ExceptionHandlerExceptionResolver personalizzata come resolver che ha sovrascritto il metodo getExceptionHandler come mostrato nel codice sottostante, quindi utilizzare nuovamente @ControllerAdvice con l'ordine di precedenza più alto, infine funziona.

public class MyExceptionHandlerExceptionResolver extends ExceptionHandlerExceptionResolver { 
private Map<ControllerAdviceBean, ExceptionHandlerMethodResolver> exceptionHandlerAdviceCache = null; 

@Override 
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception) { 
    Class<?> handlerType = (handlerMethod != null ? handlerMethod.getBeanType() : null); 
    List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext()); 
    if (exceptionHandlerAdviceCache==null){ 
     exceptionHandlerAdviceCache = new LinkedHashMap<ControllerAdviceBean, ExceptionHandlerMethodResolver>(); 
     for (ControllerAdviceBean adviceBean:adviceBeans){ 
      ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(adviceBean.getBeanType()); 
      exceptionHandlerAdviceCache.put(adviceBean, resolver); 
     } 
    } 
    for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) { 
     if (entry.getKey().isApplicableToBeanType(handlerType)) { 
      ExceptionHandlerMethodResolver resolver = entry.getValue(); 
      Method method = resolver.resolveMethod(exception); 
      if (method != null) { 
       return new ServletInvocableHandlerMethod(entry.getKey().resolveBean(), method); 
      } 
     } 
    } 
    return null; 
} 
} 

classe di utilizzo MyExceptionHandlerExceptionResolver in Configurare

@EnableWebMvc 
@Configuration 
public class WebMVCConfiguration extends WebMvcConfigurationSupport { 
@Bean 
public ExceptionHandlerExceptionResolver handlerExceptionResolver() { 
    MyExceptionHandlerExceptionResolver exceptionResolver = new MyExceptionHandlerExceptionResolver(); 
    exceptionResolver.setOrder(0); 
    exceptionResolver.setMessageConverters(messageConverters()); 
    return exceptionResolver; 
} 

private MappingJackson2HttpMessageConverter jsonHttpMessageConverter() { 
    return new MappingJackson2HttpMessageConverter(); 
} 

private List<HttpMessageConverter<?>> messageConverters() { 
    List<HttpMessageConverter<?>> messageConverters = new ArrayList<>(); 
    messageConverters.add(jsonHttpMessageConverter()); 
    return messageConverters; 
} 
}