spring security ajax登陸及返回

本文講述一下如何自定義spring security的登陸頁,網上給的資料大多過期,並且是基於後端模板技術的,講的不是太清晰,本文給出一個採用ajax的登陸及返回的先後端分離方式。css

ajax返回

總共須要處理3個地方,一個是異常的處理,須要兼容ajax請求,一個是成功返回的處理,一個是失敗返回的處理。html

ajax的異常處理

public class UnauthorizedEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        if(isAjaxRequest(request)){
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED,authException.getMessage());
        }else{
            response.sendRedirect("/login.html");
        }

    }

    public static boolean isAjaxRequest(HttpServletRequest request) {
        String ajaxFlag = request.getHeader("X-Requested-With");
        return ajaxFlag != null && "XMLHttpRequest".equals(ajaxFlag);
    }
}複製代碼

這裏咱們自定義成功及失敗的ajax返回,固然這裏咱們簡單處理,只返回statusCode前端

AjaxAuthSuccessHandler

public class AjaxAuthSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        response.setStatus(HttpServletResponse.SC_OK);
    }
}複製代碼

AjaxAuthFailHandler

public class AjaxAuthFailHandler extends SimpleUrlAuthenticationFailureHandler {
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication failed");
    }
}複製代碼

security配置

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .exceptionHandling().authenticationEntryPoint(new UnauthorizedEntryPoint())
                .and()
                .csrf().disable()
                .authorizeRequests()
                .antMatchers("/login","/css/**", "/js/**","/fonts/**").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login.html")
                .loginProcessingUrl("/login")
                .usernameParameter("name")
                .passwordParameter("password")
                .successHandler(new AjaxAuthSuccessHandler())
                .failureHandler(new AjaxAuthFailHandler())
                .permitAll()
                .and()
                .logout()
                .logoutUrl("/logout")
                .permitAll();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .inMemoryAuthentication()
                .withUser("admin").password("admin").roles("USER");
    }
}複製代碼

這裏有幾個要注意的點:java

  • permitAll
    這裏要添加前端資源路徑,以及登錄表單請求的接口地址/login
  • loginPage
    這裏設置登陸頁面的地址,這裏咱們用靜態頁面,即static目錄下的login.html
  • ajax配置
    將authenticationEntryPoint,successHandler,failureHandler設置爲上面自定義的ajax處理類

登陸頁面

就是一個純粹的html頁面,其中登陸按鈕的ajax請求以下:web

$.ajax({
            url: '/login',
            type: 'POST',
            data: "name="+name+"&password="+password,
            success: function (res, status) {
                window.location.href='/ok.html'
            },
            error: function (res, status) {
                dangerDialog(res.statusText);
            }
        });複製代碼

這裏是請求/login,也就是spring security會默認攔截的路徑,不瞭解spring security的人可能會納悶,我請求這個路徑,可是工程裏頭沒有定義/login的request mapping,沒關係麼。下面來剖析一下。ajax

spring security內置的各類filter:

Alias Filter Class Namespace Element or Attribute
CHANNEL_FILTER ChannelProcessingFilter http/intercept-url@requires-channel
SECURITY_CONTEXT_FILTER SecurityContextPersistenceFilter http
CONCURRENT_SESSION_FILTER ConcurrentSessionFilter session-management/concurrency-control
HEADERS_FILTER HeaderWriterFilter http/headers
CSRF_FILTER CsrfFilter http/csrf
LOGOUT_FILTER LogoutFilter http/logout
X509_FILTER X509AuthenticationFilter http/x509
PRE_AUTH_FILTER AbstractPreAuthenticatedProcessingFilter Subclasses N/A
CAS_FILTER CasAuthenticationFilter N/A
FORM_LOGIN_FILTER UsernamePasswordAuthenticationFilter http/form-login
BASIC_AUTH_FILTER BasicAuthenticationFilter http/http-basic
SERVLET_API_SUPPORT_FILTER SecurityContextHolderAwareRequestFilter http/@servlet-api-provision
JAAS_API_SUPPORT_FILTER JaasApiIntegrationFilter http/@jaas-api-provision
REMEMBER_ME_FILTER RememberMeAuthenticationFilter http/remember-me
ANONYMOUS_FILTER AnonymousAuthenticationFilter http/anonymous
SESSION_MANAGEMENT_FILTER SessionManagementFilter session-management
EXCEPTION_TRANSLATION_FILTER ExceptionTranslationFilter http
FILTER_SECURITY_INTERCEPTOR FilterSecurityInterceptor http
SWITCH_USER_FILTER SwitchUserFilter N/A

這裏咱們要關注的就是這個UsernamePasswordAuthenticationFilter,顧名思義,它是filter,在執行/login請求的時候攔截,於是是不須要工程裏頭去定義login的request mapping的。spring

UsernamePasswordAuthenticationFilter

spring-security-web-4.2.3.RELEASE-sources.jar!/org/springframework/security/web/authentication/UsernamePasswordAuthenticationFilter.javasegmentfault

public class UsernamePasswordAuthenticationFilter extends
        AbstractAuthenticationProcessingFilter {

public Authentication attemptAuthentication(HttpServletRequest request,
            HttpServletResponse response) throws AuthenticationException {
        if (postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException(
                    "Authentication method not supported: " + request.getMethod());
        }

        String username = obtainUsername(request);
        String password = obtainPassword(request);

        if (username == null) {
            username = "";
        }

        if (password == null) {
            password = "";
        }

        username = username.trim();

        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
                username, password);

        // Allow subclasses to set the "details" property
        setDetails(request, authRequest);

        return this.getAuthenticationManager().authenticate(authRequest);
    }
    //......
}複製代碼

這裏就是攔截,獲取login.html提交的參數,而後交給authenticationManager去認證。以後就是走後續的filter,若是成功,則會進行相應的session配置。後端

doc

相關文章
相關標籤/搜索