Http Authorization 实现认证登录

前言

以前经常看到一些开源软件的管理后台,都是基于浏览器弹框实现的认证登录;比如: haproxy管理后台,elastic-job管理后台等。在此梳理下其实现原理,进一步加深自己的理解。

Authorization

在http协议中请求头Authorization专门用于请求认证使用,用于透传凭证信息。Authorization有多种认证方式,最常见的一种就是Basic方式;通常开源软件管理后台也都是基于此方式实现的。

Basic方式具体形式:

Authorization: 认证方式 认证信息
示例:Authorization: Basic xxxxxx

其中xxxxxx是基于Base64编码的认证信息,具体格式为Base64.encode(“username:pwd”)

WWW-authenticate

WWW-authenticate 用于当用户认证失败后,告知用户浏览器的弹框提示来自何处。Http-Code=401 表示认证失败。

具体格式如下:

response.setHeader("WWW-authenticate", "Basic Realm=\"Elastic Job Console Auth\"");

Elastic-Job管理后台代码认证


public final class WwwAuthFilter implements Filter {

private static final String FILE_SEPARATOR = System.getProperty("file.separator");

private static final String AUTH_PREFIX = "Basic ";

private static final String ROOT_IDENTIFY = "root";

private static final String ROOT_DEFAULT_USERNAME = "root";

private static final String ROOT_DEFAULT_PASSWORD = "root";

private static final String GUEST_IDENTIFY = "guest";

private static final String GUEST_DEFAULT_USERNAME = "guest";

private static final String GUEST_DEFAULT_PASSWORD = "guest";


private String rootUsername;

private String rootPassword;

private String guestUsername;

private String guestPassword;

@Override
public void init(final FilterConfig filterConfig) throws ServletException {
Properties props = new Properties();
URL classLoaderURL = Thread.currentThread().getContextClassLoader().getResource("");
if (null != classLoaderURL) {
String configFilePath = Joiner.on(FILE_SEPARATOR).join(classLoaderURL.getPath(), "conf", "auth.properties");
try {
props.load(new FileInputStream(configFilePath));
} catch (final IOException ex) {
log.warn("Cannot found auth config file, use default auth config.");
}
}
rootUsername = props.getProperty("root.username", ROOT_DEFAULT_USERNAME);
rootPassword = props.getProperty("root.password", ROOT_DEFAULT_PASSWORD);
guestUsername = props.getProperty("guest.username", GUEST_DEFAULT_USERNAME);
guestPassword = props.getProperty("guest.password", GUEST_DEFAULT_PASSWORD);
}

@Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String authorization = httpRequest.getHeader("authorization");
// 认证成功后,浏览器每次请求都会携带认证信息
if (null != authorization && authorization.length() > AUTH_PREFIX.length()) {
authorization = authorization.substring(AUTH_PREFIX.length(), authorization.length());
if ((rootUsername + ":" + rootPassword).equals(new String(Base64.decodeBase64(authorization)))) {
authenticateSuccess(httpResponse, false);
chain.doFilter(httpRequest, httpResponse);
} else if ((guestUsername + ":" + guestPassword).equals(new String(Base64.decodeBase64(authorization)))) {
authenticateSuccess(httpResponse, true);
chain.doFilter(httpRequest, httpResponse);
} else {
needAuthenticate(httpResponse);
}
} else {
needAuthenticate(httpResponse);
}
}

private void authenticateSuccess(final HttpServletResponse response, final boolean isGuest) {
// 认证成功
response.setStatus(200);
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "no-store");
response.setDateHeader("Expires", 0);
response.setHeader("identify", isGuest ? GUEST_IDENTIFY : ROOT_IDENTIFY);
}

private void needAuthenticate(final HttpServletResponse response) {
// 告诉浏览器弹框提示认证失败
response.setStatus(401);
response.setHeader("Cache-Control", "no-store");
response.setDateHeader("Expires", 0);
response.setHeader("WWW-authenticate", AUTH_PREFIX + "Realm=\"Elastic Job Console Auth\"");
}

@Override
public void destroy() {
}
}

总结

基于Http协议的认证方式,在http方式访问下是非常不安全的,毕竟只是采用了Base64编码处理,在公网环境下传输,无疑是裸奔。在https环境下传输还可以。Http协议认证方式一般用于一些仅供内网可以访问的后台系统,例如:haproxy管理后台,elastic-job管理后台等。