Play 框架 2.4 的 HTTP 基本身份验证
HTTP Basic Authentication for Play framework 2.4
我正在寻找一些方法来为我的游戏框架应用程序进行一些身份验证:我想要 allow/disallow 对未经过身份验证的用户的全部访问权限
是否存在一些可用的 module/solution?我不需要任何形式的身份验证,只需要 401 HTTP 响应用于非身份验证用户(如 Apache .htacccess "AuthType Basic" 模式)。
恐怕没有这样的解决方案,原因很简单:通常当开发人员需要添加 authorization/authentication 堆栈时,他们会构建 full 解决方案。
最简单和最快的方法是使用 HTTP front-end server as a reverse-proxy for your application (I'd choose nginx 完成该任务,但如果您的机器上有 运行 Apache,也可以使用它)。它将允许您 filter/authenticate 使用通用服务器规则的流量
此外,它还为您带来其他好处,即:您可以创建类似 CDN 的路径,这样您就不会浪费应用程序的资源来提供 public 静态资产。您可以使用负载均衡器重新部署您的应用程序,而无需将其完全停止 x 分钟等。
您可以试试这个过滤器:
https://github.com/Kaliber/play-basic-authentication-filter
使用和配置看起来非常简单。
你也可以用 play.mvc.Action 来解决这个问题,就像这样。
首先你的行动:
import org.apache.commons.codec.binary.Base64;
import play.libs.F;
import play.libs.F.Promise;
import play.mvc.Action;
import play.mvc.Http.Context;
import play.mvc.Result;
import util.ADUtil;
public class BasicAuthAction extends Action<Result> {
private static final String AUTHORIZATION = "authorization";
private static final String WWW_AUTHENTICATE = "WWW-Authenticate";
private static final String REALM = "Basic realm=\"yourRealm\"";
@Override
public Promise<Result> call(Context context) throws Throwable {
String authHeader = context.request().getHeader(AUTHORIZATION);
if (authHeader == null) {
context.response().setHeader(WWW_AUTHENTICATE, REALM);
return F.Promise.promise(new F.Function0<Result>() {
@Override
public Result apply() throws Throwable {
return unauthorized("Not authorised to perform action");
}
});
}
String auth = authHeader.substring(6);
byte[] decodedAuth = new Base64().decode(auth);
String[] credString = new String(decodedAuth, "UTF-8").split(":");
String username = credString[0];
String password = credString[1];
// here I authenticate against AD, replace by your own authentication mechanism
boolean loginCorrect = ADUtil.loginCorrect(username, password);
if (!loginCorrect) {
return F.Promise.promise(new F.Function0<Result>() {
@Override
public Result apply() throws Throwable {
return unauthorized("Not authorised to perform action");
}
});
} else {
return delegate.call(context);
}
}
}
下一个注释:
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import play.mvc.With;
@With(BasicAuthAction.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
@Inherited
@Documented
public @interface BasicAuth {
}
您现在可以按如下方式注释您的控制器函数:
@BasicAuth
public Promise<Result> yourControllerFunction() {
...
我已经更新了 Jonck van der Kogel 的回答,使其在解析授权 header 时更加严格,如果授权 header 无效,则不会失败并出现丑陋的异常,允许密码带有 ' :',并使用 Play 2.6:
所以,BasicAuthAction class:
import java.io.UnsupportedEncodingException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import org.apache.commons.codec.binary.Base64;
import play.Logger;
import play.Logger.ALogger;
import play.mvc.Action;
import play.mvc.Http;
import play.mvc.Http.Context;
import play.mvc.Result;
public class BasicAuthAction extends Action<Result> {
private static ALogger log = Logger.of(BasicAuthAction.class);
private static final String AUTHORIZATION = "Authorization";
private static final String WWW_AUTHENTICATE = "WWW-Authenticate";
private static final String REALM = "Basic realm=\"Realm\"";
@Override
public CompletionStage<Result> call(Context context) {
String authHeader = context.request().getHeader(AUTHORIZATION);
if (authHeader == null) {
context.response().setHeader(WWW_AUTHENTICATE, REALM);
return CompletableFuture.completedFuture(status(Http.Status.UNAUTHORIZED, "Needs authorization"));
}
String[] credentials;
try {
credentials = parseAuthHeader(authHeader);
} catch (Exception e) {
log.warn("Cannot parse basic auth info", e);
return CompletableFuture.completedFuture(status(Http.Status.FORBIDDEN, "Invalid auth header"));
}
String username = credentials[0];
String password = credentials[1];
boolean loginCorrect = checkLogin(username, password);
if (!loginCorrect) {
log.warn("Incorrect basic auth login, username=" + username);
return CompletableFuture.completedFuture(status(Http.Status.FORBIDDEN, "Forbidden"));
} else {
context.request().setUsername(username);
log.info("Successful basic auth login, username=" + username);
return delegate.call(context);
}
}
private String[] parseAuthHeader(String authHeader) throws UnsupportedEncodingException {
if (!authHeader.startsWith("Basic ")) {
throw new IllegalArgumentException("Invalid Authorization header");
}
String[] credString;
String auth = authHeader.substring(6);
byte[] decodedAuth = new Base64().decode(auth);
credString = new String(decodedAuth, "UTF-8").split(":", 2);
if (credString.length != 2) {
throw new IllegalArgumentException("Invalid Authorization header");
}
return credString;
}
private boolean checkLogin(String username, String password) {
/// change this
return username.equals("vlad");
}
}
然后,在控制器中 classes:
@With(BasicAuthAction.class)
public Result authPage() {
String username = request().username();
return Result.ok("Successful login as user: " + username + "! Here's your data: ...");
}
我正在寻找一些方法来为我的游戏框架应用程序进行一些身份验证:我想要 allow/disallow 对未经过身份验证的用户的全部访问权限
是否存在一些可用的 module/solution?我不需要任何形式的身份验证,只需要 401 HTTP 响应用于非身份验证用户(如 Apache .htacccess "AuthType Basic" 模式)。
恐怕没有这样的解决方案,原因很简单:通常当开发人员需要添加 authorization/authentication 堆栈时,他们会构建 full 解决方案。
最简单和最快的方法是使用 HTTP front-end server as a reverse-proxy for your application (I'd choose nginx 完成该任务,但如果您的机器上有 运行 Apache,也可以使用它)。它将允许您 filter/authenticate 使用通用服务器规则的流量
此外,它还为您带来其他好处,即:您可以创建类似 CDN 的路径,这样您就不会浪费应用程序的资源来提供 public 静态资产。您可以使用负载均衡器重新部署您的应用程序,而无需将其完全停止 x 分钟等。
您可以试试这个过滤器:
https://github.com/Kaliber/play-basic-authentication-filter
使用和配置看起来非常简单。
你也可以用 play.mvc.Action 来解决这个问题,就像这样。
首先你的行动:
import org.apache.commons.codec.binary.Base64;
import play.libs.F;
import play.libs.F.Promise;
import play.mvc.Action;
import play.mvc.Http.Context;
import play.mvc.Result;
import util.ADUtil;
public class BasicAuthAction extends Action<Result> {
private static final String AUTHORIZATION = "authorization";
private static final String WWW_AUTHENTICATE = "WWW-Authenticate";
private static final String REALM = "Basic realm=\"yourRealm\"";
@Override
public Promise<Result> call(Context context) throws Throwable {
String authHeader = context.request().getHeader(AUTHORIZATION);
if (authHeader == null) {
context.response().setHeader(WWW_AUTHENTICATE, REALM);
return F.Promise.promise(new F.Function0<Result>() {
@Override
public Result apply() throws Throwable {
return unauthorized("Not authorised to perform action");
}
});
}
String auth = authHeader.substring(6);
byte[] decodedAuth = new Base64().decode(auth);
String[] credString = new String(decodedAuth, "UTF-8").split(":");
String username = credString[0];
String password = credString[1];
// here I authenticate against AD, replace by your own authentication mechanism
boolean loginCorrect = ADUtil.loginCorrect(username, password);
if (!loginCorrect) {
return F.Promise.promise(new F.Function0<Result>() {
@Override
public Result apply() throws Throwable {
return unauthorized("Not authorised to perform action");
}
});
} else {
return delegate.call(context);
}
}
}
下一个注释:
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import play.mvc.With;
@With(BasicAuthAction.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
@Inherited
@Documented
public @interface BasicAuth {
}
您现在可以按如下方式注释您的控制器函数:
@BasicAuth
public Promise<Result> yourControllerFunction() {
...
我已经更新了 Jonck van der Kogel 的回答,使其在解析授权 header 时更加严格,如果授权 header 无效,则不会失败并出现丑陋的异常,允许密码带有 ' :',并使用 Play 2.6:
所以,BasicAuthAction class:
import java.io.UnsupportedEncodingException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import org.apache.commons.codec.binary.Base64;
import play.Logger;
import play.Logger.ALogger;
import play.mvc.Action;
import play.mvc.Http;
import play.mvc.Http.Context;
import play.mvc.Result;
public class BasicAuthAction extends Action<Result> {
private static ALogger log = Logger.of(BasicAuthAction.class);
private static final String AUTHORIZATION = "Authorization";
private static final String WWW_AUTHENTICATE = "WWW-Authenticate";
private static final String REALM = "Basic realm=\"Realm\"";
@Override
public CompletionStage<Result> call(Context context) {
String authHeader = context.request().getHeader(AUTHORIZATION);
if (authHeader == null) {
context.response().setHeader(WWW_AUTHENTICATE, REALM);
return CompletableFuture.completedFuture(status(Http.Status.UNAUTHORIZED, "Needs authorization"));
}
String[] credentials;
try {
credentials = parseAuthHeader(authHeader);
} catch (Exception e) {
log.warn("Cannot parse basic auth info", e);
return CompletableFuture.completedFuture(status(Http.Status.FORBIDDEN, "Invalid auth header"));
}
String username = credentials[0];
String password = credentials[1];
boolean loginCorrect = checkLogin(username, password);
if (!loginCorrect) {
log.warn("Incorrect basic auth login, username=" + username);
return CompletableFuture.completedFuture(status(Http.Status.FORBIDDEN, "Forbidden"));
} else {
context.request().setUsername(username);
log.info("Successful basic auth login, username=" + username);
return delegate.call(context);
}
}
private String[] parseAuthHeader(String authHeader) throws UnsupportedEncodingException {
if (!authHeader.startsWith("Basic ")) {
throw new IllegalArgumentException("Invalid Authorization header");
}
String[] credString;
String auth = authHeader.substring(6);
byte[] decodedAuth = new Base64().decode(auth);
credString = new String(decodedAuth, "UTF-8").split(":", 2);
if (credString.length != 2) {
throw new IllegalArgumentException("Invalid Authorization header");
}
return credString;
}
private boolean checkLogin(String username, String password) {
/// change this
return username.equals("vlad");
}
}
然后,在控制器中 classes:
@With(BasicAuthAction.class)
public Result authPage() {
String username = request().username();
return Result.ok("Successful login as user: " + username + "! Here's your data: ...");
}