J'essaie de comprendre comment fonctionnent l'authentification et l'autorisation dans DropWizard . J'ai lu leur guide d'authentification ainsi que le projet dropwizard-security sur GitHub, mais j'ai l'impression qu'il me manque encore quelques concepts importants.
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"));
}
}
Et puis dans ma sous-classe Application
:
@Override
public void run(BackendConfiguration configuration, Environment environment) throws Exception {
environment.jersey().register(new BasicAuthProvider<SimplePrincipal>(new SimpleAuthenticator(), "SUPER SECRET STUFF"));
}
Et puis dans une méthode de ressource:
@GET
@Path("address/{address_id}")
@Override
public Address getAddress(@Auth @PathParam("address_id") Long id) {
addressDao.getAddressById(id);
}
Je pense que je l'ai correctement configuré à moitié pour l'authentification de base, mais je ne comprends pas le rôle que SimpleCredential
et SimplePrincipal
jouent. Plus précisément:
SimpleCredential
et SimplePrincipal
avec l'authentification de base? Dois-je ajouter quoi que ce soit à eux ou à d'autres classes pour que l'authentification de base fonctionne de telle sorte que le seul nom d'utilisateur valide soit simple_user
et le seul mot de passe valide est 12345
?SimplePrincipal
? Ou le concept d'autorisation est-il inexistant avec les services Web?Authentification de base le protocole indique que la demande du client doit avoir un en-tête sous la forme de
Authorization: Basic Base64Encoded(username:password)
où Base64Encoded(username:password)
est une chaîne codée Base64 réelle du username:password
. Par exemple, si mon nom d'utilisateur et mon mot de passe sont peeskillet:pass
, L'en-tête doit être envoyé sous la forme
Authorization: Basic cGVlc2tpbGxldDpwYXNz
Cela étant dit, le client Jersey (en supposant 1.x) a un HTTPBasicAuthFilter
, qui est un filtre côté client, qui gérera la partie encodage pour nous. Ainsi, la demande côté client pourrait ressembler à quelque chose comme
Client client = Client.create();
WebResource resource = client.resource(BASE_URI);
client.addFilter(new HTTPBasicAuthFilter("peeskillet", "pass"));
String response = resource.get(String.class);
C'est tout ce dont nous aurions besoin pour faire une simple demande GET avec l'en-tête d'autorisation.
SimpleCredential: Pour l'authentification de base, nous serions en fait tenus d'utiliser BasicCredentials
, au lieu de nos propres informations d'identification. Fondamentalement, la demande passera par le BasicAuthProvider
. Le fournisseur analysera l'en-tête d'autorisation et créera un objet BasicCredentials
à partir du nom d'utilisateur et du mot de passe analysés. Une fois ce traitement terminé, le BasicCredentials
sera transmis à nos SimpleAuthenticator
. Nous utilisons ces informations d'identification pour authentifier l'utilisateur.
SimplePrincipal: est fondamentalement ce que nous utiliserons pour autoriser le client. À partir du processus d'authentification, nous pouvons créer un principal, qui sera utilisé pour autoriser plus tard (voir la question 3). Donc, un exemple pourrait ressembler à quelque chose
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);
}
}
J'ai un peu modifié la classe SimplePrincipal
et créé une simple classe 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";
}
Certains pourraient préférer avoir une couche de filtre supplémentaire pour l'autorisation, mais Dropwizard semble avoir l'opinion d'opinion que l'autorisation devrait se produire dans la classe de ressources (j'ai oublié exactement où je l'ai lu, mais je croyez leur argument est la testabilité). Ce qui se passe avec le SimplePrincial
que nous avons créé dans le SimpleAuthenticator
, c'est qu'il peut être injecté dans notre méthode de ressource, avec l'utilisation des annotations @Auth
. Nous pouvons utiliser le SimplePrincipal
pour autoriser. Quelque chose comme
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();
}
}
Donc, tout mettre ensemble, avec cette configuration
environment.jersey().register(new BasicAuthProvider<SimplePrincipal>(
new SimpleAuthenticator(),
"Basic Example Realm")
);
et les informations d'identification du client que j'ai publiées précédemment, lorsque nous faisons la demande, nous devrions obtenir un retour
{"Hello": "peeskillet"}
Il convient également de mentionner que l'authentification de base seule n'est pas sécurisée, et il est recommandé de le faire via SSL
Voir aussi:
Quelques choses:
Pour Dropwizard 0.8.x, la configuration de Basic Auth a un peu changé. Vous pouvez voir plus ici . Un exemple simple serait
SimpleAuthenticator auth = new SimpleAuthenticator();
env.jersey().register(AuthFactory.binder(
new BasicAuthFactory<>(auth,"Example Realm",SimplePrincipal.class)));
Voir le lien ci-dessus pour l'utilisation recommandée de AuthenticationException