如何使Spring Security OAuth2真正无国籍/摆脱" state"参数

时间:2015-12-15 13:04:54

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

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

工作流程理念:

  1. 用户进入网站,选择"使用facebook"
  2. 登录
  3. 网络应用程序会打开一个显示Facebook登录页面的弹出窗口
  4. 用户登录,接受和Facebook重定向到webapp
  5. 网络应用程序从Facebook接收令牌并使用它登录到其余的api(GET / login / facebook?code = XXXXXXXXXXXXXXXXX)
  6. 其余的api返回JWT。
  7. webapp使用JWT对其余api进行授权以获取任何进一步的请求。
  8. 到目前为止工作

    • webapp可以处理步骤1到4
    • 如果您使用从OAuth2ClientContextFilter执行GET的重定向到" / login / facebook"其余api可以处理步骤5和6。没有代码参数。 (此重定向发送到Facebook,您登录后,您将再次重定向到api。)
    • 其余的api是无状态的,没有创建/需要jsessionid / cookies(禁​​用csrf并使用SessionCreationPolicy.STATELESS)。 除了登录/脸书"打电话,这个还在创造一个jsessionid。

    问题

    webapp和其他api的组合" login / facebook?code = XXX"电话不起作用。我发现当你进行GET" login / facebook"时,你将被重定向到facebook,并在网址上附加一个附加状态参数。此外,当facebook重定向回api时,还会添加此状态参数。从我在网上找到的,这似乎是一种CSRF保护,对吧?而且我猜这个东西也创造了jsessionid cookie?

    问题

    1. 上面提到的工作流程是否合理?
    2. 我的用例中是否需要此CSRF保护?
    3. 我怎么能禁用这种行为?我的意思是,我使用了SessionCreationPolicy.STATELESS,但Spring仍然使用jsessionid创建了一个会话。那我怎样才能创建一个真正的无状态休息api呢? (至少关于饼干......)
    4. 这是正确的方法吗?或者我错过了什么?
    5. 示例代码

      如前所述,我在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;
      }
      

      非常感谢你的帮助!

1 个答案:

答案 0 :(得分:0)

这是可选的但建议使用的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://tools.ietf.org/html/rfc6749#section-4.1.1