如何让Spring-Security以401格式返回401响应? [英] How can I get Spring-Security to return a 401 response as a JSON format?
问题描述
我有一个应用程序的ReST API,其中所有控制器都在 / api /
下找到,这些都返回带有 @ControllerAdvice的JSON响应
,它处理映射到JSON格式结果的所有异常。
I have a ReST API to an application with all controllers found under /api/
, these all return JSON responses with a @ControllerAdvice
which handles all exceptions to map to JSON formatted results.
从春季4.0开始,它的效果很好 @ControllerAdvice
现在支持在注释上匹配。我无法解决的是如何返回401的JSON结果 - 未经验证和400 - 错误请求响应。
This works great as of spring 4.0 @ControllerAdvice
now supports matching on annotations. What I can't work out is how to return a JSON result for a 401 - Unauthenticated and 400 - Bad Request responses.
而不是Spring只是将响应返回给容器(tomcat),将其呈现为HTML。我如何使用与我的 @ControllerAdvice
使用的技术相同的方法拦截此并呈现JSON结果。
Instead Spring is simply returning the response to the container (tomcat) which renders this as HTML. How can I intercept this and render a JSON result using the same technique that my @ControllerAdvice
is using.
security.xml
<bean id="xBasicAuthenticationEntryPoint"
class="com.example.security.XBasicAuthenticationEntryPoint">
<property name="realmName" value="com.example.unite"/>
</bean>
<security:http pattern="/api/**"
create-session="never"
use-expressions="true">
<security:http-basic entry-point-ref="xBasicAuthenticationEntryPoint"/>
<security:session-management />
<security:intercept-url pattern="/api/**" access="isAuthenticated()"/>
</security:http>
XBasicAuthenticationEntryPoint
public class XBasicAuthenticationEntryPoint extends BasicAuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException)
throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED,
authException.getMessage());
}
}
我可以使用<$ c $解决401 c> BasicAuthenticationEntryPoint 直接写入输出流,但我不确定这是最好的方法。
I can solve 401 by using the BasicAuthenticationEntryPoint
to write directly to the output stream, but I'm not sure it's the best approach.
public class XBasicAuthenticationEntryPoint extends BasicAuthenticationEntryPoint {
private final ObjectMapper om;
@Autowired
public XBasicAuthenticationEntryPoint(ObjectMapper om) {
this.om = om;
}
@Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException)
throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
om.writeValue(httpResponse.getOutputStream(),
new ApiError(request.getRequestURI(),
HttpStatus.SC_UNAUTHORIZED,
"You must sign in or provide basic authentication."));
}
}
我还没弄明白但是,为了处理400,我曾经尝试过捕获所有工作的控制器,但似乎有时它会与其他控制器产生奇怪的冲突行为,我不想重新访问。
I am yet to figure out how to handle 400 though, I once tried a catch all controller which did work, but it seemed that sometimes it would have odd conflicting behaviour with other controllers that I don't want to revisit.
我的 ControllerAdvice
实现有一个捕获所有如果spring抛出任何异常的错误请求(400)它理论上应该捕获它,但它不会:
My ControllerAdvice
implementation has a catch all which if spring throws any exception for bad request (400) it should in theory capture it, but it does not:
@ControllerAdvice(annotations = {RestController.class})
public class ApiControllerAdvisor {
@ExceptionHandler(Throwable.class)
public ResponseEntity<ApiError> exception(Throwable exception,
WebRequest request,
HttpServletRequest req) {
ApiError err = new ApiError(req.getRequestURI(), exception);
return new ResponseEntity<>(err, err.getStatus());
}
}
推荐答案
我实际上几周前我发现自己也在问同一个问题 - 正如Dirk在评论中指出的那样,@ ControllerAdvice只会在控制器方法中抛出异常时启动,所以不可避免地会抓住所有东西(我实际上是在尝试解决404错误的JSON响应的情况。)
I actually found myself asking the very same question a few weeks ago - As Dirk pointed out in the comments, @ControllerAdvice will only kick in if an exception is thrown from within a controller method, so will inevitably not catch all things (I was actually trying to solve the case for a JSON response for a 404 error).
我确定的解决方案,虽然不是很愉快(希望如果你得到更好的答案,我会改变我的方法是处理Web.xml中的错误映射 - 我添加了以下内容,它将覆盖具有特定URL映射的tomcat默认错误页面:
The solution I settled on, although not entirely pleasant (hopefully if you get a better answer I will change my approach) is handling the error mapping in the Web.xml - I added the following, which will override the tomcat default error pages with specific URL mappings:
<error-page>
<error-code>404</error-code>
<location>/errors/resourcenotfound</location>
</error-page>
<error-page>
<error-code>403</error-code>
<location>/errors/unauthorised</location>
</error-page>
<error-page>
<error-code>401</error-code>
<location>/errors/unauthorised</location>
</error-page>
现在,如果任何页面返回404,它将由我的错误控制器处理,如下所示:
Now, if any page returns a 404, it is handled by my error controller something like this:
@Controller
@RequestMapping("/errors")
public class ApplicationExceptionHandler {
@ResponseStatus(HttpStatus.NOT_FOUND)
@RequestMapping("resourcenotfound")
@ResponseBody
public JsonResponse resourceNotFound(HttpServletRequest request, Device device) throws Exception {
return new JsonResponse("ERROR", 404, "Resource Not Found");
}
@ResponseStatus(HttpStatus.UNAUTHORIZED)
@RequestMapping("unauthorised")
@ResponseBody
public JsonResponse unAuthorised(HttpServletRequest request, Device device) throws Exception {
return new JsonResponse("ERROR", 401, "Unauthorised Request");
}
}
这一切仍然感觉非常严峻 - 当然是全局处理错误 - 所以如果你不总是希望404响应成为json(如果你正在服务同一个应用程序的普通webapp)那么它就不能正常工作。但就像我说的那样,我决定采取行动,这里希望有一个更好的方式!
It all still feels pretty grim - and of course is a global handling of errors - so if you didn't always want a 404 response to be json (if you were serving a normal webapp of the same application) then it doesnt work so well. But like I said, its what I settled on to get moving, here's hoping there is a nicer way!
这篇关于如何让Spring-Security以401格式返回401响应?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!