我目前正在开展一个项目,我们希望用户通过Facebook和其他OAuth2提供商登录.此外,REST api应该是无状态的.因此,不应创建/使用cookie/jsessionids.对于api的授权,在通过Facebook成功登录后,api会发布JWT.消耗其余api的webapp是使用AgularJS和satellizer构建的.我将代码缩减为github上的最小示例.
工作流程理念:
用户进入该网站,选择"使用Facebook登录"
Web应用程序打开一个弹出窗口,显示Facebook登录页面
用户登录,接受和Facebook重定向到webapp
webapp从Facebook接收令牌并使用它登录到其余的api(GET/login/facebook?code = XXXXXXXXXXXXXXXXX)
其余的api返回JWT.
webapp使用JWT来针对任何进一步的请求对其余api进行授权.
到目前为止工作
webapp可以处理步骤1到4
如果您使用从执行GET的OAuth2ClientContextFilter获取的重定向到没有代码参数的"/ login/facebook",其余的api可以处理步骤5和6.(此重定向发送到Facebook,您登录后,您将再次重定向到api.)
其余的api是无状态的,没有创建/需要jsessionid/cookies(禁用csrf并使用SessionCreationPolicy.STATELESS).除了 "登录/ facebook"调用,这个仍然创建一个jsessionid.
问题
webapp和其他api的"login/facebook?code = XXX"调用的组合不起作用.我发现当你进行GET"login/facebook"时,你将被重定向到facebook,并在网址上附加一个额外的状态参数.此外,当facebook重定向回api时,还会添加此状态参数.从我在网上找到的,这似乎是一种CSRF保护,对吧?我猜这个东西也创造了jsessionid cookie?
问题
以上工作流程是否合理?
我的用例中是否需要此CSRF保护?
我怎么能禁用这种行为?我的意思是,我使用了SessionCreationPolicy.STATELESS,但Spring仍然使用jsessionid创建了一个会话.那我怎样才能创建一个真正的无状态休息api呢?(至少关于饼干...)
这是正确的方法吗?或者我错过了什么?
示例代码
如前所述,我在GitHub上放了一个完整的最小例子.在这里,我将只发布WebSecurityConfigurerAdapter(希望)最重要的部分.该完整的文件在这里.
@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; }
非常感谢您的帮助!