Reputation: 517
I've created Spring MVC application and set up Spring Security OAuth 2. While calling methods from my brower i get XML:
<oauth>
<error_description>
Full authentication is required to access this resource
</error_description>
<error>unauthorized</error>
</oauth>
Browser sends following header:
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
When I set json accept header I get JSON. I need to force my authorization server always send JSON. Haven't found any solution. Thanks.
Upvotes: 6
Views: 4545
Reputation: 11
To expand on BigDong's answer you dont need to create your own entrypoint and exception handler classes you just need to replace the renderer and set the type of message converters you want to use. For example in my Oauth2Config I do the following:
httpSecurity.authorizeRequests()
.requestMatchers(getAuthorizedRequestMatcher())
.authenticated()
...
.and()
.exceptionHandling().accessDeniedHandler(buildAccessDeniedHandler(configureExceptionRenderer()))
.and()
.exceptionHandling().authenticationEntryPoint(buildAuthenticationEntryPoint(configureExceptionRenderer()));
Where the render is created with the message converters I want:
protected OAuth2ExceptionRenderer configureExceptionRenderer() {
List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
messageConverters.add(new MappingJackson2HttpMessageConverter(Jackson2ObjectMapperBuilder.json().applicationContext(this.applicationContext).build()));
messageConverters.add(new ByteArrayHttpMessageConverter());
StringHttpMessageConverter stringConverter = new StringHttpMessageConverter();
stringConverter.setWriteAcceptCharset(false);
messageConverters.add(stringConverter);
... add more if you want ...
DefaultOAuth2ExceptionRenderer renderer = new DefaultOAuth2ExceptionRenderer();
renderer.setMessageConverters(messageConverters);
return renderer;
}
Finally instantiate the entry point and error handler and set the renderer:
protected AuthenticationEntryPoint buildAuthenticationEntryPoint(OAuth2ExceptionRenderer renderer) {
OAuth2AuthenticationEntryPoint entryPoint = new OAuth2AuthenticationEntryPoint();
entryPoint.setExceptionRenderer(renderer);
return entryPoint;
}
protected AccessDeniedHandler buildAccessDeniedHandler(OAuth2ExceptionRenderer renderer) {
OAuth2AccessDeniedHandler handler = new OAuth2AccessDeniedHandler();
handler.setExceptionRenderer(renderer);
return handler;
}
Upvotes: 1
Reputation: 14385
It's very easy to force OAuth2, you just need to figure it out by yourself first:
@Autowired
private AuthenticationEntryPoint authenticationEntryPoint;
@Autowired
private AccessDeniedHandler accessDeniedHandler;
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest()
.access("#oauth2.hasScope('read')")
.and()
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.accessDeniedHandler(accessDeniedHandler);
}
Then you will need to create your authenticationEntryPoint and accessDeniedHandler @Bean
@Bean
public AccessDeniedHandler accessDeniedHandler() {
return new AccessDeniedHandler () {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
response.getWriter().append("\"FORBIDDEN\"");
response.setStatus(HttpStatus.FORBIDDEN.value());
}
};
}
@Bean
public AuthenticationEntryPoint authenticationEntryPoint() {
return new AuthenticationEntryPoint() {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
response.getWriter().append("\"UNAUTHORIZED\"");
response.setStatus(HttpStatus.UNAUTHORIZED.value());
}
};
}
Feel free to convert in JSON the way you like, I would recommend you jackson.
Upvotes: 0
Reputation: 186
For Spring Security OAuth exceptions are rendered using the DefaultOAuth2ExceptionRenderer
It's going to match the received Accept HTTP header against the provided MessageConverters. In your case it seems that Spring Boot has automatically assigned XML and JSON MessageConverters. This behaviour is confirmed that based on the Accept header you are receiving the exception rendered in a proper Content-Type
Without the Accept header DefaultOAuth2ExceptionRenderer defaults to the Accept: * and the first MessageConverter usually to respond is XML one.
if XML is undesirable in your app you'll need to see why it's getting supported (most likely you have a FasterXML Jackson in you classpath).
If you want to support both but want to have JSON default that will require you to write your own impl of the OAuth2ExceptionRendrer and ensure that exceptions get rendered in JSON. Even better approach would be to hook your impl to the ContentNegotationManager and delegate MediaType resolution to it.
For more info on ContentNegotationManager check this link:
https://spring.io/blog/2013/05/11/content-negotiation-using-spring-mvc
Upvotes: 8