当Authorization标头具有所有大写字母的basic时,Wildfly 26.0.1无法通过基本身份验证



我们正在将平台升级到Wildfly 26.0.1我们注意到,当HTTP请求进入一个受身份验证保护的基本端点时;授权";标头的值以单词BASIC开头,所有大写字母都有,然后请求立即导致HTTP 401响应,尽管在base64编码的第二部分中登录和密码正确。如果值以"0"开头;基本的";(大写的"B",其余为小写(。

这个问题与几年前打开并修复的这个错误非常相似(为WF9.0.2打开,在10.1.0中修复(

我们还确认,这个问题存在于Wildfly的早期版本中,我们最早能够复制它的是WF18,我们也确信10.1.0没有它,因为这是我们试图迁移的版本。

有没有人遇到过这个问题,知道如何调整配置/以其他方式解决它?要求客户使用";基本的";而不是";BASIC";不是一个选项。

$ curl -vH "Authorization: BASIC ZnJhbms6cGFzc3dvcmQxMjM=" http://localhost:8080/http-basic/secure
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /http-basic/secure HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.54.0
> Accept: */*
> Authorization: BASIC ZnJhbms6cGFzc3dvcmQxMjM=
>
< HTTP/1.1 401 Unauthorized
< Expires: 0
< Connection: keep-alive
< WWW-Authenticate: Basic realm="RealmUsersRoles"
< Cache-Control: no-cache, no-store, must-revalidate
< Pragma: no-cache
< Content-Type: text/html;charset=UTF-8
< Content-Length: 71
< Date: Thu, 10 Mar 2022 15:38:55 GMT
<
* Connection #0 to host localhost left intact
<html><head><title>Error</title></head><body>Unauthorized</body></html>mark_mac:enstream-identity-warTrunk mwei$
$ curl -vH "Authorization: Basic ZnJhbms6cGFzc3dvcmQxMjM=" http://localhost:8080/http-basic/secure
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /http-basic/secure HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.54.0
> Accept: */*
> Authorization: Basic ZnJhbms6cGFzc3dvcmQxMjM=
>
< HTTP/1.1 200 OK
< Expires: 0
< Connection: keep-alive
< Cache-Control: no-cache, no-store, must-revalidate
< Pragma: no-cache
< Content-Length: 206
< Date: Thu, 10 Mar 2022 15:39:11 GMT
<
<html><head><title>servlet-security</title></head><body>
<h1>Successfully called Secured Servlet </h1>
<p>Principal : frank</p>
<p>Remote User : frank</p>
<p>Authentication Type : BASIC</p>
</body></html>
* Connection #0 to host localhost left intact

谢谢Mike-我错过了也是不区分大小写的(HTTP键一直是不区分大小字母的(。以下是我用于自己的JaxRS服务的内容。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Priority;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.NotAuthorizedException;
import javax.ws.rs.Priorities;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.PreMatching;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.Provider;
import java.nio.charset.StandardCharsets;
import java.security.Principal;
import java.util.Base64;

@Provider
@Priority(Priorities.AUTHENTICATION)
@PreMatching
public class AuthContainerRequestFilter implements ContainerRequestFilter {
private static final Logger logger = LoggerFactory.getLogger(AuthContainerRequestFilter.class);
private static final Boolean authEnabled = Boolean.TRUE;
@Context
private HttpServletResponse httpServletResponse;

@Override
public void filter(ContainerRequestContext containerRequestContext)  {
SamplePrincipal samplePrincipal;
if( authEnabled ) {   
String authToken = containerRequestContext.getHeaderString("Authorization");
if (authToken == null) {
logger.warn("this request has no Authorization header or parameter");
httpServletResponse.setHeader("WWW-Authenticate", "Basic realm="Sample Service", charset="UTF-8"");
throw new NotAuthorizedException(Response.status(Response.Status.UNAUTHORIZED)
.entity("no authorization header")
.type(MediaType.TEXT_PLAIN_TYPE)
.build());
}
if (!authToken.toLowerCase().startsWith("basic")) {
httpServletResponse.setHeader("WWW-Authenticate", "Basic realm="Sample Service", charset="UTF-8"");
throw new NotAuthorizedException(Response.status(Response.Status.UNAUTHORIZED)
.entity("expected Basic auth for Authorization header")
.type(MediaType.TEXT_PLAIN_TYPE)
.build());
}
String[] basicAuthHeaderParts = authToken.split(" ");
if (basicAuthHeaderParts.length != 2) {
httpServletResponse.setHeader("WWW-Authenticate", "Basic realm="Sample Service", charset="UTF-8"");
throw new BadRequestException(Response.status(Response.Status.BAD_REQUEST)
.entity("malformed Basic auth for Authorization header - missing auth value")
.type(MediaType.TEXT_PLAIN_TYPE)
.build());
}
String userAndPassword = new String(Base64.getDecoder().decode(basicAuthHeaderParts[1]), StandardCharsets.UTF_8);
String[] userAndPasswordValues = userAndPassword.split(":");
if (userAndPasswordValues.length != 2) {
httpServletResponse.setHeader("WWW-Authenticate", "Basic realm="Sample Service", charset="UTF-8"");
throw new BadRequestException(Response.status(Response.Status.BAD_REQUEST)
.entity("malformed Basic auth for Authorization header - can't split user/password")
.type(MediaType.TEXT_PLAIN_TYPE)
.build());
}

// validate user/password combo
// fill the principal with the appropriate values.
// this allows for the @RolesAllowed annotation too.
samplePrincipal = new SamplePrincipal(userAndPasswordValues[0], "Some Name");
samplePrincipal.addRole("normal");
}
else {
samplePrincipal = new SamplePrincipal("blah@blah.com", "Some Name");
}
containerRequestContext.setSecurityContext(new SecurityContext() {
@Override
public Principal getUserPrincipal() {
return samplePrincipal;
}
@Override
public boolean isUserInRole(String role) {
return samplePrincipal.isUserInRole(role);
}
@Override
public boolean isSecure() {
return containerRequestContext.getUriInfo().getAbsolutePath().toString().startsWith("https");
}
@Override
public String getAuthenticationScheme() {
return SecurityContext.BASIC_AUTH;
}
});
}
}

与SamplePrincipal:

import java.security.Principal;
import java.util.ArrayList;
import java.util.List;

public class SamplePrincipal implements Principal {
private String userName;
private String name;
private List<String> roles = null;

public SamplePrincipal(String userName, String name) {
this.userName = userName;
this.name = name;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void addRole(String role) {
if( roles == null)
roles = new ArrayList<>();
roles.add(role);
}
public void setRoles(List<String> roles) {
this.roles = new ArrayList<>(roles);
}
public boolean isUserInRole(String role) {
if( roles == null )
return false;
return roles.contains(role);
}
}

作为代码的一部分,我在服务上使用@RolesAllowed注释来验证权限。最后,我对用户/密码/卷信息进行了DB查询。

我确实意识到我正在重新编写已经存在的代码,但它简化了一个应用程序,该应用程序的现有用户数据库比默认的Wildfly配置允许我处理的更复杂。在您的情况下,实际上只有一行需要存在,其中标头与字符串"进行比较;基本";。我不确定这是否值得,但当这个bug回到Undertow时,我想你会做好准备的。

最新更新