当前位置: 首页 > 知识库问答 >
问题:

Spring Security Oauth-OAuth2Exceptions的自定义格式

沈永贞
2023-03-14

spring security oauth的错误格式符合oauth规范,如下所示。

{
  "error":"insufficient_scope",
  "error_description":"Insufficient scope for this resource",
  "scope":"do.something"
}

特别是在资源服务器上,我发现为身份验证问题提供不同的错误格式有点奇怪。所以我想改变这个异常的呈现方式。

授权服务器中的错误处理使用标准的Spring MVC特性,即@ExceptionHandler方法

所以我尝试了类似这样的方法来自定义错误的格式:

@ControllerAdvice
@Order(Ordered.HIGHEST_PRECEDENCE)
public class MyErrorHandler {

    @ExceptionHandler(value = {InsufficientScopeException.class})
    ResponseEntity<MyErrorRepresentation> handle(RuntimeException ex, HttpServletRequest request) {
        return errorResponse(HttpStatus.FORBIDDEN,
                MyErrorRepresentation.builder()
                        .errorId("insufficient.scope")
                        .build(),
                request);
    }
}

但这不起作用。

查看代码,所有的错误呈现似乎都是在DefaultWebResponseExceptionTranslator#HandleOauth2Exception中完成的。但实现自定义WebResponseExceptionTranslator将不允许更改格式。

有什么提示吗?

共有1个答案

童宏富
2023-03-14

首先是Spring SecurityOAuth2的一些知识。

  1. OAuth2有两个主要部分

AuthorizationServer:/oauth/token,获取令牌

这是配置

@Configuration
@EnableAuthorizationServer
public class OAuthSecurityConfig extends AuthorizationServerConfigurerAdapter {
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        //for custom
        endpoints.exceptionTranslator(new MyWebResponseExceptionTranslator());
    }
}

@Configuration
@EnableResourceServer
public  class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
        @Override
        public void configure(ResourceServerSecurityConfigurer resources) {
            // format message
            resources.authenticationEntryPoint(new MyAuthenticationEntryPoint());
            resources.accessDeniedHandler(new MyAccessDeniedHandler());
        }
}

MyWebResponseExceptionTranslator将异常转换为ourOAuthException,我们由jackson自定义ourOAuthException序列化器,默认情况下,这种方式与OAuth2使用的方式相同。

@JsonSerialize(using = OAuth2ExceptionJackson1Serializer.class)
public class OAuth2Exception extends RuntimeException {

其他自定义句柄类

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;

/**
 * @author qianggetaba
 * @date 2019/6/21
 */
public class MyWebResponseExceptionTranslator implements WebResponseExceptionTranslator {
    @Override
    public ResponseEntity<OAuth2Exception> translate(Exception exception) throws Exception {
        if (exception instanceof OAuth2Exception) {
            OAuth2Exception oAuth2Exception = (OAuth2Exception) exception;
            return ResponseEntity
                    .status(oAuth2Exception.getHttpErrorCode())
                    .body(new CustomOauthException(oAuth2Exception.getMessage()));
        }else if(exception instanceof AuthenticationException){
            AuthenticationException authenticationException = (AuthenticationException) exception;
            return ResponseEntity
                    .status(HttpStatus.UNAUTHORIZED)
                    .body(new CustomOauthException(authenticationException.getMessage()));
        }
        return ResponseEntity
                .status(HttpStatus.OK)
                .body(new CustomOauthException(exception.getMessage()));
    }
}
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;

/**
 * @author qianggetaba
 * @date 2019/6/21
 */
@JsonSerialize(using = CustomOauthExceptionSerializer.class)
public class CustomOauthException extends OAuth2Exception {
    public CustomOauthException(String msg) {
        super(msg);
    }
}
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;

import java.io.IOException;
import java.util.Arrays;
import java.util.Map;

/**
 * @author qianggetaba
 * @date 2019/6/21
 */
public class CustomOauthExceptionSerializer extends StdSerializer<CustomOauthException> {

    public CustomOauthExceptionSerializer() {
        super(CustomOauthException.class);
    }

