切換
舊版
前往
大廳
主題

Spring Boot-使用Ajax登入

默思 | 2019-06-10 13:41:44 | 巴幣 0 | 人氣 514

這篇主要是記錄使用自定義登錄頁面,然後用AJAX POST登入
因為是記錄用的,所以不太多作解釋



首先是先定義Success Handler與Failure Handler
Success Handler:
public class AjaxAuthSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        /// Response OK
        response.setStatus(HttpServletResponse.SC_OK);
    }
}

Failure Handler:
public class AjaxAuthFailureHandler extends SimpleUrlAuthenticationFailureHandler {

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        /// Response 401
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication failed");
    }
}

再來是定義處理Exception的EntryPoint:
public class UnauthorizedEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {

        /// Check the request is ajax
        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);
    }
}

最後是SecurityConfig:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .httpBasic()
                .and()
                .exceptionHandling().authenticationEntryPoint(new UnauthorizedEntryPoint())
                .and()
                /// Disable csrf
                .csrf().disable()
                .authorizeRequests()
                /// /login and /*.* don't need authentication
                .antMatchers("/login", "/*.*").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                /// Set login form
                .loginPage("/login.html")
                //.loginProcessingUrl("/login")
                //.usernameParameter("username")
                //.passwordParameter("password")
                //.successHandler(new AjaxAuthSuccessHandler())
                //.failureHandler(new AjaxAuthFailureHandler())
                //.permitAll()
                .and()
                /// Set AuthenticationFilter
                .addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class)
                .logout()
                .logoutUrl("/logout")
                .permitAll();
    }

    @Bean
    public UsernamePasswordAuthenticationFilter authenticationFilter() throws Exception {
        UsernamePasswordAuthenticationFilter filter = new UsernamePasswordAuthenticationFilter();

        /// Set parameter name called username
        filter.setUsernameParameter("username");
        // Set parameter name called password
        filter.setPasswordParameter("password");
        /// Only accept POST
        filter.setPostOnly(true);
        /// Set success handler
        filter.setAuthenticationSuccessHandler(new AjaxAuthSuccessHandler());
        /// Set failure handler
        filter.setAuthenticationFailureHandler(new AjaxAuthFailureHandler());
        /// Set request matcher
        filter.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/login", "POST"));
        /// Set AuthenticationManager (Get the default AuthenticationManager)
        filter.setAuthenticationManager(authenticationManagerBean());

        return filter;
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .inMemoryAuthentication()
                /// Normally, here needs to encrypt the password
                .withUser("user").password("{noop}user").roles("USER");
    }
}
補充:
configure function註解的部份其實也能用,不一定要使用AuthenticationFilter
因為定義在HttpSecuriity好像會幫你作,而且預設應該是使用UsernamePasswordAuthenticationFilter,所以我寫這樣算是多此一舉吧
不過如果需要自定義Filter的話,就能參考

再來是客製的登入頁面(很偷懶):
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"></html>
<head>
    <title>login </title>
    <script src="https://code.jquery.com/jquery-3.2.1.min.js" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
</head>
<body>

    <div><label> User Name : <input id="username" type="text" name="username"/> </label></div>
    <div><label> Password: <input id="password" type="password" name="password"/> </label></div>
    <div><input type="button" onclick="login()"/></div>

    <script>
        function login () {
            var username = document.getElementById("username").value
            var password = document.getElementById("password").value

            $.ajax ({
                url: "/login",
                type: "POST",
                data: "username=" + username + "&password=" + password,
                success: function(response, textStatus, xhr) {
                    alert("Login successfully");
                    window.location.href = "/user/index.html"
                },
                error: function (xhr, ajaxOptions, thrownError) {
                    alert(xhr.status);
                    alert(thrownError);
                }
            });
        }
    
</script>

</body>
</html>
可以看到登入成功後,會導向/user/index.html 這個頁面
再參考SecurityConfig裡的configure function
可以看到我只允許"/login"及"/*.*"的路徑能未經驗證的存取
所以,/user/index.html 這個頁面是需要登入後才能看到的


以上就是使用Ajax登入的範例
不過目前有一些情況沒解決
像是必須把csrf給disable掉,不然會無法登入
有查到資料套用thymeleaf似乎能解決,但我希望能用單純的html頁面就搞定

創作回應

相關創作

更多創作