จัดการข้อยกเว้นการตรวจสอบความปลอดภัยในฤดูใบไม้ผลิด้วย @ExceptionHandler


101

ฉันใช้ Spring MVC @ControllerAdviceและ@ExceptionHandlerจัดการข้อยกเว้นทั้งหมดของ REST Api ใช้งานได้ดีสำหรับข้อยกเว้นที่ส่งโดยตัวควบคุมเว็บ mvc แต่ไม่ได้ผลสำหรับข้อยกเว้นที่เกิดจากตัวกรองแบบกำหนดเองสำหรับการรักษาความปลอดภัยแบบสปริงเนื่องจากทำงานก่อนที่จะเรียกใช้เมธอดคอนโทรลเลอร์

ฉันมีตัวกรองความปลอดภัยแบบสปริงที่กำหนดเองซึ่งทำการรับรองความถูกต้องตามโทเค็น:

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

        }

    }

}

ด้วยจุดเข้าที่กำหนดเองนี้:

@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());
    }

}

และด้วยคลาสนี้เพื่อจัดการกับข้อยกเว้นทั่วโลก:

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

สิ่งที่ฉันต้องทำคือส่งคืนเนื้อความ JSON โดยละเอียดแม้กระทั่งการรับรองความปลอดภัยในฤดูใบไม้ผลิ มีวิธีที่ทำให้ Spring Security AuthenticationEntryPoint และ spring mvc @ExceptionHandler ทำงานร่วมกันได้หรือไม่

ฉันใช้ spring security 3.1.4 และ spring mvc 3.2.4


9
คุณไม่สามารถ ... (@)ExceptionHandlerจะใช้ได้ผลก็ต่อเมื่อคำขอได้รับการจัดการโดยDispatcherServlet. อย่างไรก็ตามข้อยกเว้นนี้เกิดขึ้นก่อนหน้านั้นเนื่องจากไฟล์Filter. ดังนั้นคุณจะไม่สามารถจัดการกับข้อยกเว้นนี้ด้วย(@)ExceptionHandlerไฟล์.
M. Deinum

โอเคคุณพูดถูก มีวิธีส่งคืนเนื้อความ json พร้อมกับ response.sendError ของ EntryPoint หรือไม่
Nicola

ดูเหมือนว่าคุณต้องแทรกตัวกรองแบบกำหนดเองก่อนหน้านี้ในห่วงโซ่เพื่อตรวจจับข้อยกเว้นและส่งคืน เอกสารประกอบแสดงรายการตัวกรองนามแฝงและลำดับที่ใช้: docs.spring.io/spring-security/site/docs/3.1.4.RELEASE/…
Romski

1
หากตำแหน่งเดียวที่คุณต้องการ JSON ให้สร้าง / เขียนภายในไฟล์EntryPoint. คุณอาจต้องการสร้างวัตถุตรงนั้นและฉีดเข้าไปที่MappingJackson2HttpMessageConverterนั่น
M. Deinum

@ M.Deinum ฉันจะพยายามสร้าง json ภายในจุดเข้า
Nicola

คำตอบ:


64

ตกลงฉันพยายามตามที่แนะนำให้เขียน json ด้วยตัวเองจาก AuthenticationEntryPoint และใช้งานได้

สำหรับการทดสอบฉันเปลี่ยน AutenticationEntryPoint โดยการลบ response.sendError

@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() + "\" }");

    }
}

ด้วยวิธีนี้คุณสามารถส่งข้อมูล json ที่กำหนดเองพร้อมกับ 401 ที่ไม่ได้รับอนุญาตแม้ว่าคุณจะใช้ Spring Security AuthenticationEntryPoint

เห็นได้ชัดว่าคุณจะไม่สร้าง json เหมือนที่ฉันทำเพื่อจุดประสงค์ในการทดสอบ แต่คุณจะทำให้อินสแตนซ์คลาสบางตัวเป็นอนุกรม


