使用Dropwizard 1.2.0和Dropwizard JWT library,我试图从名为/token
此端点要求客户端使用基本身份验证方法传递用户名和密码。如果成功,响应将包含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
发出请求时,我没有得到基本身份验证的提示,而是通过
访问此资源需要凭据。
如何让控制器提示输入用户名和密码并在成功时生成JWT?
答案 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));