2017-12-10 107 views
9

Trong ứng dụng khởi động mùa xuân của tôi, tôi có nhiều điểm cuối tại /api/**. Sau đây là cấu hình ứng dụng của tôi:Mùa xuân để phân biệt khách truy cập trình duyệt từ các cuộc gọi API đến điểm cuối

@Configuration 
public class AppConfig extends WebMvcConfigurerAdapter { 

    private class PushStateResourceResolver implements ResourceResolver { 
     private Resource index = new ClassPathResource("/public/index.html"); 
     private List<String> handledExtensions = Arrays.asList("html", "js", 
       "json", "csv", "css", "png", "svg", "eot", "ttf", "woff", 
       "appcache", "jpg", "jpeg", "gif", "ico"); 

     private List<String> ignoredPaths = Arrays.asList("^api\\/.*$"); 

     @Override 
     public Resource resolveResource(HttpServletRequest request, 
       String requestPath, List<? extends Resource> locations, 
       ResourceResolverChain chain) { 
      return resolve(requestPath, locations); 
     } 

     @Override 
     public String resolveUrlPath(String resourcePath, 
       List<? extends Resource> locations, ResourceResolverChain chain) { 
      Resource resolvedResource = resolve(resourcePath, locations); 
      if (resolvedResource == null) { 
       return null; 
      } 
      try { 
       return resolvedResource.getURL().toString(); 
      } catch (IOException e) { 
       return resolvedResource.getFilename(); 
      } 
     } 

     private Resource resolve(String requestPath, 
       List<? extends Resource> locations) { 
      if (isIgnored(requestPath)) { 
       return null; 
      } 
      if (isHandled(requestPath)) { 
       return locations 
         .stream() 
         .map(loc -> createRelative(loc, requestPath)) 
         .filter(resource -> resource != null 
           && resource.exists()).findFirst() 
         .orElseGet(null); 
      } 
      return index; 
     } 

     private Resource createRelative(Resource resource, String relativePath) { 
      try { 
       return resource.createRelative(relativePath); 
      } catch (IOException e) { 
       return null; 
      } 
     } 

     private boolean isIgnored(String path) { 
      return false; 
      //   return !ignoredPaths.stream().noneMatch(rgx -> Pattern.matches(rgx, path)); 
      //deliberately made this change for examining the code 
     } 

     private boolean isHandled(String path) { 
      String extension = StringUtils.getFilenameExtension(path); 
      return handledExtensions.stream().anyMatch(
        ext -> ext.equals(extension)); 
     } 
    } 
} 

các quyền truy cập vào thiết bị đầu cuối phía sau /api/** được kiểm tra phải được xác thực, do đó khi tôi gõ vào /api/my_endpoint trong trình duyệt, tôi nhận được lỗi trở lại, mà không phải là những gì tôi muốn. Tôi muốn người dùng được phục vụ với index.html.

+1

Bạn có thể sử dụng bộ chặn hoặc bộ lọc cho tất cả yêu cầu và kiểm tra phản hồi và đường dẫn và hành động theo đúng cách, đúng không? – DvTr

Trả lời

2

Vì vậy, cuối cùng tôi giải quyết vấn đề này bằng cách sửa chữa cấu hình an ninh của tôi:

Tôi có một tùy chỉnh JWTAuthenticationFilter trong đó tôi ghi đè unsuccessfulAuthentication phương pháp:

@Override 
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException { 
    logger.debug("failed authentication while attempting to access "+ URL_PATH_HELPER.getPathWithinApplication((HttpServletRequest) request)); 
    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); 
    response.sendRedirect("/"); 
} 

Như bạn có thể thấy, nếu xác thực không thành công, tôi chuyển hướng người dùng đến "/" ngược lại sẽ được trình thu thập tài nguyên ghi lại và index.html sẽ được phục vụ!

+0

Rất hay vì bạn đã giải quyết được vấn đề của mình. Vui lòng cung cấp thêm thông tin vào lần tới, ví dụ: bạn sử dụng bộ lọc xác thực tùy chỉnh. – Christian

2

Bạn có thể kiểm tra sự X-Requested-With tiêu đề:

private boolean isAjax(HttpServletRequest request) { 
    String requestedWithHeader = request.getHeader("X-Requested-With"); 
    return "XMLHttpRequest".equals(requestedWithHeader); 
} 

UPDATE: Có lẽ đó là một cách tiếp cận tốt hơn để kiểm tra tiêu đề Accept. Tôi nghĩ xác suất cao hơn nhiều so với các trình duyệt bao gồm tiêu đề Accept: text/html so với tập lệnh, v.v. bao gồm tiêu đề X-Requested-With.

Bạn có thể tạo một điểm vào xác thực tùy chỉnh và chuyển hướng người dùng nếu Accept: text/html tiêu đề là hiện tại:

public class CustomEntryPoint implements AuthenticationEntryPoint { 

    private static final String ACCEPT_HEADER = "Accept"; 

    private final RedirectStrategy redirect = new DefaultRedirectStrategy(); 

    @Override 
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) 
      throws IOException, ServletException { 
     if (isHtmlRequest(request)) { 
      redirect.sendRedirect(request, response, "/"); 
     } else { 
      response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized access is not allowed"); 
     } 
    } 

    private boolean isHtmlRequest(HttpServletRequest request) { 
     String acceptHeader = request.getHeader(ACCEPT_HEADER); 
     List<MediaType> acceptedMediaTypes = MediaType.parseMediaTypes(acceptHeader); 
     return acceptedMediaTypes.contains(MediaType.TEXT_HTML); 
    } 

} 

Lưu ý:

Nếu bạn sử dụng một bộ lọc xác thực tùy chỉnh (thừa hưởng từ AbstractAuthenticationProcessingFilter) thì điểm vào xác thực sẽ không được gọi. Bạn có thể xử lý chuyển hướng trong phương thức unsuccessfulAuthentication() của AbstractAuthenticationProcessingFilter.

Alternatives:

  • chuẩn Override BasicErrorController xuân Boot và xử lý chuyển hướng của 401 Unauthorized lỗi đó.
  • Tại sao không chỉ trả lại JSON trên tất cả các cuộc gọi /api và html khác?
Các vấn đề liên quan