Play框架2.4的HTTP基本身份验证

时间:2016-02-20 16:58:36

标签: scala authentication playframework playframework-2.4

我正在寻找一些方法来为我的Play框架应用程序进行一些身份验证:我希望允许/禁止对未经过身份验证的用户的整体访问

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

4 个答案:

答案 0 :(得分:5)

I've updated Jonck van der Kogel's answer to be more strict in parsing the authorization header, to not fail with ugly exceptions if the auth header is invalid, to allow passwords with ':', and to work with Play 2.6:

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

And then, in controller classes:

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

答案 1 :(得分:3)

您可以尝试使用此过滤器:

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

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

答案 2 :(得分:2)

您也可以使用play.mvc.Action来解决此问题。

首先你的行动:

Tweet

接下来你的注释:

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 {
}

答案 3 :(得分:1)

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

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

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