Подтвердить что ты не робот

DropWizard Auth по примеру

Я пытаюсь понять, как работают аутентификация и авторизация в DropWizard. Я прочитал их auth guide, а также dropwizard-security проект на GitHub, но чувствую, что мне все еще не хватает нескольких важных концепций.

public class SimpleCredential {
    private String password;

    public SimpleCredential(String password) {
        super();

        this.password = password;
    }
}

public class SimplePrincipal {
    pivate String username;

    public SimplePrincipal(String username) {
        super();

        this.username = username;
    }
}

public class SimpleAuthenticator implements Authenticator<SimpleCredential, SimplePrincipal> {
    @Override
    public Optional<SimplePrincipal> authenticate(SimpleCredential credential) throws AuthenticationException {
        if(!"12345".equals(credential.getPassword())) {
            throw new AuthenticationException("Sign in failed.");
        }

        Optional.fromNullable(new SimplePrincipal("simple_user"));
    }
}

И затем в моем подклассе Application:

@Override
public void run(BackendConfiguration configuration, Environment environment) throws Exception {
    environment.jersey().register(new BasicAuthProvider<SimplePrincipal>(new SimpleAuthenticator(), "SUPER SECRET STUFF"));
}

И затем в методе ресурса:

@GET
@Path("address/{address_id}")
@Override
public Address getAddress(@Auth @PathParam("address_id") Long id) {
    addressDao.getAddressById(id);
}

Я думаю, что я правильно настроил эту настройку для базового auth, но не понял роль, которую играют SimpleCredential и SimplePrincipal. В частности:

  • Как установить базовое имя пользователя/пароль auth от клиента Jersey/JAX-RS?
  • Какую роль играют SimpleCredential и SimplePrincipal с базовым auth? Нужно ли добавлять что-либо к ним или другим классам, чтобы сделать базовую работу с auth, так что единственным действительным именем пользователя является simple_user, а единственным допустимым паролем является 12345?
  • Как мне обеспечить доступ/авторизовать/роли через SimplePrincipal? Или концепция авторизации отсутствует в веб-службах?
4b9b3361

Ответ 1

Вопрос 1:

Базовая аутентификация утверждает, что запрос клиента должен иметь заголовок в форме

Authorization: Basic Base64Encoded(username:password)

где Base64Encoded(username:password) - фактическая строка с кодировкой Base64 username:password. Например, если мое имя пользователя и пароль peeskillet:pass, заголовок должен быть отправлен как

Authorization: Basic cGVlc2tpbGxldDpwYXNz

При этом клиент Джерси (предположительно 1.x) имеет HTTPBasicAuthFilter, который является фильтром на стороне клиента, который будет обрабатывать часть кодирования для нас. Таким образом, запрос на стороне клиента может выглядеть примерно так:

Client client = Client.create();
WebResource resource = client.resource(BASE_URI);
client.addFilter(new HTTPBasicAuthFilter("peeskillet", "pass"));
String response = resource.get(String.class);

Это все, что нам нужно будет сделать простым запросом GET с заголовком авторизации.

Вопрос 2:

SimpleCredential: Для Basic auth нам действительно потребуется использовать BasicCredentials вместо наших собственных учетных данных. В принципе, запрос будет проходить через BasicAuthProvider. Поставщик будет анализировать заголовок авторизации и создать объект BasicCredentials из проанализированного имени пользователя и пароля. Как только эта обработка завершится, BasicCredentials будет передана нашим SimpleAuthenticator. Мы используем эти учетные данные для аутентификации пользователя.

SimplePrincipal: - это в основном то, что мы будем использовать для авторизации клиента. Из процесса аутентификации мы можем построить принципал, который будет использоваться для авторизации позже (см. Вопрос 3). Таким образом, пример может выглядеть примерно как

import com.google.common.base.Optional;
import io.dropwizard.auth.AuthenticationException;
import io.dropwizard.auth.Authenticator;
import io.dropwizard.auth.basic.BasicCredentials;

public class SimpleAuthenticator implements Authenticator<BasicCredentials,
                                                          SimplePrincipal> {
    @Override
    public Optional<SimplePrincipal> authenticate(BasicCredentials credentials)
            throws AuthenticationException {

        // Note: this is horrible authentication. Normally we'd use some
        // service to identify the password from the user name.
        if (!"pass".equals(credentials.getPassword())) {
            throw new AuthenticationException("Boo Hooo!");
        }

        // from some user service get the roles for this user
        // I am explicitly setting it just for simplicity
        SimplePrincipal prince = new SimplePrincipal(credentials.getUsername());
        prince.getRoles().add(Roles.ADMIN);

        return Optional.fromNullable(prince);
    }
}

Я немного изменил класс SimplePrincipal и создал простой класс Roles.

public class SimplePrincipal {

    private String username;
    private List<String> roles = new ArrayList<>();

    public SimplePrincipal(String username) {
        this.username = username;
    }

    public List<String> getRoles() {
        return roles;
    }

    public boolean isUserInRole(String roleToCheck) {
        return roles.contains(roleToCheck);
    }

    public String getUsername() {
        return username;
    }
}

public class Roles {
    public static final String USER = "USER";
    public static final String ADMIN = "ADMIN";
    public static final String EMPLOYEE = "EMPLOYEE";
}

Вопрос 3:

Некоторые могут предпочесть наличие дополнительного фильтра для авторизации, но Dropwizard, похоже, придерживается мнения, что авторизация должна выполняться в классе ресурсов (я забыл, где именно я его прочитал, но я считаю, что их аргумент является тестируемостью). Что происходит с SimplePrincial, который мы создали в SimpleAuthenticator, состоит в том, что он может быть введен в наш ресурсный метод с использованием аннотаций @Auth. Мы можем использовать SimplePrincipal для авторизации. Что-то вроде

import dropwizard.sample.helloworld.security.Roles;
import dropwizard.sample.helloworld.security.SimplePrincipal;
import io.dropwizard.auth.Auth;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

@Path("/simple")
public class SimpleResource {

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response getResponse(@Auth SimplePrincipal principal) {
        if (!principal.isUserInRole(Roles.ADMIN)) {
            throw new WebApplicationException(Response.Status.FORBIDDEN);
        }
        return Response.ok(
                "{\"Hello\": \"" + principal.getUsername() + "\"}").build();
    }
}

Итак, все вместе, с этой конфигурацией

environment.jersey().register(new BasicAuthProvider<SimplePrincipal>(
                                            new SimpleAuthenticator(), 
                                            "Basic Example Realm")
);

и учетные данные клиента, которые я опубликовал ранее, когда мы делаем запрос, мы должны получить возвращенный

{"Hello": "peeskillet"}

Также следует упомянуть, что Basic auth не является безопасным, и рекомендуется делать это через SSL


См. раздел Связанный:


UPDATE

Несколько вещей:

  • Для Dropwizard 0.8.x конфигурация Basic Auth немного изменилась. Вы можете видеть здесь. Простым примером может быть

    SimpleAuthenticator auth = new SimpleAuthenticator();
    env.jersey().register(AuthFactory.binder(
            new BasicAuthFactory<>(auth,"Example Realm",SimplePrincipal.class)));
    
  • См. ссылку выше для рекомендуемого использования AuthenticationException