這篇主要是記錄使用自定義登錄頁面,然後用AJAX POST登入
補充:
可以看到登入成功後,會導向/user/index.html 這個頁面
以上就是使用Ajax登入的範例
因為是記錄用的,所以不太多作解釋
首先是先定義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> |
再參考SecurityConfig裡的configure function
可以看到我只允許"/login"及"/*.*"的路徑能未經驗證的存取
所以,/user/index.html 這個頁面是需要登入後才能看到的
以上就是使用Ajax登入的範例
不過目前有一些情況沒解決
像是必須把csrf給disable掉,不然會無法登入
有查到資料套用thymeleaf似乎能解決,但我希望能用單純的html頁面就搞定