Dropwizard 0.8.4中如何用@RolesAllowed注解实现新版本权限控制效果?
在Dropwizard 0.8.4中使用@RolesAllowed实现权限控制
我刚好对Dropwizard 0.8.4的权限控制逻辑有印象,这个版本确实没法像新版本那样直接用Authorizer接口配合@RolesAllowed,不过咱们可以通过自定义过滤器结合JAX-RS的原生机制,实现和新版本一致的权限控制效果,具体步骤如下:
1. 先搞定基础的Basic Auth认证
这部分思路和新版本类似,先完成用户身份认证,同时让用户对象携带角色信息:
1.1 定义带角色的用户类(实现Principal)
这个类用来存储用户的身份标识和对应的权限角色,方便后续权限校验:
import java.security.Principal; import java.util.Set; public class User implements Principal { private final String username; private final Set<String> roles; public User(String username, Set<String> roles) { this.username = username; this.roles = roles; } @Override public String getName() { return username; } public Set<String> getRoles() { return roles; } }
1.2 实现Authenticator接口
负责验证用户的账号密码,返回携带角色信息的User对象:
import io.dropwizard.auth.Authenticator; import io.dropwizard.auth.basic.BasicCredentials; import com.google.common.collect.Sets; import java.util.Optional; public class MyAuthenticator implements Authenticator<BasicCredentials, User> { @Override public Optional<User> authenticate(BasicCredentials credentials) throws AuthenticationException { // 这里替换成你的实际验证逻辑(比如从数据库查询用户和角色) if ("admin".equals(credentials.getUsername()) && "admin123".equals(credentials.getPassword())) { return Optional.of(new User("admin", Sets.newHashSet("ADMIN", "USER"))); } if ("user".equals(credentials.getUsername()) && "user123".equals(credentials.getPassword())) { return Optional.of(new User("user", Sets.newHashSet("USER"))); } return Optional.empty(); } }
1.3 在Application中注册Basic Auth处理器
在你的Application子类的run方法里,把Authenticator绑定到Jetty的安全处理器:
import io.dropwizard.Application; import io.dropwizard.auth.basic.BasicAuthSecurityHandler; import io.dropwizard.setup.Environment; public class MyApp extends Application<MyConfiguration> { @Override public void run(MyConfiguration config, Environment env) { // 注册Basic Auth安全处理器 env.getApplicationContext().setSecurityHandler(new BasicAuthSecurityHandler( new MyAuthenticator(), "MyAppRealm" // 自定义Realm名称,随便填写即可 )); // 注册你的资源类 env.jersey().register(UserResource.class); } }
2. 自定义过滤器处理@RolesAllowed注解
Dropwizard 0.8.4不会自动识别@RolesAllowed注解,所以我们需要写一个ContainerRequestFilter来拦截请求,校验当前用户的角色是否符合注解要求:
2.1 实现权限检查过滤器
import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerRequestFilter; import javax.ws.rs.container.ResourceInfo; import javax.ws.rs.core.Context; import javax.ws.rs.core.Response; import javax.ws.rs.core.SecurityContext; import javax.ws.rs.Priorities; import javax.ws.rs.Provider; import javax.annotation.Priority; import java.io.IOException; import java.lang.reflect.Method; import java.util.Arrays; @Provider @Priority(Priorities.AUTHORIZATION) // 确保过滤器在认证之后执行 public class RolesAllowedFilter implements ContainerRequestFilter { @Context private ResourceInfo resourceInfo; @Override public void filter(ContainerRequestContext requestContext) throws IOException { SecurityContext securityContext = requestContext.getSecurityContext(); // 先判断用户是否已认证 if (securityContext == null || securityContext.getUserPrincipal() == null) { requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).build()); return; } User currentUser = (User) securityContext.getUserPrincipal(); // 检查方法上的@RolesAllowed注解 Method targetMethod = resourceInfo.getResourceMethod(); checkRoles(targetMethod, currentUser, requestContext); // 可选:检查类上的@RolesAllowed注解(如果类上有标记,所有方法都要遵守) Class<?> targetClass = resourceInfo.getResourceClass(); checkRoles(targetClass, currentUser, requestContext); } private void checkRoles(AnnotatedElement element, User user, ContainerRequestContext context) { RolesAllowed rolesAllowed = element.getAnnotation(RolesAllowed.class); if (rolesAllowed == null) { return; } String[] allowedRoles = rolesAllowed.value(); // 校验用户是否拥有至少一个允许的角色 boolean hasPermission = Arrays.stream(allowedRoles) .anyMatch(user.getRoles()::contains); if (!hasPermission) { context.abortWith(Response.status(Response.Status.FORBIDDEN).build()); } } }
2.2 注册过滤器到Jersey环境
回到Application的run方法,把这个权限过滤器注册进去:
@Override public void run(MyConfiguration config, Environment env) { // ... 之前的Basic Auth注册代码 ... // 注册权限检查过滤器 env.jersey().register(RolesAllowedFilter.class); }
3. 在资源类中使用@RolesAllowed
现在就可以像新版本那样,在资源方法或类上标记@RolesAllowed来控制访问权限了:
import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.FormParam; import javax.ws.rs.core.Response; import javax.annotation.security.RolesAllowed; import java.util.Arrays; import java.util.List; @Path("/users") public class UserResource { @GET @RolesAllowed("USER") // 拥有USER或ADMIN角色的用户都能访问 public List<String> getUsers() { return Arrays.asList("user1", "user2", "admin"); } @POST @RolesAllowed("ADMIN") // 只有ADMIN角色的用户能访问 public Response createUser(@FormParam("username") String username) { // 这里写创建用户的业务逻辑 return Response.status(Response.Status.CREATED).entity("用户" + username + "创建成功").build(); } }
补充说明
- 如果你需要更细粒度的权限控制(比如基于资源ID的权限),可以扩展这个过滤器,结合自定义注解来实现。
- 注意Dropwizard 0.8.4原生的
SecurityContext.isUserInRole()方法默认不生效,所以我们直接从User对象中获取角色进行校验是更可靠的方式。
内容的提问来源于stack exchange,提问作者k_liaqat
相关产品推荐
相关产品推荐

