该API可以生成 JSON和HTML.
做一个很好的错误处理弹簧的安全性让我头痛:
认证可以以各种方式发生:BasicAuth,通过POST请求中的不同参数以及通过Web登录.
对于每个身份验证机制,在< http> spring security xml配置的命名空间元素.
我们在自定义HandlerExceptionResolver中处理我们所有的spring异常.这对我们的控制器中抛出的所有异常都很好,但是我不知道如何处理自定义弹簧安全过滤器中抛出的自定义异常.
由于弹簧安全过滤器在我们的任何控制器被调用之前都没有看到我们在自定义弹簧安全过滤器中抛出异常.
我在stackoverflow上发现了这个问题:
Use custom exceptions in Spring Security.但是我不明白他们在哪里处理抛出的异常.
我们尝试了这种方法,但是我们的自定义HandlerExceptionResolver不被调用.相反,用户被呈现由tomcat呈现的丑陋的堆栈跟踪.
我们为什么需要这个?
可以激活和停用用户.如果它们被禁用并尝试执行某些操作,我们希望使用自定义错误消息返回JSON.这应该不同于当spring安全性引发AccessDeniedException时显示的内容. AccessDeniedException以某种方式使其到我们的HandlerExceptionResolver,但我无法遵循如何确切.
可能的解决方案
我们考虑使用ExceptionTranslationFilter,但是当我们抛出我们的自定义异常(在doFilter()方法的catch语句中设置一个断点)时,这并不会被调用).在我的理解中,应该调用这个catch块,并且应该使用认证入口点.
另一种可能性:我们可以在spring安全性过滤器链中执行类似于ExceptionTranslationFilter的操作,并且执行类似于AccessDeniedHandler的操作:
RequestDispatcher dispatcher = request.getRequestDispatcher(errorPage); dispatcher.forward(request,response);
我们可以在请求中添加一些参数(错误代码,原因等),并将其指向将处理JSON或HTML中的渲染的控制器.
这是我们的配置的一个简短的摘录:
春天安全:
<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>
HandlerExceptionResolver的AppConfig
@Bean public HandlerExceptionResolver handlerExceptionResolver(){ logger.info("creating handler exception resolver"); return new AllExceptionHandler(); }
我们的自定义HandlerExceptionResolver
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"); }
我们的一个过滤器的相关部分:
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>
有人有任何指示我们如何解决这个问题?
我浏览了很多关于google的文章,大部分文章描述了当没有过滤器能够验证请求时,如何处理spring安全性抛出的AccessDeniedException.
我们使用Spring Security 3.1.0和spring web mvc 3.1.0.
解决方法
The
ExceptionTranslationFilter
will be able to handle and react to
only those exceptions that are thrown below it in the filter chain
execution stack. Users often get confused,especially when adding
custom filters in the incorrect order,as to why the expected behavior
differs from their application’s actual exception handling—in many of
these cases,the order of the filters is to blame!
如果您的过滤器是关于授权的,那么将默认授权过滤器使用的方法作为链条的末尾是一个很好的做法.这样你就不用重新发明了.
标准过滤器:Table in documentation
您正确配置过滤器链后,您可以配置错误页面,甚至自定义处理程序.更多信息,请参阅documentation.