【问题标题】:How to make Spring Security OAuth2 really stateless / get rid of "state" parameter?如何使 Spring Security OAuth2 真正无状态/摆脱“状态”参数?
【发布时间】:2016-03-21 07:01:54
【问题描述】:

我目前正在开展一个项目,我们希望用户通过 Facebook 和其他 OAuth2 提供商登录。此外,REST api 应该是无状态的。因此,不应创建/使用任何 cookie/jsessionid。对于 api 授权,通过 Facebook 成功登录后,api 会发出 JWT。使用其余 api 的 webapp 是使用 AgularJS 和 satellizer 构建的。我将代码缩减为minimal example available on github

工作流程思路:

  1. 用户进入网站,选择“用facebook登录”
  2. Web 应用程序打开一个弹出窗口,显示 facebook 登录页面
  3. 用户登录、接受和 facebook 重定向到 web 应用程序
  4. webapp 从 Facebook 接收令牌并使用它来登录其余 api (GET /login/facebook?code=XXXXXXXXXXXXXXXXXX)
  5. rest api 返回一个 JWT。
  6. webapp 使用 JWT 授权其余 api 以进行任何进一步的请求。

工作至今

  • webapp 可以处理步骤 1 到 4
  • 如果您使用从 OAuth2ClientContextFilter 获得的重定向执行 GET 到“/login/facebook”而不使用 code 参数,其余 api 可以处理步骤 5 和 6。 (此重定向会转到 facebook,您登录后会再次重定向到 api。)
  • 其余 api 是无状态的,没有创建/需要 jsessionid/cookies(禁​​用 csrf 并使用 SessionCreationPolicy.STATELESS)。 除了“login/facebook”调用之外,这个调用仍在创建 jsessionid。

问题

webapp 和其余 api 的“login/facebook?code=XXX”调用的组合不起作用。我发现当您执行 GET “login/facebook”时,您将被重定向到 facebook,并在 url 上附加了一个额外的状态参数。此外,当 facebook 重定向回 api 时,也会添加此状态参数。从我在网上查到的,这似乎是一种 CSRF 保护,对吧?而且我猜这个东西也在创建 jsessionid cookie?

问题

  1. 上面介绍的工作流程是一个合理的想法吗?
  2. 我的用例中是否需要这种 CSRF 保护?
  3. 如何禁用此行为?我的意思是,我使用了 SessionCreationPolicy.STATELESS 但 spring 仍然使用 jsessionid 创建一个会话。那我怎样才能创建一个真正无状态的rest api呢? (至少关于 cookie...)
  4. 这是正确的做法吗?还是我错过了什么?

示例代码

如前所述,我在 GitHub 上放了一个完整的工作最小示例。在这里,我将只发布(希望是)WebSecurityConfigurerAdapter 最重要的部分。 complete file is here.

@EnableOAuth2Client
@Configuration
public class OAuth2ClientConfigurer extends WebSecurityConfigurerAdapter {

@Autowired
private OAuth2ClientContext oAuth2ClientContext;

@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
    httpSecurity
            .csrf().disable()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).enableSessionUrlRewriting(false).and()
            .antMatcher("/**").authorizeRequests()
            .antMatchers("/login/**").permitAll()
            .anyRequest().authenticated().and()
            .exceptionHandling().authenticationEntryPoint(new Http403ForbiddenEntryPoint()).and()
            .addFilterBefore(statelessJwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
            .addFilterBefore(createSsoFilter(facebook(), facebookSuccessHandler(), "/login/facebook"), BasicAuthenticationFilter.class);
}

private OAuth2ClientAuthenticationProcessingFilter createSsoFilter(ClientResourceDetails clientDetails, AuthenticationSuccessHandler successHandler, String path) {
    OAuth2ClientAuthenticationProcessingFilter ssoFilter = new OAuth2ClientAuthenticationProcessingFilter(path);
    ssoFilter.setAllowSessionCreation(false);
    OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(clientDetails.getClient(), oAuth2ClientContext);
    ssoFilter.setRestTemplate(restTemplate);
    ssoFilter.setTokenServices(new UserInfoTokenServices(clientDetails.getResource().getUserInfoUri(), clientDetails.getClient().getClientId()));
    ssoFilter.setAuthenticationSuccessHandler(successHandler);
    return ssoFilter;
}

@Bean // handles the redirect to facebook
public FilterRegistrationBean oAuth2ClientFilterRegistration(OAuth2ClientContextFilter filter) {
    FilterRegistrationBean registration = new FilterRegistrationBean();
    registration.setFilter(filter);
    registration.setOrder(-100);
    return registration;
}

非常感谢您的帮助!

【问题讨论】:

  • 你弄明白了吗?我的应用程序结构有些不同,但我遇到了同样的问题。
  • 你找到让它无状态的方法了吗?

标签: spring jwt spring-security-oauth2 jsessionid stateless


【解决方案1】:

这是一个可选但推荐的 OAuth 2.0 功能。它由授权服务器强制执行,并且您认为其目的是防止 csrf 攻击。

从 OAuth 2.0 RFC 复制:

state
     RECOMMENDED.  An opaque value used by the client to maintain
     state between the request and callback.  The authorization
     server includes this value when redirecting the user-agent back
     to the client.  The parameter SHOULD be used for preventing
     cross-site request forgery as described in Section 10.12.

https://www.rfc-editor.org/rfc/rfc6749#section-4.1.1

【讨论】:

    猜你喜欢
    • 2016-11-09
    • 2016-06-09
    • 1970-01-01
    • 2013-03-21
    • 2016-01-11
    • 2021-05-12
    • 1970-01-01
    • 1970-01-01
    • 2017-04-20
    相关资源
    最近更新 更多