3
ตัวอย่างการใช้ Jackson: ObjectMapper mapper = new ObjectMapper (); mapper.writeValue (response.getOutputStream (), FailResponse ใหม่ (401, authException.getLocalizedMessage (), "Access ถูกปฏิเสธ", ""));
Cyrusmith

1
ฉันรู้ว่าคำถามนี้เก่าไปหน่อย แต่คุณได้ลงทะเบียน AuthenticationEntryPoint เป็น SecurityConfig หรือไม่
leventunver

1
@leventunver ที่นี่คุณสามารถค้นหาวิธีการลงทะเบียนจุดเข้า: stackoverflow.com/questions/24684806/… .
Nicola

38

นี่เป็นปัญหาที่น่าสนใจมากที่Spring SecurityและSpring Web framework ไม่ค่อยสอดคล้องกันในวิธีจัดการกับการตอบสนอง ฉันเชื่อว่ามันต้องรองรับการจัดการข้อความแสดงข้อผิดพลาดด้วยMessageConverterวิธีที่สะดวก

ฉันพยายามที่จะหาวิธีที่สง่างามที่จะฉีดMessageConverterเข้าไปในฤดูใบไม้ผลิการรักษาความปลอดภัยเพื่อให้พวกเขาสามารถจับข้อยกเว้นและกลับพวกเขาในรูปแบบที่เหมาะสมตามการเจรจาต่อรองเนื้อหา ถึงกระนั้นวิธีแก้ปัญหาของฉันด้านล่างก็ไม่สวยหรู แต่อย่างน้อยก็ใช้ประโยชน์จากรหัสสปริง

ฉันคิดว่าคุณรู้วิธีรวมไลบรารี Jackson และ JAXB มิฉะนั้นจะไม่มีจุดที่จะดำเนินการต่อ มีทั้งหมด 3 ขั้นตอน

ขั้นตอนที่ 1 - สร้างคลาสแบบสแตนด์อโลนจัดเก็บ MessageConverters

คลาสนี้ไม่เล่นเวท RequestResponseBodyMethodProcessorมันก็เก็บแปลงข้อความและประมวลผล เวทมนตร์อยู่ในโปรเซสเซอร์นั้นซึ่งจะทำงานทั้งหมดรวมถึงการเจรจาต่อรองเนื้อหาและการแปลงเนื้อหาตอบสนองให้สอดคล้องกัน

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

ขั้นตอนที่ 2 - สร้าง AuthenticationEntryPoint

เช่นเดียวกับในบทช่วยสอนหลาย ๆ คลาสนี้จำเป็นต่อการใช้การจัดการข้อผิดพลาดแบบกำหนดเอง

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

ขั้นตอนที่ 3 - ลงทะเบียนจุดเข้า

ตามที่กล่าวไว้ฉันใช้ Java Config ฉันเพิ่งแสดงการกำหนดค่าที่เกี่ยวข้องที่นี่ควรมีการกำหนดค่าอื่น ๆ เช่นเซสชัน statelessเป็นต้น

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

ลองใช้กรณีล้มเหลวในการตรวจสอบสิทธิ์โปรดจำไว้ว่าส่วนหัวของคำขอควรมีAccept: XXXและคุณควรได้รับข้อยกเว้นใน JSON, XML หรือรูปแบบอื่น ๆ


1
ฉันพยายามจับInvalidGrantExceptionแต่เวอร์ชันของคุณCustomEntryPointไม่ถูกเรียกใช้ มีความคิดอะไรที่ฉันอาจขาดหายไป?
Stefan Falk

@ชื่อที่แสดง. ข้อยกเว้นการตรวจสอบสิทธิ์ทั้งหมดที่ไม่สามารถจับได้AuthenticationEntryPoint และAccessDeniedHandlerเช่นUsernameNotFoundExceptionและInvalidGrantExceptionสามารถจัดการโดยAuthenticationFailureHandlerเป็นอธิบายที่นี่
Wilson

