经过身份验证的REST请求是否始终意味着数据库请求?

时间:2012-01-18 20:49:40

标签: java google-app-engine rest basic-authentication objectify

我正在使用RESTeasy框架来开发我的Web服务。我已经设法建立BASIC身份验证,现在它正常工作。当然,我计划在此基础上使用SSL。

这个过程很简单(如果您不知道这是什么,请关于HTTP基本身份验证read something):

  1. 每个请求都会被分析请求标头的方法截获。
  2. 解码此标头并提取用户名和密码。
  3. 然后查询数据库以检查用户名和密码是否匹配。
  4. 如果匹配请求继续,如果不匹配,则返回401代码。
  5. 使用这种方法,由于REST(和HTTP本身)的无状态特性,每个请求都意味着对数据库的请求。

    我的问题是:是否可以不在每个经过身份验证的请求中查询数据库?

    可能的提示:使用cookie的一些机制?

    这个问题在技术上是不可知的。


    正如旁注:

    我真的觉得这个REST身份验证问题的信息非常少。它只是OAuth,OAuth,OAuth ......如果我们不想验证第三方应用程序,那么信息就会分散在各处,而且没有任何具体的例子,比如使用OAuth。 如果您对REST WebServices中的身份验证有任何好的建议,我很乐意听到。

    谢谢。

5 个答案:

答案 0 :(得分:1)

有各种方法可以使用Cookie实现“Auth Ticket”(google,你可能会发现一些非OAuth引用),这样每个请求都不需要数据库查询。但是,这些通常涉及加密密钥。

我不相信最佳实践应该是将加密密钥存储在源文件中(但这是教程通常实现的方式),因此您可能涉及某种磁盘访问(通过属性文件,密钥库等) )即使你不查询数据库。

正如Perception所述,将cookie(状态)添加到无状态系统设计中是一种欺骗行为。

答案 1 :(得分:1)

使用memcached之类的东西作为数据库的中间件。检查凭据是否已缓存,是否继续请求,如果未缓存,请从数据库中提取凭据并验证凭据。如果凭据匹配,则将其缓存以供将来请求使用,并继续使用当前请求。

请记住,对memchaced的访问必须与访问数据库一样安全,因为它存储了密码。这是许多网站使用OAuth的原因之一,尤其是OAuth 2,您可以在其中分发一个短期访问令牌和一个长期存在的刷新令牌,以便在需要时获取新的访问令牌。

答案 2 :(得分:1)

欢迎来到具有代表性的国家转移安全世界!您知道,我向Roy Fielding发送了一条消息,询问您如何创建一个真正的RESTful服务,这也是安全的。他还没回到我身边。

您的两个选项实际上是Basic Auth(希望使用SSL,或者重点是什么),或OAuth。对于我目前参与的所有解决方案,我们都在使用OAuth。虽然它使测试变得复杂,但它非常安全,并且允许我们从我们的服务中创建可外部化的API。

我建议不要使用cookie来存储会话信息。这不仅违反了REST的精神,而且还打开了会话劫持的应用程序。您可以采取的一件事就是加快速度,保持一个良好的二级缓存和用户信息,这样您的查询就不会为每个传入的请求实际访问数据库。这可以提高 显着的 速度。这种策略同样适用于基本的auth和Oauth。

答案 3 :(得分:1)

如果您与AppEninge的UserService集成(以及与Google帐户一样),那么您可以阻止任何查询。 RESTlet有一个超级优雅的身份验证器,随框架一起提供:

public class GaeAuthenticator extends Authenticator {
    /**
     * The GAE UserService that provides facilities to check whether a user has
     * authenticated using their Google Account
     */
    private UserService userService = UserServiceFactory.getUserService();

    /**
     * Constructor setting the mode to "required".
     * 
     * @param context
     *            The context.
     * @see #Authenticator(Context)
     */
    public GaeAuthenticator(Context context) {
        super(context);
    }

    /**
     * Constructor using the context's default enroler.
     * 
     * @param context
     *            The context.
     * @param optional
     *            The authentication mode.
     * @see #Authenticator(Context, boolean, Enroler)
     */
    public GaeAuthenticator(Context context, boolean optional) {
        super(context, optional);
    }

    /**
     * Constructor.
     * 
     * @param context
     *            The context.
     * @param optional
     *            The authentication mode.
     * @param enroler
     *            The enroler to invoke upon successful authentication.
     */
    public GaeAuthenticator(Context context, boolean optional, Enroler enroler) {
        super(context, optional, enroler);
    }

    /**
     * Integrates with Google App Engine UserService to redirect
     * non-authenticated users to the GAE login URL. Upon successful login,
     * creates a Restlet User object based values in GAE user object. The GAE
     * "nickname" property gets mapped to the Restlet "firstName" property.
     * 
     * @param request
     *            The request sent.
     * @param response
     *            The response to update.
     * @return True if the authentication succeeded.
     */
    @Override
    protected boolean authenticate(Request request, Response response) {
        ClientInfo info = request.getClientInfo();
        if (info.isAuthenticated()) {
            // The request is already authenticated.
            return true;
        } else if (userService.isUserLoggedIn()) {
            // The user is logged in, create restlet user.
            com.google.appengine.api.users.User gaeUser = userService
                    .getCurrentUser();
            User restletUser = new User(gaeUser.getUserId());
            restletUser.setEmail(gaeUser.getEmail());
            restletUser.setFirstName(gaeUser.getNickname());
            info.setUser(restletUser);
            info.setAuthenticated(true);
            return true;
        } else {
            // The GAE user service says user not logged in, let's redirect him
            // to the login page.
            String loginUrl = userService.createLoginURL(request
                    .getOriginalRef().toString());
            response.redirectTemporary(loginUrl);
            return false;
        }
    }
}

答案 4 :(得分:0)

答案最终是缓存

在我的特定情况下,我使用RESTeasy作为REST框架,使用Google App Engine作为Application Server。不难发现GAE支持memcache

如果您正在使用Objectify(您真的应该这样做,那真棒),那就更容易了。只需使用 @Cached 注释您的实体类。此过程如here所示。

Objectify支持会话对象中的另一种缓存。换句话说,只要你的Objectify对象被实例化,它甚至可以在不使用memcache的情况下提供你的对象(这很好,因为在GAE中有使用memcache的配额,尽管它们比数据存储更便宜)。我强烈建议你阅读Objectify在他们的wiki中的良好做法。

作为最后一点,我会考虑使用摘要式身份验证而不是 Basic 。它似乎更安全。密码永远不会通过网络传播的事实让我感到宽慰。

我希望这个问题对某些人和帮助我的人有用:谢谢。 :)