有什么好的方法可以根据 Spring Security 角色过滤 JSON 输出吗?我正在寻找类似@JsonIgnore 的东西,但对于角色,例如@HasRole("ROLE_ADMIN")。我应该如何实现这个?
3 回答
对于那些从谷歌登陆这里的人,这里有一个与 Spring Boot 1.4 类似的解决方案。
为您的每个角色定义接口,例如
public class View {
public interface Anonymous {}
public interface Guest extends Anonymous {}
public interface Organizer extends Guest {}
public interface BusinessAdmin extends Organizer {}
public interface TechnicalAdmin extends BusinessAdmin {}
}
@JsonView
在您的实体中声明,例如
@Entity
public class SomeEntity {
@JsonView(View.Anonymous.class)
String anonymousField;
@JsonView(View.BusinessAdmin.class)
String adminField;
}
并定义一个根据角色@ControllerAdvice
选择权利:JsonView
@ControllerAdvice
public class JsonViewConfiguration extends AbstractMappingJacksonResponseBodyAdvice {
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return super.supports(returnType, converterType);
}
@Override
protected void beforeBodyWriteInternal(MappingJacksonValue bodyContainer, MediaType contentType,
MethodParameter returnType, ServerHttpRequest request, ServerHttpResponse response) {
Class<?> viewClass = View.Anonymous.class;
if (SecurityContextHolder.getContext().getAuthentication() != null && SecurityContextHolder.getContext().getAuthentication().getAuthorities() != null) {
Collection<? extends GrantedAuthority> authorities = SecurityContextHolder.getContext().getAuthentication().getAuthorities();
if (authorities.stream().anyMatch(o -> o.getAuthority().equals(Role.GUEST.getValue()))) {
viewClass = View.Guest.class;
}
if (authorities.stream().anyMatch(o -> o.getAuthority().equals(Role.ORGANIZER.getValue()))) {
viewClass = View.Organizer.class;
}
if (authorities.stream().anyMatch(o -> o.getAuthority().equals(Role.BUSINESS_ADMIN.getValue()))) {
viewClass = View.BusinessAdmin.class;
}
if (authorities.stream().anyMatch(o -> o.getAuthority().equals(Role.TECHNICAL_ADMIN.getValue()))) {
viewClass = View.TechnicalAdmin.class;
}
}
bodyContainer.setSerializationView(viewClass);
}
}
更新:新答案
您应该考虑使用rkonovalov/jfilter。特别@DynamicFilterComponent
有很大帮助。你可以在这篇DZone文章中看到一个很好的指南。
@DynamicFilterComponent
在这里解释。
旧答案
我刚刚实现了您上面提到的要求。我的系统使用 Restful Jersey 1.17
, Spring Security 3.0.7
, Jackson 1.9.2
. 但是该解决方案与 Jersey Restful API 无关,您可以在任何其他类型的 Servlet 实现中使用它。
这是我的解决方案的全部 5 个步骤:
首先,您应该为您的目的创建一个 Annotation 类,如下所示:
JsonSpringView.java
import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) public @interface JsonSpringView { String springRoles(); }
然后是一个注解内省,它的大部分方法应该返回
null
,根据您的需要填写方法,因为我刚刚使用了我的要求isIgnorableField
。Feature
是我对 GrantedAuthority 接口的实现。像这样:JsonSpringViewAnnotationIntrospector.java
@Component public class JsonSpringViewAnnotationIntrospector extends AnnotationIntrospector implements Versioned { // SOME METHODS HERE @Override public boolean isIgnorableField(AnnotatedField) { if(annotatedField.hasAnnotation(JsonSpringView.class)) { JsonSpringView jsv = annotatedField.getAnnotation(JsonSpringView.class); if(jsv.springRoles() != null) { Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); if(principal != null && principal instanceof UserDetails) { UserDetails principalUserDetails = (UserDetails) principal; Collection<? extends GrantedAuthority> authorities = principalUserDetails.getAuthorities(); List<String> requiredRoles = Arrays.asList(jsv.springRoles().split(",")); for(String requiredRole : requiredRoles) { Feature f = new Feature(); f.setName(requiredRole); if(authorities.contains(f)) // if The Method Have @JsonSpringView Behind it, and Current User has The Required Permission(Feature, Right, ... . Anything You may Name It). return false; } // if The Method Have @JsonSpringView Behind it, but the Current User doesn't have The required Permission(Feature, Right, ... . Anything You may Name It). return true; } } } // if The Method Doesn't Have @JsonSpringView Behind it. return false; } }
Jersey 服务器有一个默认
ObjectMapper
的序列化/反序列化目的。如果你正在使用这样的系统并且你想改变它的默认 ObjectMapper,步骤 3、4 和 5 是你的,否则你可以阅读这一步,你的工作就在这里完成。JsonSpringObjectMapperProvider.java
@Provider public class JsonSpringObjectMapperProvider implements ContextResolver<ObjectMapper> { ObjectMapper mapper; public JsonSpringObjectMapperProvider() { mapper = new ObjectMapper(); AnnotationIntrospector one = new JsonSpringViewAnnotationIntrospector(); AnnotationIntrospector two = new JacksonAnnotationIntrospector(); AnnotationIntrospector three = AnnotationIntrospector.pair(one, two); mapper.setAnnotationIntrospector(three); } @Override public ObjectMapper getContext(Class<?> arg0) { return this.mapper; } }
您应该在 Web.xml 中扩展
javax.ws.rs.core.Application
并提及您的班级名称。我的是 RestApplication。像这样:RestApplication.java
import java.util.HashSet; import java.util.Set; import javax.ws.rs.core.Application; public class RestApplication extends Application { public Set<Class<?>> getClasses() { Set<Class<?>> classes = new HashSet<Class<?>>(); classes.add(JsonSpringObjectMapperProvider.class); return classes ; } }
这是最后一步。您应该在 web.xml 中提及您的 Application 类(来自第 4 步):
我的 web.xml 的一部分
<servlet> <servlet-name>RestService</servlet-name> <servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class> <init-param> <param-name>com.sun.jersey.config.property.package</param-name> <param-value>your_restful_resources_package_here</param-value> </init-param> <init-param> <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name> <param-value>true</param-value> </init-param> <!-- THIS IS THE PART YOU SHOULD PPPAYYY ATTTTENTTTTION TO--> <init-param> <param-name>javax.ws.rs.Application</param-name> <param-value>your_package_name_here.RestApplication</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
并且从现在开始你只需要在任何你想要的属性后面提到@JsonSpringView 注解。像这样:
PersonDataTransferObject.java
public class PersonDataTransferObject
{
private String name;
@JsonSpringView(springRoles="ADMIN, SUPERUSER") // Only Admins And Super Users Will See the person National Code in the automatically produced Json.
private String nationalCode;
}
尽管可以编写自定义 JSON 处理过滤器(例如基于JSON 指针),但它会有点复杂。
最简单的方法是创建您自己的 DTO 并仅映射用户有权获取的那些属性。