[英]Creating JSON Web Tokens through Basic Authentication endpoint? Dropwizard
使用Dropwizard 1.2.0和Dropwizard JWT库 ,我试图从名为/token
的API端点创建json web /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
发出请求时,我没有得到基本身份验证的提示,而是获得HTTP 401响应
需要凭据才能访问此资源。
如何让控制器提示输入用户名和密码并成功生成JWT?
我使用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”角色,只要用户不为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"}
(顺便说一句,URL中的“test”是用户名,post数据中的“test”是密码。和基本auth一样简单,可以配置为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."}
您在配置中遗漏了这一行。
environment.jersey().register(new AuthValueFactoryProvider.Binder<>(User.class));
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.