如何在 Springdoc 中设置 header 的编码

问题描述 投票:0回答:1

我对 Swagger 自动生成的 Swagger 文档有疑问。问题是,尽管我已将项目设置为 UTF-8,但身份验证标头仍然以 ISO-8859-1 进行编码,这会导致密码中的特殊字符出现问题。我已经尝试了很多方法,但找不到任何有效的方法。

这是我的

application.yml
的相关部分:

server:
  servlet:
    encoding:
      charset: UTF-8
      enabled: true
      force: true

所讨论的端点如下所示:

    @PostMapping(value = Endpoints.USER_CHECK,
        produces = MediaType.APPLICATION_JSON_VALUE)
    @Operation(
        security = @SecurityRequirement(name = AuthGroups.BASIC_AUTH))
    )
    public UserCheckResponse postUserCheck(
        @AuthenticationPrincipal UserDetails userDetails
    ) {
        if (userDetails == null) {
            throw new HttpClientErrorException(HttpStatus.UNAUTHORIZED,"Login failed: Credentials not set");
        }
        return portalService.postUserCheck(userDetails.getUsername(), userDetails.getPassword());
    }

...安全方案定义为:

@SecurityScheme(
    name = AuthGroups.BASIC_AUTH,
    type = SecuritySchemeType.HTTP,
    scheme = "basic"
)

现在,我已经确认,如果我通过 Postman 调用端点,它工作得很好,但是当我使用自动生成的 Swagger 文档执行相同的请求时,授权标头会突然编码为 ISO-8859-1:

客户 输入验证 生成的身份验证标头
邮差 测试:Müll 授权:基本VGVzdDpNw7xsbA==
Springdoc Swagger 测试:Müll 授权:基本VGVzdDpN/Gxs

我需要做什么才能让 Springdoc Swagger 页面以 UTF-8 编码授权标头?


更新:

我还检查了生成的swagger页面,它的字符集设置为UTF-8,这让我更加困惑为什么标头采用ISO-8859-1编码:

<html lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
java spring swagger openapi springdoc
1个回答
0
投票

好吧,虽然我没有找到在自动生成的 Swagger 文档页面中设置标头编码的方法,但我did通过创建能够解码 UTF-8 和 ISO-8859 的自定义过滤器找到了解决方法 - 1 标题正确。整个解决方法如下所示:

MyUserDetails

import java.util.*;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import lombok.Getter;

@Getter
public class MyUserDetails implements UserDetails {
    private final String username;
    private final String password;

    public MyUserDetails (String username, String password) {
        this.username = username;
        this.password = password;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return List.of();
    }
}

MyAuthenticationManager

import org.springframework.security.authentication.*;
import org.springframework.security.core.*;
import org.springframework.security.core.userdetails.UserDetails;

public class MyAuthenticationManager implements AuthenticationManager {
    @Override
    public Authentication authenticate(
        Authentication authentication
    ) throws AuthenticationException {
        String username = (String) authentication.getPrincipal();
        String password = (String) authentication.getCredentials();
        UserDetails user = new MyUserDetails(username, password);
        return UsernamePasswordAuthenticationToken.authenticated(
            user,
            password,
            authentication.getAuthorities()
        );
    }
}

MyAuthenticationFilter

import java.nio.*;
import java.nio.charset.*;
import java.util.Base64;

import org.apache.commons.lang3.NotImplementedException;
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;

import jakarta.servlet.http.HttpServletRequest;

public class MyAuthenticationFilter extends
    AbstractPreAuthenticatedProcessingFilter {

    MyAuthenticationFilter(){
        setAuthenticationManager(new MyAuthenticationManager());
    }

    @Override
    protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
        String header = request.getHeader("Authorization");
        if (header != null && header.startsWith("Basic ")) {
            String decodedHeader = decodeBasicAuthHeader(header);
            String[] values = decodedHeader.split(":", 2);
            return values[0];
        }
        return null;
    }

    @Override
    protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
        String header = request.getHeader("Authorization");
        if (header != null && header.startsWith("Basic ")) {
            String decodedHeader = decodeBasicAuthHeader(header);
            String[] values = decodedHeader.split(":", 2);
            return values[1];
        }
        return null;
    }

    private String decodeBasicAuthHeader(String header) {
        String basicAuthHeader = header.substring("Basic".length()).trim();
        byte[] credDecoded = Base64.getDecoder()
            .decode(basicAuthHeader);
        try {
            return decode(credDecoded, StandardCharsets.UTF_8);
        } catch (CharacterCodingException e) {
            try {
                return decode(credDecoded, StandardCharsets.ISO_8859_1);
            } catch (CharacterCodingException ex) {
                throw new NotImplementedException(
                    "Basic auth header is not encoded in a supported charset"
                        + System.lineSeparator()
                        + "Supported Charsets are: UTF-8, ISO_8859_1");
            }
        }
    }

    private static String decode(byte[] bytes, Charset charset)
        throws CharacterCodingException {
        CharsetDecoder decoder = charset.newDecoder();
        decoder.onMalformedInput(CodingErrorAction.REPORT);
        decoder.onUnmappableCharacter(CodingErrorAction.REPORT);

        ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
        CharBuffer charBuffer = decoder.decode(byteBuffer);
        return charBuffer.toString();
    }
}

AuthConfiguration

import org.springframework.context.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.*;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

@Configuration
@EnableWebSecurity
@Order(1)
public class AuthConfiguration {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.addFilterBefore(new MyAuthenticationFilter(), BasicAuthenticationFilter.class)
            .authorizeHttpRequests(
                authorizeRequests -> authorizeRequests
                    .anyRequest().permitAll()
            )
            .httpBasic(Customizer.withDefaults());
        return http.build();
    }
}

虽然这有效,但我觉得它过于复杂。如果有人知道更好的解决方案,请分享!

© www.soinside.com 2019 - 2024. All rights reserved.