java DropWizard 身份验证示例

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27392224/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 11:42:46  来源:igfitidea点击:

DropWizard Auth by Example

javajerseyjax-rsbasic-authenticationdropwizard

提问by IAmYourFaja

I'm trying to understand how authentication and authorization work in DropWizard. I've read their auth guideas well as the dropwizard-securityproject on GitHub, but feel like I'm still missing a few important concepts.

我试图了解身份验证和授权在DropWizard 中是如何工作的。我已经阅读了他们的身份验证指南以及GitHub 上的dropwizard-security项目,但感觉我仍然缺少一些重要的概念。

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

And then in my Applicationsubclass:

然后在我的Application子类中:

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

And then in a resource method:

然后在资源方法中:

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

I think I have this half-configured correctly for basic auth, but not understanding the role that SimpleCredentialand SimplePrincipalplay. Specifically:

我觉得我有这个半正确配置为基本身份验证,但不理解的角色SimpleCredentialSimplePrincipal发挥。具体来说:

  1. How do I set basic auth username/password from the Jersey/JAX-RS client?
  2. What role do SimpleCredentialand SimplePrincipalplay with basic auth? Do I need to add anything to them or other classes to make basic auth work such that the only valid username is simple_userand the only valid password is 12345?
  3. How do I enforce access/authroization/roles via SimplePrincipal? Or is the concept of authorization non-existent with web services?
  1. 如何从 Jersey/JAX-RS 客户端设置基本身份验证用户名/密码?
  2. 什么作用SimpleCredential,并SimplePrincipal与基本身份验证戏?我是否需要向它们或其他类添加任何内容以使基本身份验证工作,以便唯一有效的用户名simple_user和唯一有效的密码是12345
  3. 我如何通过 强制访问/授权/角色SimplePrincipal?或者 Web 服务不存在授权的概念?

回答by Paul Samsotha

Question 1:

问题 1:

Basic Authenticationprotocol 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

其中Base64Encoded(username:password)是 的实际 Base64 编码字符串username:password。例如,如果我的用户名和密码是peeskillet:pass,则标头应作为

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

话虽如此,Jersey 客户端(假设是 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);

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

这就是我们使用授权标头发出简单 GET 请求所需的全部内容。

Question 2:

问题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 BasicCredentialsobject from the parsed username and password. Once that processing has finished, the BasicCredentialswill get passed to our SimpleAuthenticator's. We use those credentials to authenticate the user.

SimpleCredential:对于基本身份验证,我们实际上需要使用BasicCredentials,而不是我们自己的凭据。基本上,请求将通过BasicAuthProvider. 提供者将解析 Authorization 标头并BasicCredentials根据解析的用户名和密码创建一个对象。处理完成后,BasicCredentials将传递给我们SimpleAuthenticator的 。我们使用这些凭据对用户进行身份验证。

SimplePrincipal:is basically what we will use to authorizethe 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

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

I altered the SimplePrincipalclass a bit, and created a simple Rolesclass.

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

Question 3:

问题 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 believetheir argument is testability). What happens with the SimplePrincialthat we created in the SimpleAuthenticatoris that it can be injected into our resource method, with the use of the @Authannotations. We can use the SimplePrincipalto authorize. Something like

有些人可能更喜欢有一个额外的授权过滤层,但 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();
    }
}


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

还应该提到的是,单独的 Basic auth 并不安全,建议通过 SSL 完成



See Related:

见相关:



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

  • 对于 Dropwizard 0.8.x,Basic Auth 的配置略有变化。你可以在这里看到更多。一个简单的例子是

    SimpleAuthenticator auth = new SimpleAuthenticator();
    env.jersey().register(AuthFactory.binder(
            new BasicAuthFactory<>(auth,"Example Realm",SimplePrincipal.class)));
    
  • 请参阅上面的链接以了解推荐的用法 AuthenticationException