Behandeln Sie Spring Security-Authentifizierungsausnahmen mit @ExceptionHandler

95

Ich benutze Spring MVCs @ControllerAdviceund @ExceptionHandlerum alle Ausnahmen einer REST-API zu behandeln. Es funktioniert gut für Ausnahmen, die von Web-MVC-Controllern ausgelöst werden, aber nicht für Ausnahmen, die von benutzerdefinierten Spring Security-Filtern ausgelöst werden, da sie ausgeführt werden, bevor die Controller-Methoden aufgerufen werden.

Ich habe einen benutzerdefinierten Federsicherheitsfilter, der eine tokenbasierte Authentifizierung ausführt:

public class AegisAuthenticationFilter extends GenericFilterBean {

...

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

        try {

            ...         
        } catch(AuthenticationException authenticationException) {

            SecurityContextHolder.clearContext();
            authenticationEntryPoint.commence(request, response, authenticationException);

        }

    }

}

Mit diesem benutzerdefinierten Einstiegspunkt:

@Component("restAuthenticationEntryPoint")
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint{

    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authenticationException) throws IOException, ServletException {
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authenticationException.getMessage());
    }

}

Und mit dieser Klasse können Ausnahmen global behandelt werden:

@ControllerAdvice
public class RestEntityResponseExceptionHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler({ InvalidTokenException.class, AuthenticationException.class })
    @ResponseStatus(value = HttpStatus.UNAUTHORIZED)
    @ResponseBody
    public RestError handleAuthenticationException(Exception ex) {

        int errorCode = AegisErrorCode.GenericAuthenticationError;
        if(ex instanceof AegisException) {
            errorCode = ((AegisException)ex).getCode();
        }

        RestError re = new RestError(
            HttpStatus.UNAUTHORIZED,
            errorCode, 
            "...",
            ex.getMessage());

        return re;
    }
}

Was ich tun muss, ist, einen detaillierten JSON-Body auch für Spring Security AuthenticationException zurückzugeben. Gibt es eine Möglichkeit, Spring Security AuthenticationEntryPoint und Spring MVC @ExceptionHandler zusammenzuarbeiten?

Ich benutze Spring Security 3.1.4 und Spring MVC 3.2.4.

Nicola
quelle
9
Sie können nicht ... Das (@)ExceptionHandlerwird nur funktionieren, wenn die Anfrage von der bearbeitet wird DispatcherServlet. Diese Ausnahme tritt jedoch vorher auf, da sie von a ausgelöst wird Filter. Sie werden diese Ausnahme also niemals mit einem behandeln können (@)ExceptionHandler.
M. Deinum
OK du hast recht. Gibt es eine Möglichkeit, einen JSON-Body zusammen mit dem response.sendError des EntryPoint zurückzugeben?
Nicola
Klingt so, als müssten Sie früher in der Kette einen benutzerdefinierten Filter einfügen, um die Ausnahme abzufangen und entsprechend zurückzukehren. In der Dokumentation sind die Filter, ihre Aliase und die Reihenfolge aufgeführt, in der sie angewendet werden: docs.spring.io/spring-security/site/docs/3.1.4.RELEASE/…
Romski
1
Wenn Sie den JSON als einzigen Speicherort benötigen, erstellen / schreiben Sie ihn einfach in den EntryPoint. Möglicherweise möchten Sie das Objekt dort erstellen und dort ein Objekt einfügen MappingJackson2HttpMessageConverter.
M. Deinum
@ M.Deinum Ich werde versuchen, den JSON innerhalb des Einstiegspunkts zu bauen.
Nicola

Antworten:

55

Ok, ich habe wie vorgeschlagen versucht, den JSON selbst aus dem AuthenticationEntryPoint zu schreiben, und es funktioniert.

Nur zum Testen habe ich den AutenticationEntryPoint durch Entfernen von response.sendError geändert

