Chúng tôi đang tạo một API RESTful với Spring MVC + spring security + hibernate. API có thể tạo cả JSON và HTML. Thực hiện xử lý lỗi tốt cho bảo mật mùa xuân khiến tôi đau đầu:Xử lý ngoại lệ tùy chỉnh trong Spring Security
Xác thực có thể xảy ra theo nhiều cách khác nhau: BasicAuth, thông qua các tham số khác nhau trong yêu cầu POST và thông qua đăng nhập web. Đối với mỗi cơ chế xác thực, có một bộ lọc được khai báo trong phần tử không gian tên <http>
của cấu hình bảo mật mùa xuân xml.
Chúng tôi xử lý tất cả ngoại lệ mùa xuân của chúng tôi trong một tùy chỉnh HandlerExceptionResolver
. Điều này làm việc tốt cho tất cả các ngoại lệ được ném trong bộ điều khiển của chúng tôi, nhưng tôi không biết cách xử lý các ngoại lệ tùy chỉnh được ném trong các bộ lọc bảo mật mùa xuân tùy chỉnh. Vì bộ lọc bảo mật mùa xuân xuất hiện trước khi bất kỳ bộ điều khiển nào của chúng ta được gọi, chúng ta không thấy các ngoại lệ mà chúng ta ném vào các bộ lọc bảo mật mùa xuân tùy chỉnh của mình.
Tôi đã tìm thấy câu hỏi này tại đây trên stackoverflow: Use custom exceptions in Spring Security. Tuy nhiên tôi không hiểu nơi họ xử lý các trường hợp ngoại lệ được ném ở đó. Chúng tôi đã thử phương pháp này nhưng tùy chỉnh HandlerExceptionResolver
của chúng tôi không được gọi. Thay vào đó, người dùng được trình bày với một stacktrace xấu xí được render bởi tomcat.
Tại sao chúng ta cần điều này? Người dùng có thể được kích hoạt và hủy kích hoạt. Nếu chúng bị hủy kích hoạt và cố gắng thực hiện một số hành động nhất định, chúng tôi muốn trả lại JSON bằng thông báo lỗi tùy chỉnh. Điều này sẽ khác với những gì được hiển thị khi bảo mật mùa xuân ném một số AccessDeniedException
. Các AccessDeniedException
bằng cách nào đó làm cho nó để HandlerExceptionResolver
của chúng tôi, nhưng tôi không thể làm theo cách chính xác.
giải pháp có thể Chúng tôi nghĩ về việc sử dụng ExceptionTranslationFilter
, tuy nhiên điều này không được gọi khi chúng ta ném ngoại lệ tùy chỉnh của chúng tôi (thiết lập một breakpoint trong báo cáo kết quả khai thác của phương pháp doFilter()). Theo hiểu biết của tôi, khối catch này nên được gọi và một điểm vào xác thực sẽ được sử dụng.
Một khả năng khác: Chúng ta có thể làm điều gì đó tương tự như ExceptionTranslationFilter
trong chuỗi bộ lọc bảo mật mùa xuân và làm điều gì đó tương tự như những gì AccessDeniedHandler
của nó không:
RequestDispatcher dispatcher = request.getRequestDispatcher(errorPage);
dispatcher.forward(request, response);
Chúng ta có thể thêm một số thông số (mã lỗi, lý do, vv) theo yêu cầu và nó trỏ đến một bộ điều khiển sẽ xử lý hiển thị trong JSON hoặc HTML.
Dưới đây là một trích đoạn ngắn của cấu hình của chúng tôi:
Xuân An:
<http create-session="stateless" use-expressions="true" >
<!-- Try getting the authorization object from the request parameters. -->
<security:custom-filter ref="filter1" after="SECURITY_CONTEXT_FILTER"/>
<security:custom-filter ref="filter2" before="LOGOUT_FILTER"/>
<!-- Intercept certain URLS differently -->
<intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')" />
<!-- Some more stuff here -->
<intercept-url pattern="/**" access="denyAll" />
<http-basic />
</http>
AppConfig của HandlerExceptionResolver
@Bean
public HandlerExceptionResolver handlerExceptionResolver(){
logger.info("creating handler exception resolver");
return new AllExceptionHandler();
}
HandlerExceptionResolver tùy chỉnh của chúng tôi
public class AllExceptionHandler implements HandlerExceptionResolver {
private static final Logger logger = LoggerFactory
.getLogger(AppConfig.class);
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) {
// This is just a snipped of the real method code
return new ModelAndView("errorPage");
}
Phần liên quan của một bộ lọc của chúng tôi:
try {
Authentication authResult = authenticationManger.authenticate(authRequest);
SecurityContextHolder.getContext().setAuthentication(authResult);
}
catch(AuthenticationException failed) {
SecurityContextHolder.clearContext();
throw failed;
}
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>xxx.xxx.xxx.config</param-value>
</context-param>
<context-param>
<param-name>spring.profiles.default</param-name>
<param-value>LIVE</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<!-- Add multipart support for files up to 10 MB -->
<multipart-config>
<max-file-size>10000000</max-file-size>
</multipart-config>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<filter>
<filter-name>openEntityManagerInViewFilter</filter-name>
<filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>openEntityManagerInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<!-- Map filters -->
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<error-page>
<error-code>404</error-code>
<location>/handle/404</location>
</error-page>
</web-app>
Có ai có bất kỳ gợi ý về cách chúng ta có thể giải quyết này? Tôi đã xem qua nhiều bài viết trên google, hầu hết trong số họ mô tả cách xử lý AccessDeniedException được ném bởi bảo mật mùa xuân khi không có bộ lọc nào có thể xác thực yêu cầu.
Chúng tôi đang sử dụng Spring Security 3.1.0 và web mùa xuân mvc 3.1.0.
Cảm ơn câu trả lời của bạn: Chỉ để đảm bảo rằng nếu tôi hiểu chính xác bạn. Bạn đề nghị sử dụng một cái gì đó như sau = "EXCEPTION_TRANSLATION_FILTER" cho bộ lọc của tôi. – David
Có. Bất kỳ vị trí nào sau 'EXCEPTION_TRANSLATION_FILTER' đều tốt. –
Điều này kết hợp với một xử lý tùy chỉnh sẽ làm các trick. Cảm ơn! – David