2015-08-04 20 views
5

Mamy odpoczynku API, który używa Spring OAuth2. Po uwierzytelnieniu użytkownika, wszystkie odpowiedzi JSON są w następującym formacie:Jak zawinąć wyjątek OAuth2?

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

Ale odpowiedź JSON dla niepowodzeń uwierzytelniania nie jest ono zgodne z powyższym formacie, jako że zajmuje się wiosną.

Na przykład w przypadku nieprawidłowych poświadczeń, klienci dostać kod stanu HTTP 400 z odpowiedzi JSON, co następuje:

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

W przypadku, gdy konto użytkownika zostanie zablokowane, klienci otrzymują kod stanu HTTP 400 z JSON odpowiedź następująco

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

Wszystko to dlatego, że wiosna TokenEndpoint.handleException() jest obsługa wyjątków związanych z/OAuth/żeton

Chciałbym zmienić odpowiedź JSON na niepowodzenia OAuth2, aby postępować zgodnie z pierwszym formatem.

To co próbowałem dotąd bez powodzenia:

  1. Korzystanie ControllerAdvice z najwyższego rzędu precendence & użytku @ExceptionHandler opisanym here
  2. wykonawczych OAuth2ExceptionRenderer jak opisano here
  3. wdrożyć ExceptionMapper
  4. dodano nowy ObjectMapper z rozszerzeniem StdSerializer. Chociaż mój obiektarz jest zainicjowany, nie jest używany do szeregowania wyjątków. Może dlatego, że Spring wywołuje bezpośrednio MappingJackson2HttpMessageConverter i wygląda na to, że w mojej aplikacji jest kilka wystąpień tej klasy.

Każda pomoc w dowolnym z powyższych podejść lub w nowym byłaby bardzo cenna.

Nie próbowałem podejścia this, ponieważ nie mogę zmienić ścieżki kontekstowej dla istniejących klientów.

Odpowiedz

0

Jeśli chcesz obsługiwać proces uwierzytelniania, można skonfigurować swój własny menedżer uwierzytelniania zwyczaj

<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> 

create niestandardowego dostawcy uwierzytelniania, który implementuje 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); 
    } 


} 

Teraz masz własny wyjątek, a za pomocą ExceptionMapper można przetłumaczyć wyjątek zgłoszony w procesie uwierzytelniania do niestandardowego komunikatu odpowiedzi.

Innym dostosowywania można utworzyć jest proces autoryzacji poprzez utworzenie niestandardowej klasy, która rozciąga 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; 
    } 
} 

stworzyć definicję fasoli dla tej klasy

<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

jaki sposób mogę korzystać ExceptionMapper tłumaczyć wyjątek? –

+0

@FerasOdeh ExceptionMapper jest funkcją JAX-WS, istnieje wiele przykładów tam w Internecie. Lepiej utworzyć nowe pytania w razie potrzeby – MangEngkus

+0

@MangEngkus Jak mogę rozwiązać "approvalStore", ponieważ są to zmienne na poziomie klasy obiektu ApprovalStoreUserApprovalHandler i otrzymuję poniżej wyjątku, spowodowane przez: org.springframework.beans.factory.NoSuchBeanDefinitionException: Brak komponentu bean o nazwie "approvalStore" jest zdefiniowane jako – Soumyaansh

1

wychodził dokładną sam problem i treningu rozwiązanie ostatecznie. Używam niestandardowej klasy ExceptionHandlerExceptionResolver jako resolvera, która nadpisała metodę getExceptionHandler jak pokazano w poniższym kodzie, a następnie ponownie użyj @ControllerAdvice z najwyższą kolejnością precendence, wreszcie działa.

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; 
} 
} 

użycie klasy MyExceptionHandlerExceptionResolver w configure

@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; 
} 
} 
Powiązane problemy