赞
踩
引发错误的SpringSecurity配置如下:
- @Configuration
- @EnableWebSecurity
- @EnableGlobalMethodSecurity(prePostEnabled = true)
- public class EdenOauthWebSecurityConfig extends WebSecurityConfigurerAdapter {
-
- @Override
- protected void configure(HttpSecurity http) throws Exception {
- http
- // 禁用csrf攻击防护
- .csrf().disable()
- .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
- .and()
- .authorizeRequests()
- .anyRequest().authenticated()
- .and()
- //启用表单身份验证,放行登录有关接口
- .formLogin()
- // 默认/login
- .loginProcessingUrl("/login")
- .permitAll()
- .and()
- // 和退出登录有关的直接放行
- .logout()
- // .logoutUrl(authProperty.getLogoutUrl())
- .invalidateHttpSession(false)
- ;
- }
- }
测试授权码模式,访问地址:http://localhost:9949/oauth/authorize?client_id=eden&response_type=code&scope=server&redirect_uri=http://www.baidu.com。被Security拦截后,重定向到登录页面。输入正确的用户名和密码,仍然回到登录页面,陷入死循环...
先放一张流程图,待会儿会用到。
当我们请求地址:http://localhost:9949/oauth/authorize?client_id=eden&response_type=code&scope=server&redirect_uri=http://www.baidu.com获取授权码的时候,因为此时没有登陆,所以会将我们重定向到登录页面/login,在重定向之前还有一个动作就是缓存本次请求。
上面文字描述的其实就是流程图中的1、2、3、4步骤。
因为我们没有登录,所以抛出AccessDeniedWxception,然后ExceptionTranslationFilter捕获到并由handleSpringSecurityException方法进行处理。debug如下:
处理过程中会进入sendStartAuthentication方法,有两步操作:
放开断点后,浏览器回到登录页面:
既然如此,那现在我们就先登录。
此处描述的是流程图中的5、6步骤。
输入正确的用户名和密码,点击登录。我们知道登录成功之后,AbstractAuthenticationProcessingFilter会调用handler做成功后的处理。
- protected void successfulAuthentication(HttpServletRequest request,
- HttpServletResponse response, FilterChain chain, Authentication authResult)
- throws IOException, ServletException {
- //省略...
- successHandler.onAuthenticationSuccess(request, response, authResult);
- }
最终会调用SavedRequestAwareAuthenticationSuccessHandler的onAuthenticationSuccess方法。如下图断点位置所示,他会根据我们当前的请求来获取我们之前缓存的请求,但是此时获取结果为null:
然后进入到super.onAuthenticationSuccess(request, response, authentication)方法,这个方法里最终把我们重定向到/根路径。
于是从浏览器上看到我们又回到了登录页面。
既然我们是因为无法从缓存中拿到之前的请求而导致再次被重定向到登录界面的,那我们就看一下,获取缓存请求是怎么执行的?
刚才分析过,登陆成功后,执行requestCache.getRequest(request, response),这个方法有两个默认实现。
而当我们尝试获取验证码时,因为没有登录抛出异常被重定向到登录页面/login之前的那次缓存请求时怎么做的呢?debug如下,他执行的是NullRequestCache的saveRequest方法。
从源码中我们看到,saveRequest方法是个空实现,什么都不做,也就是根本就没有缓存这次请求;而当你登陆成功后尝试获取请求的时候,getRequest返回的也是null。
至此,我们晓得了,为什么我们得不到缓存请求,被一直重定向到登录页面,那如何解决呢?
既然我们是因为session策略,导致使用了NullRequestCache的默认实现,那我们更改一下session策略不就可以了吗?
修改SpringSecurity的配置中的Session策略,去掉关于session策略的配置。
- @Override
- protected void configure(HttpSecurity http) throws Exception {
- http
- // 禁用csrf攻击防护
- .csrf().disable()
- .authorizeRequests()
- .anyRequest().authenticated()
- .and()
- //启用表单身份验证,放行登录有关接口
- .formLogin()
- // 默认/login
- .loginProcessingUrl("/login")
- .permitAll()
- .and()
- // 和退出登录有关的直接放行
- .logout()
- // .logoutUrl(authProperty.getLogoutUrl())
- .invalidateHttpSession(false)
- ;
- }
访问地址:http://localhost:9949/oauth/authorize?client_id=eden&response_type=code&scope=server&redirect_uri=http://www.baidu.com。在这里我们不再debug其他位置,直接观察如何缓存的请求。debug如下:
这是我们看到,缓存请求时的默认实现已经换成了HttpSessionRequestCache,他的saveRequest方法里,将此次请求缓存了起来。
我们知道这次请求,肯定会因为抛出了AccessDeniedException,被重定向到/login登录页面,那我们继续登录。
登录验证成功后,进入认证成功处理流程。此时我们看到获取缓存请求时的默认实现是HttpSessionRequestCache的方法。
在getRequest方法中,我们可以顺利得到上一次请求的地址,并最终被重定向到redirect_uri的地址中去。
至此,我们就可以顺利得到授权码了。
情况二是转载自大佬一把杀猪刀的一篇文章《Spring Security OAuth2登录后无法跳转获取授权码地址,直接跳转根路径原因详解》,主要是分析由于存在多个RequestCache对象,当资源服务器接管路径的配置不正确时,无法获取正确的RequestCache对象,导致无法得到授权码。情况一分析时所使用的流程图,也出自一把杀猪刀大佬的这篇文章。而且他的博客中也有很多相当精彩的文章。再次表示感谢!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。