我在我的项目中使用 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 为密码为“test”的用户“test”提供令牌:
@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 允许“ANY”角色,只要用户不为空:
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"}
(顺便说一句,URL 中的“test”是用户名,post 数据中的“test”是密码。就像基本身份验证一样简单,可以为 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."}