【问题标题】:How to force Spring Security OAuth 2 to use JSON instead of XML?如何强制 Spring Security OAuth 2 使用 JSON 而不是 XML?
【发布时间】:2016-02-29 00:38:47
【问题描述】:

我已经创建了 Spring MVC 应用程序并设置了 Spring Security OAuth 2。 从我的浏览器调用方法时,我得到了 XML:

<oauth>
    <error_description>
        Full authentication is required to access this resource
    </error_description>
    <error>unauthorized</error>
</oauth>

浏览器发送以下标头:

Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8

当我设置 json 接受标头时,我得到 JSON。我需要强制我的授权服务器始终发送 JSON。没有找到任何解决方案。谢谢。

【问题讨论】:

    标签: java spring spring-mvc spring-security spring-security-oauth2


    【解决方案1】:

    对于 Spring Security,OAuth 异常使用 DefaultOAuth2ExceptionRenderer 呈现

    它会将接收到的 Accept HTTP 标头与提供的 MessageConverters 进行匹配。在您的情况下,Spring Boot 似乎已自动分配 XML 和 JSON MessageConverters。此行为已确认,基于 Accept 标头,您正在接收以适当的 Content-Type 呈现的异常

    没有 Accept 标头 DefaultOAuth2ExceptionRenderer 默认为 Accept: * 并且通常响应的第一个 MessageConverter 是 XML。

    如果 XML 在您的应用中不受欢迎,您需要了解为什么它会得到支持(很可能您的类路径中有 FasterXML Jackson)。

    如果您想同时支持这两种方法,但又想使用 JSON 默认值,则需要您编写自己的 OAuth2ExceptionRendrer 实现,并确保异常以 JSON 形式呈现。更好的方法是将您的 impl 连接到 ContentNegotationManager 并将 MediaType 解析委托给它。

    有关 ContentNegotationManager 的更多信息,请查看此链接:

    https://spring.io/blog/2013/05/11/content-negotiation-using-spring-mvc

    【讨论】:

      【解决方案2】:

      设置Accept: application/json为头部属性

      【讨论】:

        【解决方案3】:

        要扩展 BigDong 的答案,您不需要创建自己的入口点和异常处理程序类,您只需要替换渲染器并设置要使用的消息转换器的类型。例如,在我的 Oauth2Config 中,我执行以下操作:

            httpSecurity.authorizeRequests()
               .requestMatchers(getAuthorizedRequestMatcher())
               .authenticated()
               ...
               .and()
               .exceptionHandling().accessDeniedHandler(buildAccessDeniedHandler(configureExceptionRenderer()))
               .and()
               .exceptionHandling().authenticationEntryPoint(buildAuthenticationEntryPoint(configureExceptionRenderer()));
        

        使用我想要的消息转换器创建渲染的位置:

            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;
            }
        

        最后实例化入口点和错误处理程序并设置渲染器:

            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;
            }
        

        【讨论】:

          【解决方案4】:

          强制OAuth2很简单,你自己先想办法:

          @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);
          }
          

          然后你需要创建你的 authenticationEntryPoint 和 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());
                  }
              };
          }
          

          随意用你喜欢的方式转换成 JSON,我会推荐你​​ jackson。

          【讨论】:

            猜你喜欢
            • 2013-09-24
            • 2019-02-18
            • 2014-09-15
            • 2015-04-22
            • 1970-01-01
            • 2010-11-14
            • 1970-01-01
            • 1970-01-01
            • 2021-06-24
            相关资源
            最近更新 更多