Spring MVC,RESTful Services& Apache Shiro战略

时间:2011-03-22 08:45:29

标签: java spring rest spring-mvc shiro

我正在忙于一个需要REST API的项目,以允许用户在服务器上访问他们的信息。我让系统使用Spring MVC和MappingJacksonJsonView在需要的地方返回JSON。

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

例如:

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

场景B:用户A想要访问用户B的电话号码 - 因此在答复中排除用户B的消息列表,因为A无权访问该部分信息。

  • Q1:有一种干净利落的方式 关于使用Apache Shiro执行此操作 和Spring MVC?
  • Q2:有没有人 有一个教程的示例或链接 关于其他人如何取得的成就 这个?
  • 问题3:什么样的权限 计划,使用Shiro,将是最多的 有效地允许这种类型的罚款 粒度控制?

我以前没有和Shiro合作过,但是通过阅读示例并阅读文档,看起来这是可能的,并且Shiro最适合解决方案。我对其他解决方案持开放态度。

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

1 个答案:

答案 0 :(得分:1)

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

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。

My 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,以及是否获得其最大权限级别。

这个策略似乎适合我的目的。当用户请求用户列表时,他们只获得经过身份验证的属性,但是当用户请求他们自己的详细信息时,他们只获得他们的私有属性。

我怀疑这段代码可以进一步改进,但是我花了太多时间试图让它变得不那么复杂。