使用 OAuth2 从 Spring 安全自定义身份验证错误
Customize auth error from Spring Security using OAuth2
我想知道是否可以自定义以下授权错误:
{
"error": "unauthorized",
"error_description": "Full authentication is required to access this resource"
}
当用户请求没有权限时,我得到它。我想将其自定义为与 Spring 启动错误:
非常相似
{
"timestamp":1445441285803,
"status":401,
"error":"Unauthorized",
"message":"Bad credentials",
"path":"/oauth/token"
}
有可能吗?
非常感谢。
我认为您可以使用 @ControllerAdvice
捕获未经授权的异常,然后按照您的期望格式化响应并 return 它。像这样:
@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");
}
希望对您有所帮助。
我明白了:)
我需要创建一个新的 class 来实现 "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 项目,它实现了您需要的一切:
接受的答案对使用 Oauth2 的我不起作用。经过一些研究,exception translator solution 起作用了。
基本上,您需要创建一个 WebResponseExceptionTranslator
并将其注册为您的异常翻译器。
首先,创建一个WebResponseExceptionTranslator
bean:
@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 配置以注册 bean 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 规范。有关详细信息,请参阅 the RFC and OAuth2 API definitions。
您还可以自定义结果:覆盖 error
或 error_description
(只需调用 addAdditionalInformation
),用 instance of
到 return 识别特定异常] 不同的 json 结果等。但是也有限制:如果你想将某个字段定义为 integer
,我认为这是不可能的,因为 addAdditionalInformation
方法只接受String
作为类型。
短篇小说: https://github.com/melardev/JavaSpringBootOAuth2JwtCrudPagination.git
阅读@pakkk 的回复后我不同意,所以我决定尝试自己的想法,但也失败了,所以我决定看一下 Spring 安全源代码本身,发生了什么是这样的:
有一个很早就被调用的过滤器,OAuth2AuthenticationProcessingFilter。
此过滤器尝试从 header 中提取 JWT,如果抛出异常,它会调用
它的 authenticationEntryPoint.commence() (@pakk 就在这里)
我试图添加一个过滤器来检查它是否在 Jwt 无效或存在时被调用,但它没有,因此,添加自定义过滤器来更改响应将不起作用。
然后我查看了 OAuth2AuthenticationProcessingFilter 的配置位置,我发现它是在 ResourceServerSecurityConfigurer::configure(HttpSecurity http) 上设置的。
话虽如此,让我们看看如何连接到流程中。
事实证明这很容易,因为您将在资源服务器应用程序中扩展 ResourceServerConfigurerAdapter class:
@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 class.
更改入口点
这是我的入口点:注意我正在发送错误响应 object,这是我自己的 class,所以我可以完全控制响应:
@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());
}
}
我想知道是否可以自定义以下授权错误:
{
"error": "unauthorized",
"error_description": "Full authentication is required to access this resource"
}
当用户请求没有权限时,我得到它。我想将其自定义为与 Spring 启动错误:
非常相似{
"timestamp":1445441285803,
"status":401,
"error":"Unauthorized",
"message":"Bad credentials",
"path":"/oauth/token"
}
有可能吗?
非常感谢。
我认为您可以使用 @ControllerAdvice
捕获未经授权的异常,然后按照您的期望格式化响应并 return 它。像这样:
@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");
}
希望对您有所帮助。
我明白了:)
我需要创建一个新的 class 来实现 "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 项目,它实现了您需要的一切:
接受的答案对使用 Oauth2 的我不起作用。经过一些研究,exception translator solution 起作用了。
基本上,您需要创建一个 WebResponseExceptionTranslator
并将其注册为您的异常翻译器。
首先,创建一个WebResponseExceptionTranslator
bean:
@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 配置以注册 bean 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 规范。有关详细信息,请参阅 the RFC and OAuth2 API definitions。
您还可以自定义结果:覆盖 error
或 error_description
(只需调用 addAdditionalInformation
),用 instance of
到 return 识别特定异常] 不同的 json 结果等。但是也有限制:如果你想将某个字段定义为 integer
,我认为这是不可能的,因为 addAdditionalInformation
方法只接受String
作为类型。
短篇小说: https://github.com/melardev/JavaSpringBootOAuth2JwtCrudPagination.git
阅读@pakkk 的回复后我不同意,所以我决定尝试自己的想法,但也失败了,所以我决定看一下 Spring 安全源代码本身,发生了什么是这样的:
有一个很早就被调用的过滤器,OAuth2AuthenticationProcessingFilter。
此过滤器尝试从 header 中提取 JWT,如果抛出异常,它会调用
它的 authenticationEntryPoint.commence() (@pakk 就在这里)
我试图添加一个过滤器来检查它是否在 Jwt 无效或存在时被调用,但它没有,因此,添加自定义过滤器来更改响应将不起作用。
然后我查看了 OAuth2AuthenticationProcessingFilter 的配置位置,我发现它是在 ResourceServerSecurityConfigurer::configure(HttpSecurity http) 上设置的。
话虽如此,让我们看看如何连接到流程中。
事实证明这很容易,因为您将在资源服务器应用程序中扩展 ResourceServerConfigurerAdapter class:
@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 class.
更改入口点这是我的入口点:注意我正在发送错误响应 object,这是我自己的 class,所以我可以完全控制响应:
@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());
}
}