我正在忙于一个需要REST API的项目,以允许用户访问服务器上的信息。我让系统使用Spring MVC和MappingJacksonJsonView
在需要的地方返回JSON。
我现在想在系统中包括安全性,首先,确保用户进行身份验证并具有访问资源的正确权限,其次,我希望对数据进行足够的细粒度控制,以确保用户只能访问公开可用的资源。资源的一部分,或者如果它们具有正确的权限,则整个资源。
因此,例如:
方案A:用户A想要访问他们的消息列表以及他们的姓名和电话号码。然后,他们将获得身份验证,其权限将允许他们访问自己的所有信息。
方案B:用户A想要访问用户B的电话号码-因此在响应中排除了用户B的消息列表,因为用户A没有访问该信息部分的权限。
Q1:有没有整齐的方法
关于使用Apache Shiro做到这一点
和Spring MVC?
Q2:有人吗
有例子或链接到教程
关于其他人的成就
这个?
Q3:什么样的权限
方案,使用Shiro,将是最
有效地允许这种罚款
细粒度的控制?
我以前没有与Shiro一起工作过,但是通过玩示例和阅读文档,看来这是可能的,并且Shiro将是最适合该解决方案的。我也欢迎其他解决方案。
编辑:一种解决方案,我不知道Shiro&Jackson是否可行,是对我可以标记为公共或私有的POJO中的属性进行注释。甚至更好,将它们标记为具有访问它们所需的权限。然后,当杰克逊打印出对象的JSON表示形式时,它可以检查当前属性的权限,并从其注释中决定是否打印该属性。
最佳答案
解决此问题的方法非常简单。我为杰克逊创建了一组视图类:
public class SecureViews {
public static class Public{};
public static class Authenticated extends Public{};
public static class User extends Authenticated{};
public static class Internal extends User {};
}
然后,通过添加以下内容,为要保护的每个实体注释所有的getter方法:
@JsonView(SecureViews.Authenticated.class)
public String getPhoneNumber() {
return phoneNumber;
}
上述规则的意思是,只有在系统内通过身份验证的用户才能查看用户的电话号码。
要么
@JsonView(SecureViews.User.class)
public List<Event> getEvents() {
return Collections.unmodifiableList(events);
}
然后,我创建了一个接口,并在我所有的安全实体中实现了该接口:
public interface SecurePropertyInterface {
Class<?> getMaxPermissionLevel(Long userID);
}
这是该方法的实现:
public Class<?> getMaxPermissionLevel(Long userID) {
if (userID != null && userID == uid) {
return SecureViews.User.class;
}
if (SecurityUtils.getSubject().isAuthenticated()) {
return SecureViews.Authenticated.class;
}
return SecureViews.Public.class;
}
现在为魔术。扩展
MappingJacksonJsonView
并覆盖以下方法:@Override
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request,
HttpServletResponse response) throws Exception {
Long userID = CTConversionUtils.convertToLong(request.getParameter("id"));
model = (Map<String, Object>) super.filterModel(model);
Class<?> viewClass = SecureViews.Public.class;
if(SecurityUtils.getSubject().isAuthenticated()) {
viewClass = SecureViews.Authenticated.class;
}
for (Entry<String, Object> modelEntry : model.entrySet()) {
if (modelEntry.getValue() instanceof SecurePropertyInterface) {
viewClass = ((SecurePropertyInterface)modelEntry.getValue()).getMaxPermissionLevel(userID);
}
}
objectMapper.viewWriter(viewClass).writeValue(getJsonGenerator(response), model);
}
当Spring设置
setObjectMapper
时,您也可以重写MappingJacksonJsonView
方法来捕获objectMapper。我的Spring配置看起来像这样:
<bean
class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver"
p:order="1">
<property name="mediaTypes">
<map>
<entry key="json" value="*/*" />
</map>
</property>
<property name="defaultViews">
<list>
<bean
class="com.bytesizecreations.connecttext.json.SecureMappingJacksonJsonView">
<property name="objectMapper">
<bean
class="org.codehaus.jackson.map.ObjectMapper" />
</property>
</bean>
</list>
</property>
</bean>
那我们现在在这里有什么?每次需要将响应反序列化为JSON时,都会调用renderMergedOutputModel方法。如果用户的ID在请求中,我们将其存储。然后,我们可以遍历模型映射并检查每个值是否为SecurePropertyInterface,以及是否获得其最大权限级别。
这个策略似乎正确地达到了我的目的。当用户请求用户列表时,他们只会获得经过身份验证的属性,但是当用户请求自己的详细信息时,他们只会获得其私有属性。
我怀疑此代码可以进一步改进,但是我花了太多时间试图使其变得不那么复杂。
关于java - Spring MVC,RESTful服务和Apache Shiro策略,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/5388687/