@Component("restAuthenticationEntryPoint")
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint{

    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authenticationException) throws IOException, ServletException {

        response.setContentType("application/json");
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.getOutputStream().println("{ \"error\": \"" + authenticationException.getMessage() + "\" }");

    }
}

Auf diese Weise können Sie benutzerdefinierte JSON-Daten zusammen mit dem 401 nicht autorisiert senden, selbst wenn Sie Spring Security AuthenticationEntryPoint verwenden.

Natürlich würden Sie den JSON nicht wie ich zu Testzwecken erstellen, aber Sie würden eine Klasseninstanz serialisieren.

Nicola
quelle
3
Beispiel mit Jackson: ObjectMapper mapper = new ObjectMapper (); mapper.writeValue (response.getOutputStream (), neue FailResponse (401, authException.getLocalizedMessage (), "Zugriff verweigert", ""));
Cyrusmith
1
Ich weiß, dass die Frage etwas alt ist, aber haben Sie Ihren AuthenticationEntryPoint bei SecurityConfig registriert?
Leventunver
1
@leventunver Hier erfahren Sie, wie Sie den Einstiegspunkt registrieren: stackoverflow.com/questions/24684806/… .
Nicola
36

Dies ist ein sehr interessantes Problem, da Spring Security und Spring Web Framework in der Art und Weise, wie sie mit der Antwort umgehen, nicht ganz konsistent sind. Ich glaube, es muss die Behandlung von Fehlermeldungen MessageConverterauf praktische Weise nativ unterstützen .

Ich habe versucht, einen eleganten Weg zu finden, um MessageConverterSpring Security zu integrieren, damit sie die Ausnahme abfangen und sie entsprechend der Inhaltsverhandlung in einem richtigen Format zurückgeben können . Trotzdem ist meine Lösung unten nicht elegant, aber ich benutze zumindest Spring-Code.

Ich gehe davon aus, dass Sie wissen, wie man die Jackson- und JAXB-Bibliothek einbindet, sonst macht es keinen Sinn, fortzufahren. Insgesamt gibt es 3 Schritte.

Schritt 1 - Erstellen Sie eine eigenständige Klasse, in der MessageConverters gespeichert sind

Diese Klasse spielt keine Magie. Es speichert einfach die Nachrichtenkonverter und einen Prozessor RequestResponseBodyMethodProcessor. Die Magie steckt in diesem Prozessor, der die gesamte Arbeit erledigt, einschließlich der Aushandlung von Inhalten und der entsprechenden Konvertierung des Antwortkörpers.

public class MessageProcessor { // Any name you like
    // List of HttpMessageConverter
    private List<HttpMessageConverter<?>> messageConverters;
    // under org.springframework.web.servlet.mvc.method.annotation
    private RequestResponseBodyMethodProcessor processor;

    /**
     * Below class name are copied from the framework.
     * (And yes, they are hard-coded, too)
     */
    private static final boolean jaxb2Present =
        ClassUtils.isPresent("javax.xml.bind.Binder", MessageProcessor.class.getClassLoader());

    private static final boolean jackson2Present =
        ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", MessageProcessor.class.getClassLoader()) &&
        ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", MessageProcessor.class.getClassLoader());

    private static final boolean gsonPresent =
        ClassUtils.isPresent("com.google.gson.Gson", MessageProcessor.class.getClassLoader());

    public MessageProcessor() {
        this.messageConverters = new ArrayList<HttpMessageConverter<?>>();

        this.messageConverters.add(new ByteArrayHttpMessageConverter());
        this.messageConverters.add(new StringHttpMessageConverter());
        this.messageConverters.add(new ResourceHttpMessageConverter());
        this.messageConverters.add(new SourceHttpMessageConverter<Source>());
        this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());

        if (jaxb2Present) {
            this.messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
        }
        if (jackson2Present) {
            this.messageConverters.add(new MappingJackson2HttpMessageConverter());
        }
        else if (gsonPresent) {
            this.messageConverters.add(new GsonHttpMessageConverter());
        }

