2011-03-22 88 views
2

我正忙于开发一个需要REST API的项目,以允许用户访问服务器上的信息。我有系统使用Spring MVC和​​在需要时返回JSON。Spring MVC,REST风格的服务和Apache Shiro策略

我现在想要在系统中包含安全性,首先要确保用户对自己进行身份验证并拥有访问资源的正确权限,其次我希望对数据有足够的细粒度控制以确保用户能够只访问资源的公共可用部分,或者如果他们拥有整个资源的正确权限。

因此,例如:

场景A:用户A想访问他们的邮件列表,以及他们的名字和电话号码。然后他们会得到身份验证,他们的权限将允许他们访问他们自己的所有信息。

情景B:用户A想要访问用户B的电话号码 - 因此排除用户B的响应中的消息列表,因为A没有权限访问该部分信息。

  • Q1:有没有要去 有关执行此操作使用Apache四郎 和Spring MVC一种巧妙的方法?
  • Q2:是否有人 对如何别人取得 这样的例子或链接到一个教程 ?
  • Q3:什么样的权限 方案,使用四郎的,将是最有效的 允许这种类型的细 细粒度控制的?

我以前没有和Shiro一起工作过,但是从玩例子和阅读文档来看,这看起来像是可能的,而且Shiro最适合这个解决方案。尽管我接受其他解决方案。

编辑:一个解决方案,我不知道这是否可能与四郎&杰克逊,是标注在我的POJO的属性,我可以标记为公共或私人。或者更好的是,用访问它们所需的许可标记它们。然后,当杰克逊打印出对象的JSON表示时,它可以检查当前属性的权限,并决定是否从其注释打印属性。

回答

1

这个问题的解决方案非常简单。我创建了杰克逊一组视图类:

@JsonView(SecureViews.Authenticated.class) 
public String getPhoneNumber() { 
    return phoneNumber; 
} 

什么上面的规则的意思是:

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.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; 
    } 

现在的魔法。扩展​​并覆盖下面的方法:

@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); 
    } 

您也可以覆盖setObjectMapper方法来捕获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时,都会调用renderedMergedOutputModel方法。如果用户的ID在请求中,我们将其存储。然后,我们可以遍历模型映射并检查每个值是否为SecurePropertyInterface,以及它是否获得其最大权限级别。

这个策略似乎正确地服务于我的目的。当用户请求用户列表时,他们只能获得已认证的属性,但当用户请求自己的详细信息时,他们只能获得其私有属性。

虽然我怀疑这个代码可以进一步改进,但是我花了太多的时间试图让它变得更简单。

+0

你可以做'Class ',并取消原始类型警告。 – 2011-03-30 06:52:04

+0

谢谢。我已经添加了它。 – 2011-03-30 15:45:42