DropWizard Auth by Example

我试图了解DropWizard中的身份validation和授权是如何工作的。 我已经阅读了他们的auth指南以及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 { @Override public Optional 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(new SimpleAuthenticator(), "SUPER SECRET STUFF")); } 

然后在资源方法中:

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

我认为我已经为基本身份validation正确配置了一半,但没有理解SimpleCredentialSimplePrincipal扮演的角色。 特别:

  1. 如何从Jersey / JAX-RS客户端设置基本身份validation用户名/密码?
  2. SimpleCredentialSimplePrincipal在基本身份validation中扮演什么角色? 我是否需要向它们或其他类添加任何内容以进行基本身份validation工作,以便唯一有效的用户名是simple_user ,唯一有效的密码是12345
  3. 如何通过SimplePrincipal强制执行访问/ authroization /角色? 或者是Web服务不存在授权的概念?

问题1:

基本身份validation协议规定客户端请求应具有表头forms的标头

 Authorization: Basic Base64Encoded(username:password) 

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

 Authorization: Basic cGVlc2tpbGxldDpwYXNz 

话虽如此,Jersey客户端(假设1.x)有一个HTTPBasicAuthFilter ,它是一个客户端filter,它将为我们处理编码部分。 所以客户端请求可能看起来像

 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 。 提供程序将解析Authorization标头并从解析的用户名和密码创建BasicCredentials对象。 一旦处理完成, BasicCredentials将传递给我们的SimpleAuthenticator 。 我们使用这些凭据来validation用户身份。

SimplePrincipal:基本上是我们用来授权客户端的。 从身份validation过程中,我们可以构建一个主体,稍后将用于授权(请参阅问题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 { @Override public Optional 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 roles = new ArrayList<>(); public SimplePrincipal(String username) { this.username = username; } public List 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似乎认为授权应该在资源类中发生(我忘记了我在哪里阅读它,但我相信他们的论点是可测试性)。 我们在SimpleAuthenticator创建的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( new SimpleAuthenticator(), "Basic Example Realm") ); 

以及我之前发布的客户凭据,当我们发出请求时,我们应该返回一个

 {"Hello": "peeskillet"} 

另外应该提到单独的基本身份validation不安全,建议通过SSL完成


见相关:

  • DropWizard Auth领域
  • 使用DropWizard的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推荐用法,请参阅上面的链接