        processor = new RequestResponseBodyMethodProcessor(this.messageConverters);
    }

    /**
     * This method will convert the response body to the desire format.
     */
    public void handle(Object returnValue, HttpServletRequest request,
        HttpServletResponse response) throws Exception {
        ServletWebRequest nativeRequest = new ServletWebRequest(request, response);
        processor.handleReturnValue(returnValue, null, new ModelAndViewContainer(), nativeRequest);
    }

    /**
     * @return list of message converters
     */
    public List<HttpMessageConverter<?>> getMessageConverters() {
        return messageConverters;
    }
}

Schritt 2 - Erstellen Sie AuthenticationEntryPoint

Wie in vielen Tutorials ist diese Klasse für die Implementierung einer benutzerdefinierten Fehlerbehandlung unerlässlich.

public class CustomEntryPoint implements AuthenticationEntryPoint {
    // The class from Step 1
    private MessageProcessor processor;

    public CustomEntryPoint() {
        // It is up to you to decide when to instantiate
        processor = new MessageProcessor();
    }

    @Override
    public void commence(HttpServletRequest request,
        HttpServletResponse response, AuthenticationException authException)
        throws IOException, ServletException {

        // This object is just like the model class, 
        // the processor will convert it to appropriate format in response body
        CustomExceptionObject returnValue = new CustomExceptionObject();
        try {
            processor.handle(returnValue, request, response);
        } catch (Exception e) {
            throw new ServletException();
        }
    }
}

Schritt 3 - Registrieren Sie den Einstiegspunkt

Wie bereits erwähnt, mache ich das mit Java Config. Ich zeige hier nur die relevante Konfiguration, es sollte eine andere Konfiguration geben, z. B. Sitzung ohne Status usw.

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.exceptionHandling().authenticationEntryPoint(new CustomEntryPoint());
    }
}

Versuchen Sie es mit einigen Fällen, in denen die Authentifizierung fehlschlägt. Denken Sie daran, dass der Anforderungsheader Accept: XXX enthalten sollte und Sie die Ausnahme in JSON, XML oder anderen Formaten erhalten sollten.

Victor Wong
quelle
1
Ich versuche eine zu fangen, InvalidGrantExceptionaber meine Version von Ihnen CustomEntryPointwird nicht aufgerufen. Irgendeine Idee, was mir fehlen könnte?
Anzeigename
@Anzeigename. Alle Authentifizierungs Ausnahmen, die nicht durch gefangen werden AuthenticationEntryPoint und AccessDeniedHandlerwie UsernameNotFoundExceptionund InvalidGrantExceptionkann behandelt werden , AuthenticationFailureHandlerwie hier erläutert .
Wilson
23

Der beste Weg, den ich gefunden habe, besteht darin, die Ausnahme an den HandlerExceptionResolver zu delegieren

@Component("restAuthenticationEntryPoint")
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {

    @Autowired
    private HandlerExceptionResolver resolver;

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        resolver.resolveException(request, response, null, exception);
    }
}

Anschließend können Sie @ExceptionHandler verwenden, um die Antwort wie gewünscht zu formatieren.

Christophe Bornet
quelle
9
Klappt wunderbar. Wenn Spring einen Fehler auslöst, der besagt, dass es eine 2-Bean-Definition für Autowirering gibt, müssen Sie eine Qualifier-Annotation hinzufügen: @Autowired @Qualifier ("handlerExceptionResolver") privater HandlerExceptionResolver-Resolver;
Daividh
1
@ControllerAdviceBeachten Sie, dass das Übergeben eines Null- Handlers nicht funktioniert, wenn Sie in der Annotation basePackages angegeben haben. Ich musste dies vollständig entfernen, damit der Handler aufgerufen werden konnte.
Jarmex
Warum hast du gegeben @Component("restAuthenticationEntryPoint")? Warum braucht man einen Namen wie restAuthenticationEntryPoint? Ist es, um einige Kollisionen mit Spring-Namen zu vermeiden?
Programmierer
@Jarmex Also, anstelle von null, was hast du bestanden? Es ist eine Art Handler, oder? Sollte ich nur eine Klasse bestehen, die mit @ControllerAdvice kommentiert wurde? Danke
Programmierer
@theprogrammer, ich musste die Anwendung leicht umstrukturieren, um den annotationsparameter basePackages zu entfernen, um sie zu umgehen - nicht ideal!
Jarmex
5

