Spring Security登录后一直跳转登录页面原因分析

情况一:

引发错误的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)
        ;
    }
}

1.情况描述:

测试授权码模式,访问地址:http://localhost:9949/oauth/authorize?client_id=eden&response_type=code&scope=server&redirect_uri=http://www.baidu.com。被Security拦截后,重定向到登录页面。输入正确的用户名和密码,仍然回到登录页面,陷入死循环...

2.源码分析:

先放一张流程图,待会儿会用到。

2.1.分析流程:

2.1.1.尝试获取授权码

当我们请求地址: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方法,有两步操作:

  • 缓存我们发出的请求。

  • 做异常的处理:异常处理这里我们可以看到,他会把我们重定向到/login。

放开断点后,浏览器回到登录页面:

既然如此,那现在我们就先登录。

2.1.2.尝试登录

此处描述的是流程图中的5、6步骤。

输入正确的用户名和密码,点击登录。我们知道登录成功之后,AbstractAuthenticationProcessingFilter会调用handler做成功后的处理。

protected void successfulAuthentication(HttpServletRequest request,
      HttpServletResponse response, FilterChain chain, Authentication authResult)
      throws IOException, ServletException {
   //省略...
   successHandler.onAuthenticationSuccess(request, response, authResult);
}

最终会调用SavedRequestAwareAuthenticationSuccessHandleronAuthenticationSuccess方法。如下图断点位置所示,他会根据我们当前的请求来获取我们之前缓存的请求,但是此时获取结果为null:

然后进入到super.onAuthenticationSuccess(request, response, authentication)方法,这个方法里最终把我们重定向到/根路径。

于是从浏览器上看到我们又回到了登录页面。

既然我们是因为无法从缓存中拿到之前的请求而导致再次被重定向到登录界面的,那我们就看一下,获取缓存请求是怎么执行的?

3.原因分析

刚才分析过,登陆成功后,执行requestCache.getRequest(request, response),这个方法有两个默认实现。

而当我们尝试获取验证码时,因为没有登录抛出异常被重定向到登录页面/login之前的那次缓存请求时怎么做的呢?debug如下,他执行的是NullRequestCachesaveRequest方法。

从源码中我们看到,saveRequest方法是个空实现,什么都不做,也就是根本就没有缓存这次请求;而当你登陆成功后尝试获取请求的时候,getRequest返回的也是null。

至此,我们晓得了,为什么我们得不到缓存请求,被一直重定向到登录页面,那如何解决呢?

5.解决方案

既然我们是因为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)
    ;
}

5.1.源码分析

5.1.1.尝试获取验证码

访问地址: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登录页面,那我们继续登录。

5.1.2.尝试登录

登录验证成功后,进入认证成功处理流程。此时我们看到获取缓存请求时的默认实现是HttpSessionRequestCache的方法。

getRequest方法中,我们可以顺利得到上一次请求的地址,并最终被重定向到redirect_uri的地址中去。

至此,我们就可以顺利得到授权码了。

情况二

情况二是转载自大佬一把杀猪刀的一篇文章《Spring Security OAuth2登录后无法跳转获取授权码地址,直接跳转根路径原因详解》,主要是分析由于存在多个RequestCache对象,当资源服务器接管路径的配置不正确时,无法获取正确的RequestCache对象,导致无法得到授权码。情况一分析时所使用的流程图,也出自一把杀猪刀大佬的这篇文章。而且他的博客中也有很多相当精彩的文章。再次表示感谢!

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Security中,可以通过配置实现登录成功后的跳转,具体步骤如下: 1. 配置登录页面Spring Security的配置文件中,配置登录页面,例如: ```java @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/login").permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage("/login") .defaultSuccessUrl("/home") .permitAll() .and() .logout() .permitAll(); } ``` 这里的`loginPage("/login")`表示登录页面的URL是`/login`,`.defaultSuccessUrl("/home")`表示登录成功后跳转到`/home`页面。 2. 配置登录成功处理器 在Spring Security的配置文件中,配置登录成功处理器,例如: ```java @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/login").permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage("/login") .successHandler(loginSuccessHandler()) // 配置登录成功处理器 .permitAll() .and() .logout() .permitAll(); } @Bean public AuthenticationSuccessHandler loginSuccessHandler(){ return new AuthenticationSuccessHandler() { @Override public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { httpServletResponse.sendRedirect("/home"); // 登录成功后跳转到/home页面 } }; } } ``` 这里的`successHandler(loginSuccessHandler())`表示在登录成功后调用一个名为`loginSuccessHandler`的Bean,该Bean是一个实现了`AuthenticationSuccessHandler`接口的类,在其`onAuthenticationSuccess`方法中实现登录成功后的跳转。 以上就是在Spring Security中实现登录成功后跳转的步骤。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值