【发布时间】: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。
工作流程思路:
- 用户进入网站,选择“用facebook登录”
- Web 应用程序打开一个弹出窗口,显示 facebook 登录页面
- 用户登录、接受和 facebook 重定向到 web 应用程序
- webapp 从 Facebook 接收令牌并使用它来登录其余 api (GET /login/facebook?code=XXXXXXXXXXXXXXXXXX)
- rest api 返回一个 JWT。
- 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?
问题
- 上面介绍的工作流程是一个合理的想法吗?
- 我的用例中是否需要这种 CSRF 保护?
- 如何禁用此行为?我的意思是,我使用了 SessionCreationPolicy.STATELESS 但 spring 仍然使用 jsessionid 创建一个会话。那我怎样才能创建一个真正无状态的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;
}
非常感谢您的帮助!
【问题讨论】:
-
你弄明白了吗?我的应用程序结构有些不同,但我遇到了同样的问题。
-
你找到让它无状态的方法了吗?
标签: spring jwt spring-security-oauth2 jsessionid stateless