Im Fall von Spring Boot und @EnableResourceServerist es relativ einfach und bequem, ResourceServerConfigurerAdapteranstelle WebSecurityConfigurerAdapterder Java-Konfiguration zu erweitern und eine benutzerdefinierte zu registrieren, indem die Methode AuthenticationEntryPointüberschrieben configure(ResourceServerSecurityConfigurer resources)und verwendet resources.authenticationEntryPoint(customAuthEntryPoint())wird.

Etwas wie das:

@Configuration
@EnableResourceServer
public class CommonSecurityConfig extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.authenticationEntryPoint(customAuthEntryPoint());
    }

    @Bean
    public AuthenticationEntryPoint customAuthEntryPoint(){
        return new AuthFailureHandler();
    }
}

Es gibt auch eine nette OAuth2AuthenticationEntryPoint, die erweitert werden kann (da sie nicht endgültig ist) und teilweise wiederverwendet werden kann, während eine benutzerdefinierte implementiert wird AuthenticationEntryPoint. Insbesondere werden "WWW-Authenticate" -Header mit fehlerbezogenen Details hinzugefügt.

Hoffe das wird jemandem helfen.

Vladimir Salin
quelle
Ich versuche es, aber die commence()Funktion von mir AuthenticationEntryPointwird nicht aufgerufen - fehlt mir etwas?
Anzeigename
4

Nehmen Sie die Antworten von @Nicola und @Victor Wing und fügen Sie einen standardisierteren Weg hinzu:

import org.springframework.beans.factory.InitializingBean;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class UnauthorizedErrorAuthenticationEntryPoint implements AuthenticationEntryPoint, InitializingBean {

    private HttpMessageConverter messageConverter;

    @SuppressWarnings("unchecked")
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {

        MyGenericError error = new MyGenericError();
        error.setDescription(exception.getMessage());

        ServerHttpResponse outputMessage = new ServletServerHttpResponse(response);
        outputMessage.setStatusCode(HttpStatus.UNAUTHORIZED);

        messageConverter.write(error, null, outputMessage);
    }

    public void setMessageConverter(HttpMessageConverter messageConverter) {
        this.messageConverter = messageConverter;
    }

    @Override
    public void afterPropertiesSet() throws Exception {

        if (messageConverter == null) {
            throw new IllegalArgumentException("Property 'messageConverter' is required");
        }
    }

}

Jetzt können Sie konfiguriertes Jackson, Jaxb oder was auch immer Sie verwenden, um Antwortkörper in Ihre MVC-Annotation oder XML-basierte Konfiguration mit ihren Serialisierern, Deserialisierern usw. zu konvertieren.

Gabriel Villacis
quelle
Ich bin sehr neu in Spring Boot: Bitte sagen Sie mir, "wie MessageConverter-Objekt an AuthenticationEntry Point übergeben"
Kona Suresh
Durch den Setter. Wenn Sie XML verwenden, müssen Sie ein <property name="messageConverter" ref="myConverterBeanName"/>Tag erstellen . Wenn Sie eine @ConfigurationKlasse verwenden, verwenden Sie einfach die setMessageConverter()Methode.
Gabriel Villacis
4

Wir müssen HandlerExceptionResolverin diesem Fall verwenden.

