通过基本身份验证端点创建JSON Web令牌? Dropwizard

时间:2017-11-02 17:06:42

标签: java jwt dropwizard

使用Dropwizard 1.2.0和Dropwizard JWT library,我试图从名为/token

的API端点创建json网络令牌

此端点要求客户端使用基本身份验证方法传递用户名和密码。如果成功,响应将包含JSON Web令牌。

主要

public class ShepherdAuth implements JwtCookiePrincipal {

private String name;
private Set<String> roles;

public ShepherdAuth(String name, Set<String> roles) {
    this.name = checkNotNull(name, "User name is required");
    this.roles = checkNotNull(roles, "Roles are required");
}

@Override
public boolean isPersistent() {
    return false;
}

@Override
public boolean isInRole(final String s) {
    return false;
}

@Override
public String getName() {
    return this.name;
}

@Override
public boolean implies(Subject subject) {
    return false;
}

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

身份验证

public class ShepherdAuthenticator implements Authenticator<BasicCredentials, ShepherdAuth> {

private static final Map<String, Set<String>> VALID_USERS = ImmutableMap.of(
        "guest", ImmutableSet.of(),
        "shepherd", ImmutableSet.of("SHEPHERD"),
        "admin", ImmutableSet.of("ADMIN", "SHEPHERD")
);

@Override
public Optional<ShepherdAuth> authenticate(BasicCredentials credentials) throws AuthenticationException {
    if (VALID_USERS.containsKey(credentials.getUsername()) && "password".equals(credentials.getPassword())) {
        return Optional.of(new ShepherdAuth(credentials.getUsername(), VALID_USERS.get(credentials.getUsername())));
    }
    return Optional.empty();
}
}

资源/控制器

public class ShepherdController implements ShepherdApi {

public ShepherdController() {
}

@PermitAll
@GET
@Path("/token")
public ShepherdAuth auth(@Auth final BasicCredentials user) {
    return new ShepherdAuth(user.getUsername(),
        ImmutableSet.of("SHEPHERD"));
}

App / Config

    @Override
public void run(final ShepherdServiceConfiguration configuration,
                final Environment environment) {

    final ShepherdController shepherdController = new ShepherdController();

    // app authentication
    environment.jersey().register(new AuthDynamicFeature(new BasicCredentialAuthFilter.Builder<ShepherdAuth>()
            .setAuthenticator(new ShepherdAuthenticator())
            .setAuthorizer(new ShepherdAuthorizer())
            .setRealm(configuration.getName())
            .buildAuthFilter()));

当我尝试向/shepherd/token发出请求时,我没有得到基本身份验证的提示,而是通过

获得HTTP 401响应
  

访问此资源需要凭据。

如何让控制器提示输入用户名和密码并在成功时生成JWT?

2 个答案:

答案 0 :(得分:2)

我使用https://github.com/jwtk/jjwt在我的项目中实现了JWT令牌,但该解决方案很容易应用于另一个库。诀窍是使用不同的身份验证器。

这个答案不适合Dropwizard JWT Library但是为Dropwizard提供JWT的工作很好:)

首先,申请:

environment.jersey().register(new TokenResource(configuration.getJwsSecretKey()));
environment.jersey().register(new HelloResource());
environment.jersey().register(RolesAllowedDynamicFeature.class);
environment.jersey().register(new AuthValueFactoryProvider.Binder<>(User.class));
environment.jersey()
    .register(
        new AuthDynamicFeature(
            new ChainedAuthFilter<>(
                Arrays
                    .asList(
                        new JWTCredentialAuthFilter.Builder<User>()
                            .setAuthenticator(
                                new JWTAuthenticator(configuration.getJwsSecretKey()))
                            .setPrefix("Bearer").setAuthorizer(new UserAuthorizer())
                            .buildAuthFilter(),
                        new JWTDefaultCredentialAuthFilter.Builder<User>()
                            .setAuthenticator(new JWTDefaultAuthenticator())
                            .setAuthorizer(new UserAuthorizer()).setRealm("SUPER SECRET STUFF")
                            .buildAuthFilter()))));

请注意,配置类必须包含配置设置:

String jwsSecretKey;

此处,TokenResource是令牌提供资源,HelloResource是我们的测试资源。 User是校长,如下所示:

public class User implements Principal {
  private String name;
  private String password;
  ...
}

还有一个用于传递JWT令牌的类:

public class JWTCredentials {
  private String jwtToken;
  ...
}

TokenResource为用户提供令牌&#34; test&#34;用密码&#34;测试&#34;:

@POST
@Path("{user}")
@PermitAll
public String createToken(@PathParam("user") String user, String password) {
  if ("test".equals(user) && "test".equals(password)) {
    SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
    long nowMillis = System.currentTimeMillis();
    Date now = new Date(nowMillis);
    byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(this.secretKey);
    Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
    JwtBuilder builder = Jwts.builder().setIssuedAt(now).setSubject("test")
      .signWith(signatureAlgorithm, signingKey);
    return builder.compact();
  }
  throw new WebApplicationException(Response.Status.UNAUTHORIZED);
}

HelloResource只是回应用户:

@GET
@RolesAllowed({"ANY"})
public String hello(@Auth User user) {
  return "hello user \"" + user.getName() + "\"";
}

JWTCredentialAuthFilter提供两种身份验证方案的凭据:

@Priority(Priorities.AUTHENTICATION)
public class JWTCredentialAuthFilter<P extends Principal> extends AuthFilter<JWTCredentials, P> {