26

วิธีที่ดีที่สุดที่ฉันพบคือมอบหมายข้อยกเว้นให้กับ HandlerExceptionResolver

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

จากนั้นคุณสามารถใช้ @ExceptionHandler เพื่อจัดรูปแบบการตอบสนองตามที่คุณต้องการ


10
ใช้งานได้เหมือนมีเสน่ห์ หาก Spring แสดงข้อผิดพลาดว่ามีคำจำกัดความของ bean 2 สำหรับการกำหนดเส้นทางอัตโนมัติคุณต้องเพิ่มคำอธิบายประกอบคุณสมบัติ: @Autowired @Qualifier ("handlerExceptionResolver") ตัวแก้ไข HandlerExceptionResolver ส่วนตัว
Daividh

2
โปรดทราบว่าการส่งตัวจัดการ null ของคุณ@ControllerAdviceจะไม่ทำงานหากคุณได้ระบุ basePackages ไว้ในคำอธิบายประกอบ ฉันต้องลบสิ่งนี้ออกทั้งหมดเพื่อให้สามารถเรียกตัวจัดการได้
Jarmex

ทำไมคุณถึงให้@Component("restAuthenticationEntryPoint")? ทำไมต้องมีชื่อเช่น restAuthenticationEntryPoint? เป็นการหลีกเลี่ยงการชนกันของชื่อสปริงหรือไม่?
theprogrammer

@Jarmex แทนโมฆะคุณผ่านอะไรมา? มันเป็นตัวจัดการบางอย่างใช่มั้ย? ฉันควรจะผ่านคลาสที่มีคำอธิบายประกอบ @ControllerAdvice หรือไม่? ขอบคุณ
โปรแกรมเมอร์

@ โปรแกรมเมอร์ฉันต้องปรับโครงสร้างแอปพลิเคชันเล็กน้อยเพื่อลบพารามิเตอร์คำอธิบายประกอบ basePackages เพื่อหลีกเลี่ยง - ไม่เหมาะ!
Jarmex

5

ในกรณีของ Spring Boot และ@EnableResourceServerค่อนข้างง่ายและสะดวกในการขยายResourceServerConfigurerAdapterแทนWebSecurityConfigurerAdapterการกำหนดค่า Java และลงทะเบียนแบบกำหนดเองAuthenticationEntryPointโดยการแทนที่configure(ResourceServerSecurityConfigurer resources)และใช้resources.authenticationEntryPoint(customAuthEntryPoint())ภายในวิธีการ

สิ่งนี้:

@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();
    }
}

นอกจากนี้ยังมีสิ่งที่ดี OAuth2AuthenticationEntryPointที่สามารถขยายได้ (เพราะมันไม่ได้สุดท้าย) AuthenticationEntryPointและบางส่วนมาใช้อีกครั้งในขณะที่การดำเนินการที่กำหนดเอง โดยเฉพาะอย่างยิ่งจะเพิ่มส่วนหัว "WWW-Authenticate" ที่มีรายละเอียดเกี่ยวกับข้อผิดพลาด

หวังว่านี่จะช่วยใครบางคนได้


ฉันกำลังลองสิ่งนี้ แต่commence()ฟังก์ชันของฉันAuthenticationEntryPointไม่ได้รับการเรียกใช้ - ฉันขาดอะไรไปหรือเปล่า?
Stefan Falk

4

รับคำตอบจาก @Nicola และ @Victor Wing และเพิ่มวิธีที่เป็นมาตรฐานมากขึ้น:

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

}

ตอนนี้คุณสามารถฉีด Jackson, Jaxb ที่กำหนดค่าไว้หรืออะไรก็ได้ที่คุณใช้เพื่อแปลงเนื้อหาการตอบสนองบนคำอธิบายประกอบ MVC หรือการกำหนดค่าตาม XML ด้วย serializers, deserializers และอื่น ๆ


