【问题标题】:HTTP Basic Authentication for Play framework 2.4Play 框架 2.4 的 HTTP 基本身份验证
【发布时间】:2016-02-20 16:58:35
【问题描述】:

我正在寻找某种方法来为我的游戏框架应用程序进行一些身份验证:我希望允许/禁止对未经身份验证的用户进行全部访问

是否存在一些工作模块/解决方案?我不需要任何形式的身份验证,只需 401 HTTP 响应非身份验证用户(如 Apache .htacccess "AuthType Basic" 模式)。

【问题讨论】:

  • 你可能想看看模块列表:playframework.com/modules
  • @cchantep 这些模块仅适用于 Play 1.x 系列,现在是只读的。

标签: scala authentication playframework playframework-2.4


【解决方案1】:

我更新了 Jonck van der Kogel 的答案,使其在解析授权标头时更加严格,如果 auth 标头无效,则不会因丑陋的异常而失败,允许使用“:”的密码,并与 Play 2.6 一起使用:

所以,BasicAuthAction 类:

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

然后,在控制器类中:

@With(BasicAuthAction.class)
public Result authPage() {
    String username = request().username();
    return Result.ok("Successful login as user: " + username + "! Here's your data: ...");
}

【讨论】:

    【解决方案2】:

    你可以试试这个过滤器:

    https://github.com/Kaliber/play-basic-authentication-filter

    使用和配置看起来非常简单。

    【讨论】:

    • 这是我一直在寻找的:简单而严谨,没有额外的功能
    【解决方案3】:

    你也可以像这样使用 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() {
    ...
    

    【讨论】:

      【解决方案4】:

      恐怕没有这样的解决方案,原因很简单:通常当开发人员需要添加授权/身份验证堆栈时,他们会构建完整的解决方案。

      最简单、最快的方法是使用HTTP front-end server 作为您的应用程序的反向代理(我会为该任务选择nginx,但如果您在机器上运行Apache,它也可以使用)。它将允许您使用通用服务器的规则过滤/验证流量

      此外,它还为您提供了其他好处,即:您可以创建类似 CDN 的路径,因此您不会浪费应用程序的资源来提供公共静态资产。您可以使用负载均衡器重新部署您的应用程序,而无需完全停止 x 分钟等。

      【讨论】:

      猜你喜欢
      • 2013-11-10
      • 1970-01-01
      • 2013-01-27
      • 1970-01-01
      • 2021-03-21
      • 2011-11-12
      • 2011-05-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多