  public static class Builder<P extends Principal>
      extends AuthFilterBuilder<JWTCredentials, P, JWTCredentialAuthFilter<P>> {

    @Override
    protected JWTCredentialAuthFilter<P> newInstance() {
      return new JWTCredentialAuthFilter<>();
    }
  }

  @Override
  public void filter(ContainerRequestContext requestContext) throws IOException {
    final JWTCredentials credentials =
    getCredentials(requestContext.getHeaders().getFirst(HttpHeaders.AUTHORIZATION));
    if (!authenticate(requestContext, credentials, "JWT")) {
      throw new WebApplicationException(
          this.unauthorizedHandler.buildResponse(this.prefix, this.realm));
    }
  }

  private static JWTCredentials getCredentials(String authLine) {
    if (authLine != null && authLine.startsWith("Bearer ")) {
      JWTCredentials result = new JWTCredentials();
      result.setJwtToken(authLine.substring(7));
      return result;
    }
    return null;
  }
}

JWTAuthenticator是提供JWT凭据的时间:

public class JWTAuthenticator implements Authenticator<JWTCredentials, User> {

  private String secret;

  public JWTAuthenticator(String jwtsecret) {
    this.secret = jwtsecret;
  }

  @Override
  public Optional<User> authenticate(JWTCredentials credentials) throws AuthenticationException {
    try {
      Claims claims = Jwts.parser().setSigningKey(DatatypeConverter.parseBase64Binary(this.secret))
      .parseClaimsJws(credentials.getJwtToken()).getBody();
      User user = new User();
      user.setName(claims.getSubject());
      return Optional.ofNullable(user);
    } catch (@SuppressWarnings("unused") ExpiredJwtException | UnsupportedJwtException
        | MalformedJwtException | SignatureException | IllegalArgumentException e) {
      return Optional.empty();
    }
  }
}

JWTDefaultAuthenticator是没有凭据时,代码为空用户:

public class JWTDefaultAuthenticator implements Authenticator<JWTCredentials, User> {

  @Override
  public Optional<User> authenticate(JWTCredentials credentials) throws AuthenticationException {
    return Optional.of(new User());
  }
}

UserAuthorizer允许&#34; ANY&#34;角色,只要用户不为null:

public class UserAuthorizer implements Authorizer<User> {
  @Override
  public boolean authorize(User user, String role) {
    return user != null && "ANY".equals(role)
  }
}

如果一切顺利,

curl -s -X POST -d 'test' http://localhost:8080/token/test

会给你一些类似的东西:

eyJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1MDk3MDYwMjYsInN1YiI6InRlc3QifQ.ZrRmWTUDpaA6JlU4ysIcFllxtqvUS2OPbCMJgyou_tY

和此查询

curl -s -X POST -d 'xtest' http://localhost:8080/token/test

将失败

{"code":401,"message":"HTTP 401 Unauthorized"}

(BTW,&#34;测试&#34; URL中是用户名和&#34;测试&#34;后期数据是密码。和基本身份验证一样简单,可以配置为CORS 。)

和请求

curl -s -X GET -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1MDk3MDYwMjYsInN1YiI6InRlc3QifQ.ZrRmWTUDpaA6JlU4ysIcFllxtqvUS2OPbCMJgyou_tY' http://localhost:8080/hello

将显示

hello user "test"

,而

curl -s -X GET -H 'Authorization: Bearer invalid' http://localhost:8080/hello

curl -s -X GET http://localhost:8080/hello

将导致

{"code":403,"message":"User not authorized."}

答案 1 :(得分:1)

您的配置中缺少此行。

environment.jersey().register(new AuthValueFactoryProvider.Binder<>(User.class));

相关问题