我目前正在开展一个项目,我们希望用户通过 Facebook 和其他 OAuth2 提供商登录。此外,REST api 应该是无状态的。因此,不应创建/使用任何 cookie/jsessionid。对于 api 授权,通过 Facebook 成功登录后,api 会发出 JWT。使用其余 api 的 webapp 是使用 AgularJS 和 satellizer 构建的。我将代码缩减为 minimal example available on github .
工作流程思路:
- 用户进入网站,选择“用 facebook 登录”
- Web 应用程序打开一个弹出窗口,显示 facebook 登录页面
- 用户登录、接受和 facebook 重定向到 web 应用
- webapp 从 Facebook 接收 token 并使用它来登录其余 api (GET/login/facebook?code=XXXXXXXXXXXXXXXXXX)
- 其余的 api 返回一个 JWT。
- webapp 使用 JWT 授权其余 api 以进行任何进一步的请求。
工作至今
- webapp 可以处理步骤 1 到 4
- 如果您使用从 OAuth2ClientContextFilter 获得的重定向,则其余 api 可以处理第 5 步和第 6 步,执行 GET 到“/login/facebook”而不使用 code 参数。 (此重定向会转到 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?
问题
- 上面介绍的工作流程是否合理?
- 我的用例中是否需要这种 CSRF 保护?
- 如何禁用此行为?我的意思是,我使用了 SessionCreationPolicy.STATELESS 但 spring 仍然使用 jsessionid 创建一个 session 。那我怎样才能创建一个真正无状态的rest api呢? (至少关于 cookie...)
- 这是正确的做法吗?还是我错过了什么?
示例代码
如前所述,我在 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;
}
非常感谢您的帮助!
最佳答案
这是一个可选但推荐的 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.
关于spring - 如何使 Spring Security OAuth2 真正无状态/摆脱 "state"参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34290060/