    @Override
    public void serialize(CustomOauthException value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        jsonGenerator.writeStartObject();
        jsonGenerator.writeNumberField("code4444", value.getHttpErrorCode());
        jsonGenerator.writeBooleanField("status", false);
        jsonGenerator.writeObjectField("data", null);
        jsonGenerator.writeObjectField("errors", Arrays.asList(value.getOAuth2ErrorCode(),value.getMessage()));
        if (value.getAdditionalInformation()!=null) {
            for (Map.Entry<String, String> entry : value.getAdditionalInformation().entrySet()) {
                String key = entry.getKey();
                String add = entry.getValue();
                jsonGenerator.writeStringField(key, add);
            }
        }
        jsonGenerator.writeEndObject();
    }
}

对于自定义ResourceServer异常

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author qianggetaba
 * @date 2019/6/21
 */
public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
                         AuthenticationException authException)
            throws ServletException {

        Map map = new HashMap();
        map.put("errorentry", "401");
        map.put("message", authException.getMessage());
        map.put("path", request.getServletPath());
        map.put("timestamp", String.valueOf(new Date().getTime()));
        response.setContentType("application/json");
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        try {
            ObjectMapper mapper = new ObjectMapper();
            mapper.writeValue(response.getOutputStream(), map);
        } catch (Exception e) {
            throw new ServletException();
        }
    }

}
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author qianggetaba
 * @date 2019/6/21
 */
public class MyAccessDeniedHandler implements AccessDeniedHandler{
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        response.setContentType("application/json;charset=UTF-8");
        Map map = new HashMap();
        map.put("errorauth", "400");
        map.put("message", accessDeniedException.getMessage());
        map.put("path", request.getServletPath());
        map.put("timestamp", String.valueOf(new Date().getTime()));
        response.setContentType("application/json");
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        try {
            ObjectMapper mapper = new ObjectMapper();
            mapper.writeValue(response.getOutputStream(), map);
        } catch (Exception e) {
            throw new ServletException();
        }
    }
}
 类似资料:
  • 是否可以使用用户扩展在Selenium IDE中创建自定义命令.js并使其能够将自身更改为自定义导出格式,如果是这样,如何? 我将使用SeleniumIDE记录测试,这些测试将以完全自定义的Java格式导出,作为全自动测试系统的一部分运行。格式不是JUnit,而是每个测试都是它自己的类,并根据整个系统的输入动态加载。 我想要做的是在Selenium IDE中创建一个名为“take snapshot

  • 我想通过LLCP在一个NDEF记录中的NDEF消息中传输一个自定义数据(或者有效载荷可能会被分块到几个NDEF中)。传输的内容是具有特定格式的文件,仅在特定应用中具有意义。那么,指定NDEF头的最佳方法是什么? 1) 将TNF设置为0x04(NFC论坛外部类型),0x03(绝对URI)或0x05(未知)?0x04将在 TYPE 字段中具有自定义相对 URI,如果是绝对 URI,则0x03绝对 UR

  • 本文向大家介绍.NET Framework 格式:自定义DateTime格式,包括了.NET Framework 格式:自定义DateTime格式的使用技巧和注意事项,需要的朋友参考一下 示例            

  • 本文向大家介绍jquery自定义表格样式,包括了jquery自定义表格样式的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了jquery自定义表格样式实现代码。分享给大家供大家参考。具体如下: 运行效果截图如下: 上面这张图有3种状态,默认状态(灰白相间),鼠标悬浮状态(绿色),鼠标点击状态(黄色),是如何实现的呐? Html代码如下: 插件实现代码如下: 有些时候我们可能并不需要鼠标点击后

  • 我正在使用poi版本:3.14 我访问Excel(.xlsx)文件 下面是应用于单元格的.xlsx文件中的自定义格式。我想在代码中显示单元格值。 null > cell.getStringCellValue()=>“42153283700100” Cell.GetRichStringCellValue()=>“42153283700100” 带有formatCellValue(cell)方法的旧H

  • 有没有办法在Intellij中创建自定义的代码格式顺序?更具体地说,当一起按Option+Command+L时,我试图更改默认格式,并按类型格式化导入。