1. 程式人生 > >springsecurity ajax超時返回登入頁面

springsecurity ajax超時返回登入頁面

公司開發採用Spring Security+AngualerJS框架,在session過期之後,ajax請求會直接出錯。本文介紹如何實現出錯情況下自動跳轉至登入頁。

  整體思路是,session過期後,ajax請求返回401 unauthentication錯誤,前端對$http服務新增攔截器,對401錯誤進行跳轉處理,跳轉至登入頁。

  由於session過期,需要驗證的請求(不論是不是ajax請求)會返回302重定向,我們先配置spring security使之能對ajax請求返回401錯誤。如下:

  實現自定義的RequestMatcher,當請求是ajax請求即匹配上(angular預設不會帶上X-Requested-With,這裡通過Accept進行判斷,也可以在前端對ajax請求新增X-Requested-With頭):

複製程式碼
public static class AjaxRequestMatcher implements RequestMatcher {
    @Override
    public boolean matches(HttpServletRequest request) {
        return "XMLHttpRequest".equals(request.getHeader("X-Requested-With")) ||
                request.getHeader("Accept") != null && 
                    request.getHeader(
"Accept").contains("application/json"); } }
複製程式碼

  實現自定義的AuthenticationEntryPoint,返回401錯誤:

複製程式碼
@Component
public class AjaxAuthenticationEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
            AuthenticationException authException) 
throws IOException, ServletException { response.sendError(HttpServletResponse.SC_UNAUTHORIZED); } }
複製程式碼

  配置錯誤處理,對ajax請求使用AjaxAuthenticationEntryPoint(

  .exceptionHandling()
        .defaultAuthenticationEntryPointFor(authenticationEntryPoint, new AjaxRequestMatcher())):

複製程式碼
    @Autowired
    private AuthenticationEntryPoint authenticationEntryPoint;
    
    protected void configure(HttpSecurity http) throws Exception {
        http
            .headers()
                .cacheControl()
                .and()
            .authorizeRequests()
                .antMatchers(
                    "/login",
                    "/css/**",
                    "/img/**",
                    "/js/**",
                    "/partial/**",
                    "/script/**",
                    "/upload/**",
                    "/plugin/**").permitAll()
                .antMatchers("/**")
                .authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
                .defaultSuccessUrl("/app.html", true)
                .and()
            .logout()
                .logoutUrl("/logout")
                .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                .logoutSuccessUrl("/login")
                .and()
            .exceptionHandling()
                .defaultAuthenticationEntryPointFor(authenticationEntryPoint, new AjaxRequestMatcher())
                .and()
            .csrf().disable();
    }
複製程式碼

  前端,新增攔截器:

複製程式碼
angular.module('app', [])
.config(function($httpProvider) {
    $httpProvider.interceptors.push(function($q, $window) {
          return {
            // optional method
            'request': function(config) {
              // do something on success
              return config;
            },
            // optional method
           'requestError': function(rejection) {
              // do something on error
              if (canRecover(rejection)) {
                return responseOrNewPromise
              }
              return $q.reject(rejection);
            },
            // optional method
            'response': function(response) {
              // do something on success
              return response;
            },
            // optional method
           'responseError': function(rejection) {
              // do something on error
              if (rejection.status === 401) {
//                return responseOrNewPromise
                  console.log('401');
                  $window.location.href = 'login?expired';
              }
              return $q.reject(rejection);
            }
          };
        });
});