ฉันเป็นมือใหม่มากสำหรับการเริ่มต้นฤดูใบไม้ผลิ: โปรดบอกฉันว่า "วิธีส่งวัตถุ messageConverter ไปยังจุดเริ่มต้นของการรับรองความถูกต้อง"
Kona Suresh

ผ่านตัวตั้งค่า เมื่อคุณใช้ XML คุณต้องสร้าง<property name="messageConverter" ref="myConverterBeanName"/>แท็ก เมื่อคุณใช้@Configurationคลาสเพียงแค่ใช้setMessageConverter()เมธอด
Gabriel Villacis

4

เราจำเป็นต้องใช้HandlerExceptionResolverในกรณีนั้น

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

นอกจากนี้คุณต้องเพิ่มในคลาสตัวจัดการข้อยกเว้นเพื่อส่งคืนวัตถุของคุณ

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

คุณอาจได้รับข้อผิดพลาดในช่วงเวลาของการทำงานโครงการเนื่องจากการใช้งานของหลาย ๆ ที่HandlerExceptionResolverในกรณีที่คุณต้องมีการเพิ่ม@Qualifier("handlerExceptionResolver")ในHandlerExceptionResolver


GenericResponseBeanเป็นเพียง java pojo คุณสามารถสร้างของคุณเองได้
Vinit Solanki

2

ฉันสามารถจัดการกับมันได้โดยเพียงแค่แทนที่เมธอด 'unsuccessfulAuthentication' ในตัวกรองของฉัน ที่นั่นฉันส่งการตอบสนองข้อผิดพลาดไปยังไคลเอนต์พร้อมรหัสสถานะ HTTP ที่ต้องการ

@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());
    }
}

1

อัปเดต:หากคุณชอบและต้องการดูโค้ดโดยตรงฉันมีสองตัวอย่างสำหรับคุณหนึ่งตัวอย่างใช้ Spring Security มาตรฐานซึ่งเป็นสิ่งที่คุณกำลังมองหาอีกอันหนึ่งใช้ Reactive Web และ Reactive Security ที่เทียบเท่า:
- ปกติ Web + Jwt Security
- Reactive Jwt

สิ่งที่ฉันมักใช้สำหรับปลายทางที่ใช้ JSON ของฉันมีลักษณะดังนี้:

@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());
    }
}

ตัวทำแผนที่วัตถุจะกลายเป็นถั่วเมื่อคุณเพิ่ม Spring web starter แต่ฉันชอบที่จะปรับแต่งดังนั้นนี่คือการใช้งาน 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;
    }

AuthenticationEntryPoint ดีฟอลต์ที่คุณตั้งไว้ในคลาส WebSecurityConfigurerAdapter ของคุณ:

@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);
    }
// ..........
}

1

ปรับแต่งฟิลเตอร์และกำหนดความผิดปกติควรมีวิธีการที่ดีกว่านี้

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

}


0

ฉันใช้ objectMapper Rest Service ทุกแห่งส่วนใหญ่ทำงานกับ json และในหนึ่งในการกำหนดค่าของคุณคุณได้กำหนดค่าตัวทำแผนที่วัตถุไว้แล้ว

รหัสเขียนด้วย Kotlin หวังว่ามันจะโอเค

@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)
     }}

0

ในResourceServerConfigurerAdapterชั้นเรียนการตัดโค้ดด้านล่างใช้ได้ผลสำหรับฉัน http.exceptionHandling().authenticationEntryPoint(new AuthFailureHandler()).and.csrf()..ไม่ทำงาน. นั่นเป็นเหตุผลว่าทำไมฉันจึงเขียนเป็นคำเรียกแยกต่างหาก

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

การใช้งาน AuthenticationEntryPoint สำหรับการตรวจจับการหมดอายุของโทเค็นและส่วนหัวการอนุญาตที่ขาดหายไป


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"
              + "}");
    }

  }
}


ไม่ทำงาน .. ยังคงแสดงข้อความเริ่มต้น
aswzen
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.