通过基本身份验证端点创建 JSON Web 令牌?下拉精灵
Creating JSON Web Tokens through Basic Authentication endpoint? Dropwizard
使用 Dropwizard 1.2.0 和 Dropwizard JWT library,我正在尝试从名为 /token
的 API 端点创建 json 网络令牌
此端点要求客户端使用基本身份验证方法传递用户名和密码。如果成功,响应将包含一个 JSON 网络令牌。
校长
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"));
}
应用/配置
@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 响应
Credentials are required to access this resource.
如何让控制器提示输入用户名和密码并在成功时生成 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()))));
请注意配置class必须包含配置设置:
String jwsSecretKey;
这里的TokenResource
是token提供资源,HelloResource
是我们的测试资源。 User
是校长,像这样:
public class User implements Principal {
private String name;
private String password;
...
}
还有一个 class 用于传递 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"是密码。和basic 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));
使用 Dropwizard 1.2.0 和 Dropwizard JWT library,我正在尝试从名为 /token
此端点要求客户端使用基本身份验证方法传递用户名和密码。如果成功,响应将包含一个 JSON 网络令牌。
校长
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"));
}
应用/配置
@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
发出请求时,我没有收到基本身份验证提示,而是收到带有
Credentials are required to access this resource.
如何让控制器提示输入用户名和密码并在成功时生成 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()))));
请注意配置class必须包含配置设置:
String jwsSecretKey;
这里的TokenResource
是token提供资源,HelloResource
是我们的测试资源。 User
是校长,像这样:
public class User implements Principal {
private String name;
private String password;
...
}
还有一个 class 用于传递 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"是密码。和basic 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));