【问题标题】:Spring Security Oauth2 - InsufficientAuthenticationException: Authentication is required to obtain an access token (anonymous not allowed)Spring Security Oauth2 - InsufficientAuthenticationException:需要进行身份验证才能获取访问令牌(不允许匿名)
【发布时间】:2017-05-13 20:09:32
【问题描述】:

我试图了解为什么客户端中的匿名用户无法获得访问令牌。

我发现这个 post on the Spring Blog 关于这个话题,Dave Syer 回答了这个问题:

请记住,这是与客户端应用程序有关的问题,而不是身份验证问题 服务器,所以试着从客户的角度来看它。在 步骤 1. 有一个用户试图访问受保护的资源。 如果你 无法识别该用户,那么您的所有用户最终都会得到相同的 访问令牌(第一个用户使用他的 身份验证服务器上的凭据)。这绝对是个坏主意。

但是我不明白为什么匿名用户会共享同一个令牌:

    Authentication auth = SecurityContextHolder.getContext().getAuthentication();

    if (auth instanceof AnonymousAuthenticationToken) {
        if (!resource.isClientOnly()) {
            throw new InsufficientAuthenticationException(
                    "Authentication is required to obtain an access token (anonymous not allowed)");
        }
    }

虽然它们没有完全通过身份验证,但它们有一个绑定到每个匿名用户的 JSESSIONID。

包含 accessToken 的OAuth2ClientContext 是一个会话范围的 Spring bean。由于匿名用户有一个 HttpSession,他们有独立的OAuth2ClientContexts,因此他们可以存储个人 accessToken。

有人能解释一下这是有道理的还是我不理解的?

【问题讨论】:

    标签: spring spring-security oauth oauth-2.0 spring-security-oauth2


    【解决方案1】:

    我相信这实际上与会话无关。 Dave 可能意味着您在 spring 上下文中只能有一个匿名用户。对我来说,为什么会有这个 anonymous user 术语一直是个谜,但它的故事不同。

    查看JdbcClientTokenServices类中方法getAccessToken的代码

    public OAuth2AccessToken getAccessToken(OAuth2ProtectedResourceDetails resource, Authentication authentication) {
    
        OAuth2AccessToken accessToken = null;
    
        try {
            accessToken = jdbcTemplate.queryForObject(selectAccessTokenSql, new RowMapper<OAuth2AccessToken>() {
                public OAuth2AccessToken mapRow(ResultSet rs, int rowNum) throws SQLException {
                    return SerializationUtils.deserialize(rs.getBytes(2));
                }
            }, keyGenerator.extractKey(resource, authentication));
        }
        catch (EmptyResultDataAccessException e) {
            if (LOG.isInfoEnabled()) {
                LOG.debug("Failed to find access token for authentication " + authentication);
            }
        }
    
        return accessToken;
    }
    

    它在数据库中使用序列化身份验证。因此,即使在不同的会话中,它也会使用相同的匿名用户来加载令牌,从而有效地加载相同的令牌。

    authorization_code oauth2 流程中,您需要code 来获取令牌(所以第一步是请求代码,只有在授权用户后才会给出代码 - 在此步骤中,用户可能会被重定向到 facebook)。第二步是通过提供code 来获取tokencode 在 db 中有有限的授权,所以 spring 知道谁是新令牌的所有者:

    \d+ oauth_code;
                                                         Table "mydb.oauth_code"
         Column     |          Type          |                        Modifiers                        | Storage  | Stats target | Description
    ----------------+------------------------+---------------------------------    ------------------------+----------+--------------+-------------
     id             | integer                | not null default     nextval('oauth_code_id_seq'::regclass) | plain    |              |
     authentication | bytea                  |                                                         | extended |              |
     code           | character varying(255) |                                                         | extended |              |
    

    因此,总而言之,如果您要使用相同的令牌填充 OAuth2ClientContext 范围为 sessionrequest 将无济于事。

    【讨论】:

    • 感谢您花时间澄清这一点。你是绝对正确的,从我们配置JdbcClientTokenServices 来持久化令牌的那一刻起,由于selectAccessTokenSql 查询使用Authentication.getName()(这将是anomymousUser),所有用户都将共享令牌。但是,只要我们不保留令牌就不会有问题,我仔细检查了OAuth2ClientContext 是会话范围的,并且每个客户端都在 HttpSession 中保留自己的 accessToken。查询可以实现为使用 session.id 而不是 Authorization.getName()...
    • 可能还早.. 我不记得看到没有持久层的 oauth2 身份验证服务器。如果不存储令牌,您将如何检查从客户端发送的令牌是否有效?你会如何将它与任何东西进行比较?您将如何撤销/刷新它?也许您需要 JWT 令牌而不是 oauth2 持有者 :)
    • 可能我没有解释清楚...当然我们需要服务器来存储令牌,我指的是客户端令牌持久化,这是通过JdbcClientTokenServices完成的,它是可选的.
    猜你喜欢
    • 2016-11-01
    • 1970-01-01
    • 2015-01-08
    • 2018-03-03
    • 2015-10-09
    • 2019-10-24
    • 2019-08-19
    • 2017-08-03
    • 2013-06-12
    相关资源
    最近更新 更多