Мне было интересно, если бы я мог настроить следующую ошибку авторизации:
{
"error": "unauthorized",
"error_description": "Full authentication is required to access this resource"
}
Я получаю, когда пользовательский запрос не имеет разрешений. И я хотел бы настроить его так, чтобы он был похож на ошибку Spring Boot:
{
"timestamp":1445441285803,
"status":401,
"error":"Unauthorized",
"message":"Bad credentials",
"path":"/oauth/token"
}
Возможно ли это?
Благодарю.
4 ответа
Я понял :)
https://stackoverflow.com/a/37132751/2520689
Мне нужно создать новый класс, который реализует «AuthenticationEntryPoint» следующим образом:
public class AuthExceptionEntryPoint implements AuthenticationEntryPoint
{
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException arg2) throws IOException, ServletException
{
final Map<String, Object> mapBodyException = new HashMap<>() ;
mapBodyException.put("error" , "Error from AuthenticationEntryPoint") ;
mapBodyException.put("message" , "Message from AuthenticationEntryPoint") ;
mapBodyException.put("exception", "My stack trace exception") ;
mapBodyException.put("path" , request.getServletPath()) ;
mapBodyException.put("timestamp", (new Date()).getTime()) ;
response.setContentType("application/json") ;
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED) ;
final ObjectMapper mapper = new ObjectMapper() ;
mapper.writeValue(response.getOutputStream(), mapBodyException) ;
}
}
И добавьте его в мою реализацию ResourceServerConfigurerAdapter:
@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter
{
@Override
public void configure(HttpSecurity http) throws Exception
{
http.exceptionHandling().authenticationEntryPoint(new AuthExceptionEntryPoint()) ;
}
}
Вы можете найти мой проект GitHub, который реализует все, что вам нужно:
https://github.com/pakkk/custom-spring-security
Я думаю, что вы можете использовать @ControllerAdvice
, чтобы перехватить исключение неавторизованным, а затем отформатировать ответ как ожидаемое и вернуть его. Что-то вроде этого:
@ResponseBody
@ExceptionHandler(CustomException.class)
@ResponseStatus(value=HttpStatus.UNAUTHORIZED, reason="Exception message")
public JsonResponse unAuthorised(HttpServletRequest request, Exception ex) {
return new JsonResponse("ERROR", 401, "Unauthorised Request");
}
Надеюсь, это поможет.
@Configuration
@EnableResourceServer
public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {
// ....
}
Вы идете вперед и переопределяете:
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
super.configure(resources);
}
Как видите, да! у вас есть доступ к ResourceServerSecurityConfigurer, и что теперь? ну давайте заменим точку входа по умолчанию на нашу:
@Autowired
private AuthenticationEntryPoint oauthEntryPoint;
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
super.configure(resources);
resources.authenticationEntryPoint(oauthEntryPoint);
}
Для полного исходного кода с примером посмотрите на: https://github.com/melardev/JavaSpringBootOAuth2JwtCrudPagination.git
Без этих шагов, по крайней мере, для меня это не сработало бы, ответ, предоставленный @pakkk, не работает для меня, я проверил на отладчике, и по умолчанию используемая точка входа не наша, даже используя:
http.and().exceptionHandling().authenticationEntryPoint(oauthEntryPoint)
Это было первое, что я протестировал, чтобы заставить его работать, нужно изменить точку входа непосредственно из класса ResourceServerSecurityConfigurer.
И это моя точка входа: обратите внимание, я отправляю объект ErrorResponse, который является моим собственным классом, поэтому у меня есть полный контроль над ответом:
@Component
public class OAuthEntryPoint implements AuthenticationEntryPoint {
@Autowired
ObjectMapper mapper;
@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
ServletServerHttpResponse res = new ServletServerHttpResponse(httpServletResponse);
res.setStatusCode(HttpStatus.FORBIDDEN);
res.getServletResponse().setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
res.getBody().write(mapper.writeValueAsString(new ErrorResponse("You must authenticated")).getBytes());
}
}
Принятый ответ не работает для меня с использованием Oauth2. После некоторого исследования переводчик исключений работает.
По сути, вам нужно создать WebResponseExceptionTranslator
и зарегистрировать его в качестве транслятора исключений.
Сначала создайте бин WebResponseExceptionTranslator
:
@Slf4j
@Configuration
public class Oauth2ExceptionTranslatorConfiguration {
@Bean
public WebResponseExceptionTranslator oauth2ResponseExceptionTranslator() {
return new DefaultWebResponseExceptionTranslator() {
@Override
public ResponseEntity<OAuth2Exception> translate(Exception e) throws Exception {
ResponseEntity<OAuth2Exception> responseEntity = super.translate(e);
OAuth2Exception body = responseEntity.getBody();
HttpStatus statusCode = responseEntity.getStatusCode();
body.addAdditionalInformation("timestamp", dateTimeFormat.format(clock.instant()))
body.addAdditionalInformation("status", body.getHttpErrorCode().toString())
body.addAdditionalInformation("message", body.getMessage())
body.addAdditionalInformation("code", body.getOAuth2ErrorCode().toUpperCase())
HttpHeaders headers = new HttpHeaders();
headers.setAll(responseEntity.getHeaders().toSingleValueMap());
// do something with header or response
return new ResponseEntity<>(body, headers, statusCode);
}
};
}
}
Теперь вам нужно изменить конфигурацию Oauth2, чтобы зарегистрировать компонент WebResponseExceptionTranslator
:
@Slf4j
@Configuration
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private ClientDetailsServiceBuilder builder;
@Autowired
private WebResponseExceptionTranslator oauth2ResponseExceptionTranslator;
@Autowired
private UserDetailsService userDetailsService;
@Override
public void configure(ClientDetailsServiceConfigurer clients) {
clients.setBuilder(builder);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(
Arrays.asList(tokenEnhancer(), accessTokenConverter()));
endpoints.tokenStore(tokenStore())
.tokenEnhancer(tokenEnhancerChain)
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService)
.exceptionTranslator(oauth2ResponseExceptionTranslator);
}
}
Окончательный результат будет:
{
"error": "unauthorized",
"error_description": "Full authentication is required to access this resource",
"code": "UNAUTHORIZED",
"message": "Full authentication is required to access this resource",
"status": "401",
"timestamp": "2018-06-28T23:55:28.86Z"
}
Как видите, я не удаляю error
и error_description
из исходного тела OAuth2Exception. Я рекомендую сохранить их, потому что эти два поля соответствуют спецификации OAuth2. См. RFC и Определения API OAuth2 для получения дополнительных сведений.
Вы также можете настроить результат: переопределить error
или error_description
(просто вызывая addAdditionalInformation
), идентифицировать конкретное исключение с помощью instance of
для возврата другого результата json и т. Д. Но Есть и ограничения: если вы хотите определить какое-то поле как integer
, я не думаю, что это возможно, потому что метод addAdditionalInformation
принимает только String
в качестве типа.
Связанные вопросы
Новые вопросы
java
Java - это язык программирования высокого уровня. Используйте этот тег, если у вас возникли проблемы с использованием или пониманием самого языка. Этот тег редко используется отдельно и чаще всего используется вместе с [spring], [spring-boot], [jakarta-ee], [android], [javafx], [hadoop], [gradle] и [maven].