@Component
public class RESTAuthenticationEntryPoint implements AuthenticationEntryPoint {

    @Autowired
    //@Qualifier("handlerExceptionResolver")
    private HandlerExceptionResolver resolver;

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
        resolver.resolveException(request, response, null, authException);
    }
}

Außerdem müssen Sie die Exception-Handler-Klasse hinzufügen, um Ihr Objekt zurückzugeben.

@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler(AuthenticationException.class)
    public GenericResponseBean handleAuthenticationException(AuthenticationException ex, HttpServletResponse response){
        GenericResponseBean genericResponseBean = GenericResponseBean.build(MessageKeys.UNAUTHORIZED);
        genericResponseBean.setError(true);
        response.setStatus(HttpStatus.UNAUTHORIZED.value());
        return genericResponseBean;
    }
}

können Sie einen Fehler zum Zeitpunkt des bekommen ein Projekt laufen , weil mehrere Implementierungen HandlerExceptionResolver, in diesem Fall muss man hinzufügen , @Qualifier("handlerExceptionResolver")aufHandlerExceptionResolver

Vinit Solanki
quelle
GenericResponseBeanist nur Java Pojo, können Sie Ihre eigenen erstellen
Vinit Solanki
2

Ich konnte damit umgehen, indem ich einfach die Methode 'unsuccessfulAuthentication' in meinem Filter überschrieb. Dort sende ich eine Fehlerantwort mit dem gewünschten HTTP-Statuscode an den Client.

@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
        AuthenticationException failed) throws IOException, ServletException {

    if (failed.getCause() instanceof RecordNotFoundException) {
        response.sendError((HttpServletResponse.SC_NOT_FOUND), failed.getMessage());
    }
}
user3619911
quelle
1

Update: Wenn Sie den Code direkt sehen möchten und möchten, habe ich zwei Beispiele für Sie, eines mit Standard-Spring-Sicherheit, nach dem Sie suchen, und das andere mit dem Äquivalent von Reactive Web und Reactive Security:
- Normal Web + Jwt-Sicherheit
- Reaktives Jwt

Das, das ich immer für meine JSON-basierten Endpunkte verwende, sieht folgendermaßen aus:

@Component
public class JwtAuthEntryPoint implements AuthenticationEntryPoint {

    @Autowired
    ObjectMapper mapper;

    private static final Logger logger = LoggerFactory.getLogger(JwtAuthEntryPoint.class);

    @Override
    public void commence(HttpServletRequest request,
                         HttpServletResponse response,
                         AuthenticationException e)
            throws IOException, ServletException {
        // Called when the user tries to access an endpoint which requires to be authenticated
        // we just return unauthorizaed
        logger.error("Unauthorized error. Message - {}", e.getMessage());

        ServletServerHttpResponse res = new ServletServerHttpResponse(response);
        res.setStatusCode(HttpStatus.UNAUTHORIZED);
        res.getServletResponse().setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
        res.getBody().write(mapper.writeValueAsString(new ErrorResponse("You must authenticated")).getBytes());
    }
}

Der Objekt-Mapper wird zu einer Bean, sobald Sie den Spring-Web-Starter hinzufügen. Ich ziehe es jedoch vor, ihn anzupassen. Hier ist meine Implementierung für ObjectMapper:

  @Bean
    public Jackson2ObjectMapperBuilder objectMapperBuilder() {
        Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
        builder.modules(new JavaTimeModule());

        // for example: Use created_at instead of createdAt
        builder.propertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);

        // skip null fields
        builder.serializationInclusion(JsonInclude.Include.NON_NULL);
        builder.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        return builder;
    }

Der Standard-AuthenticationEntryPoint, den Sie in Ihrer WebSecurityConfigurerAdapter-Klasse festgelegt haben:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// ............
   @Autowired
    private JwtAuthEntryPoint unauthorizedHandler;
@Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and().csrf().disable()
                .authorizeRequests()
                // .antMatchers("/api/auth**", "/api/login**", "**").permitAll()
                .anyRequest().permitAll()
                .and()
                .exceptionHandling().authenticationEntryPoint(unauthorizedHandler)
                .and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);


        http.headers().frameOptions().disable(); // otherwise H2 console is not available
        // There are many ways to ways of placing our Filter in a position in the chain
        // You can troubleshoot any error enabling debug(see below), it will print the chain of Filters
        http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
    }
// ..........
}
Melardev
quelle
1

Passen Sie den Filter an und bestimmen Sie, welche Art von Anomalie es gibt. Es sollte eine bessere Methode als diese geben

public class ExceptionFilter extends OncePerRequestFilter {

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
    String msg = "";
    try {
        filterChain.doFilter(request, response);
    } catch (Exception e) {
        if (e instanceof JwtException) {
            msg = e.getMessage();
        }
        response.setCharacterEncoding("UTF-8");
        response.setContentType(MediaType.APPLICATION_JSON.getType());
        response.getWriter().write(JSON.toJSONString(Resp.error(msg)));
        return;
    }
}

}}

Polieren
quelle
0

Ich benutze den objectMapper. Jeder Rest Service arbeitet hauptsächlich mit json, und in einer Ihrer Konfigurationen haben Sie bereits einen Objekt-Mapper konfiguriert.

Code ist in Kotlin geschrieben, hoffentlich ist es in Ordnung.

@Bean
fun objectMapper(): ObjectMapper {
    val objectMapper = ObjectMapper()
    objectMapper.registerModule(JodaModule())
    objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)

    return objectMapper
}

class UnauthorizedAuthenticationEntryPoint : BasicAuthenticationEntryPoint() {

    @Autowired
    lateinit var objectMapper: ObjectMapper

    @Throws(IOException::class, ServletException::class)
    override fun commence(request: HttpServletRequest, response: HttpServletResponse, authException: AuthenticationException) {
        response.addHeader("Content-Type", "application/json")
        response.status = HttpServletResponse.SC_UNAUTHORIZED

        val responseError = ResponseError(
            message = "${authException.message}",
        )

        objectMapper.writeValue(response.writer, responseError)
     }}
Spin1987
quelle
0

In der ResourceServerConfigurerAdapterKlasse hat der unten stehende Code für mich funktioniert. http.exceptionHandling().authenticationEntryPoint(new AuthFailureHandler()).and.csrf()..funktioniert nicht. Deshalb habe ich es als separaten Anruf geschrieben.

public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {

        http.exceptionHandling().authenticationEntryPoint(new AuthFailureHandler());

        http.csrf().disable()
                .anonymous().disable()
                .authorizeRequests()
                .antMatchers(HttpMethod.OPTIONS).permitAll()
                .antMatchers("/subscribers/**").authenticated()
                .antMatchers("/requests/**").authenticated();
    }

Implementierung von AuthenticationEntryPoint zum Abfangen des Ablaufs von Token und des fehlenden Autorisierungsheaders.


public class AuthFailureHandler implements AuthenticationEntryPoint {

  @Override
  public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e)
      throws IOException, ServletException {
    httpServletResponse.setContentType("application/json");
    httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);

    if( e instanceof InsufficientAuthenticationException) {

      if( e.getCause() instanceof InvalidTokenException ){
        httpServletResponse.getOutputStream().println(
            "{ "
                + "\"message\": \"Token has expired\","
                + "\"type\": \"Unauthorized\","
                + "\"status\": 401"
                + "}");
      }
    }
    if( e instanceof AuthenticationCredentialsNotFoundException) {

      httpServletResponse.getOutputStream().println(
          "{ "
              + "\"message\": \"Missing Authorization Header\","
              + "\"type\": \"Unauthorized\","
              + "\"status\": 401"
              + "}");
    }

  }
}
Kemal Atik
quelle