DropWizard Auth by Example

前端 未结 1 972
时光取名叫无心
时光取名叫无心 2020-12-05 01:01

I\'m trying to understand how authentication and authorization work in DropWizard. I\'ve read their auth guide as well as the dropwizard-security project on GitHub, but feel

相关标签:
1条回答
  • 2020-12-05 01:09

    Question 1:

    Basic Authentication protocol states the client request should have a header in the form of

    Authorization: Basic Base64Encoded(username:password)
    

    where Base64Encoded(username:password) is an actual Base64 encoded string of the username:password. For example, if my username and password are peeskillet:pass, the header should be sent out as

    Authorization: Basic cGVlc2tpbGxldDpwYXNz
    

    That being said, the Jersey Client (assuming 1.x) has an HTTPBasicAuthFilter, which is a client side filter, that will handle the encoding part for us. So the client side request might look something like

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

    That's all we would need to make a simple GET request with the authorization header.

    Question 2:

    SimpleCredential: For Basic auth, we would actually be required to use BasicCredentials, instead of our own credentials. Basically, the request will go through the BasicAuthProvider. The provider will parse the Authorization header and create a BasicCredentials object from the parsed username and password. Once that processing has finished, the BasicCredentials will get passed to our SimpleAuthenticator's. We use those credentials to authenticate the user.

    SimplePrincipal: is basically what we will use to authorize the client. From the authentication process, we can build a principal, that will be used to authorize later (see Question 3). So an example might look something like

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

    I altered the SimplePrincipal class a bit, and created a simple Roles class.

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

    Question 3:

    Some might prefer to have an extra filter layer for authorization, but Dropwizard appears to have the opinionated view that the authorization should occur in the resource class (I forgot exactly where I read it, but I believe their argument is testability). What happens with the SimplePrincial that we created in the SimpleAuthenticator is that it can be injected into our resource method, with the use of the @Auth annotations. We can use the SimplePrincipal to authorize. Something like

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

    So putting it all together, with this configuration

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

    and the client credentials I posted previously, when we make the request, we should get a returned

    {"Hello": "peeskillet"}
    

    Also it should be mentioned that Basic auth alone is not secure, and it is recommended to be done over SSL


    See Related:

    • DropWizard Auth Realms
    • SSL with DropWizard

    UPDATE

    A couple things:

    • For Dropwizard 0.8.x, the configuration of Basic Auth has changed a bit. You can see more here. A simple example would be

      SimpleAuthenticator auth = new SimpleAuthenticator();
      env.jersey().register(AuthFactory.binder(
              new BasicAuthFactory<>(auth,"Example Realm",SimplePrincipal.class)));
      
    • See above link for recommended usage of AuthenticationException

    0 讨论(0)
提交回复
热议问题