【问题标题】:Enabling OAuth1 Support on a Jersey Jax-rs web service在 Jersey Jax-rs Web 服务上启用 OAuth1 支持
【发布时间】:2014-03-14 23:09:28
【问题描述】:

我想在我的 RESTful Web 服务上启用 OAuth1 Provider 支持。泽西岛支持这一点,如 Jersey OAuth1 Provider support 所述。 我一直在尝试这样注册:

public ApplicationConfig(){
    super();
    addRestResourceClasses(getMyResourceClasses());                 
    register(new OAuth1ServerFeature(new DefaultOAuth1Provider(),"/oauth/access_token","/oauth/request_token"));
}

但是,当我注册 OAuth1ServerFeature 时,我在尝试访问我的资源时收到 404。 似乎在任何地方都找不到任何实现 jersey oauth 支持的示例/教程!

是否有一个简单的组件可以插入到我的 jax-rs 服务中以启用 oauth 支持?

【问题讨论】:

    标签: authentication oauth oauth-2.0 jersey jax-rs


    【解决方案1】:

    我意识到这个帖子有点老了 - 但我自己刚刚开始工作,我觉得回复是为了!如果有时间,我什至可以创建一个包含更完整示例的博客文章。请注意 - 这不是一个简短的答案!

    关于在泽西岛使用 OAuth1 服务器(又名提供者)功能的信息示例绝对缺乏 - 我不记得有一个技术主题显示了如此少的有用 Google 信息。我几乎放弃了寻找另一种解决方案,因为它让我认为它可能不起作用。但是,只要有毅力,我可以说它不仅可用,而且似乎工作得很好。当然,如果你已经在使用 Jersey 作为你的 REST API - 你不需要任何额外的库。

    我不是 OAuth1 专家 - 我强烈建议那些尝试这样做的人阅读一些背景知识。我在这里也假设你有 Jersey 工作,了解 ContainerRequestFilters 之类的东西,并且还有一些内部手段来授权用户。

    我的示例还使用了出色的 JAX-RS OSGi 连接器 - 唯一真正的区别是,我们使用 OSGi 捆绑上下文通过 OSGI 服务注册 OAuth1 功能,普通 Jersey 用户将需要通过他们的正常应用程序/服务器配置模型。

    初始化

    您必须创建您的 OAuth1 功能 - 并为其提供提供者:

    DefaultOAuth1Provider oap = new DefaultOAuth1Provider();
    Feature oaFeature = new OAuth1ServerFeature(oap, "oauth1/request_token", "oauth1/access_token");       
    

    别忘了将 oaFeature 注册到 Jersey!

    DefaultOAuth1Provider 完全基于内存 - 这对我们来说很好。许多人希望保留访问令牌以在服务器重新启动时使用,这需要扩展子类(或干净的实现)

    添加您的消费者密钥和秘密

    我花了一段时间才意识到消费者不是用户而是客户,即应用程序。如果您不为希望连接的每个消费者(也称为客户端应用程序)注册密钥和秘密,则 Jersey 实现将无法工作

    oap.registerConsumer("some-owner-id", 
                          "abcdef" ,
                          "123456",
                          new MultivaluedHashMap<String,String> ());
    

    您显然永远不会对这些内容进行硬编码,并且还会对秘密使用某种形式的安全存储(参数 3)。

    如果您不添加这些,您将无法获得任何进一步的信息。

    OAuth 协议第 1 步 - 获取请求令牌

    在这个阶段,您已经准备好客户端获取请求令牌 - 这里有一个 perfectly good example on GitHub

    ConsumerCredentials consumerCredentials = new ConsumerCredentials("abcdef","123456");
    
    //TODO - user proper client builder with real location + any ssl context
    OAuth1AuthorizationFlow authFlow = OAuth1ClientSupport.builder(consumerCredentials)
                .authorizationFlow(
                        "http://myhost:8080/myapi/oauth1/request_token",
                        "http://myhost:8080/myapi/oauth1/access_token",
                        "http://myhost:8080/myapi/oauth1/authorize")
                .build();
    String authorizationUri = authFlow.start();
    System.out.println("Auth URI: " + authorizationUri); 
    

    显然,您会更改 URL 以指向您的服务器,并且 - 至关重要的是 - 客户端需要使用您在服务器中注册的相同 Conumer Key 和 Secret。

    您将收到包含 oauth_token 字符串的响应,例如

    http://myhost:8080/myapi/oauth/authorize?oauth_token=a1ec37598da
    b47f6b9d770b1b23a5f99
    

    OAuth 协议第 2 步 - 授权用户

    正如您将在任何文章中看到的那样,实际用户授权超出了 OAuth1 的范围 - 在此阶段,您必须调用服务器的身份验证过程。

    但是!!!!如果用户授权成功,您的服务器需要执行的操作不在 OAuth1 范围之外。您必须告诉您的 DefaultOAuth1Provider 成功的身份验证:

    // Dummy code - make out like we're auth'd
    Set<String> dummyRoles = new HashSet<> (Arrays.asList( new String[] { "my-role-1", "my-role-2" }));
    DefaultOAuth1Provider.Token tok1 = getRequestToken("a1ec37598da
    b47f6b9d770b1b23a5f99");
    String verifier = authorizeToken(tok1, new Principal()
                {
                    public String getName()
                    {
                        return "my-user";
                    }
                }, 
                dummyRoles);
    System.out.println("***** verifier: " + verifier);
    

    请注意,请求令牌字符串是步骤 1 中的字符串。显然,真正的实现会为授权用户传递一个真正的 Principal 和一组角色。

    当然,打印出验证器并没有多大用处 - 您需要通过独立渠道或可能作为身份验证响应中的标头以某种方式将其返回给您的客户 - 这可能需要加密以增加保护。

    OAuth 协议第 3 步 - 将请求令牌换成访问令牌

    一旦客户端收到或手动输入验证者,它就可以完成流程并将请求令牌交换为访问令牌,例如

    String verifier = System.console().readLine("%s", "Verifier: ");
    final AccessToken accessToken = authFlow.finish(verifier);        
    System.out.println("Access token: " + accessToken.getToken());      
    

    同样,这不是一个现实的例子 - 但它展示了这个过程。

    如果您的 OAuth1Provider 将访问令牌保存到服务器上的某个持久存储区,您可以在以后的会话中重复使用此处返回的任何访问令牌,而无需执行所有前面的步骤。

    就是这样 - 然后您只需要确保客户端在此过程中创建的每个请求都使用该访问令牌。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-09-01
      • 1970-01-01
      • 2016-12-21
      • 2014-06-20
      • 2013-10-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多