Я пытаюсь реализовать Spring Security с формой входа в систему, отправляемой через Ajax. Я создал свой собственный аутентификационныйFailureHandler и свой собственный аутентификационныйSuccessHandler. Я не уверен, что моя проблема заключается в моем коде Spring/Java или в моем коде на стороне клиента. Я также видел, что проблема может быть связана с защитой от межсайтовых сценариев. Когда я вхожу в систему, он проходит проверку подлинности, но на самом деле я получаю новую HTML-страницу с моим JSON, но тип содержимого — html/text.
Когда вход не удался, я получаю то же самое.
Заголовки запроса и ответа мне не кажутся правильными. Во-первых, нет заголовков ответов, а во-вторых, в заголовках запросов нет X-Requested-With.
Некоторые пункты на заметку
- Я использую Spring Boot 1.2.5
- У меня включен CSRF в Spring Security, это требование клиента
- Не может быть страницы входа, логин должен быть в правом верхнем углу верхней навигации/заголовка, поэтому я отправляю через JS/Ajax
Вот как будет выглядеть форма входа:
Вот мои пользовательские обработчики
@Component
public class AuthFailureHandler extends SimpleUrlAuthenticationFailureHandler {
@Autowired
private UserMapper userMapper;
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
userMapper.incrementFailedLogin(request.getParameter("sec-user"));
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().print("{\"success\": false}");
response.getWriter().flush();
}
}
-
@Component
public class AuthSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
@Autowired
private UserMapper userMapper;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws ServletException, IOException {
userMapper.updateUserOnAuthSuccess(request.getParameter("sec-user"));
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().print("{\"success\": true}");
response.getWriter().flush();
}
}
Моя конфигурация безопасности Spring
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
AuthFailureHandler authFailureHandler;
@Autowired
AuthSuccessHandler authSuccessHandler;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/about","/public/**").permitAll()
.anyRequest().fullyAuthenticated()
.and()
.formLogin()
.usernameParameter("sec-user")
.passwordParameter("sec-password")
.failureHandler(authFailureHandler)
.successHandler(authSuccessHandler)
.permitAll()
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/")
.deleteCookies("remember-me", "JSESSIONID")
.permitAll()
.and()
.rememberMe();
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(new BCryptPasswordEncoder());
}
}
Форма входа (Thymeleaf) — встроена в меню навигации
<div th:fragment="login">
<form th:action="@{/login}" method="post" accept-charset="UTF-8" class="login-pane" id="login-form">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
<div class="form-group">
<label for="sec-user">Email</label>
<input type="email" class="form-control" id="sec-user" name="sec-user" placeholder="Email" />
</div>
<div class="form-group">
<label for="sec-password">Password</label>
<input type="password" class="form-control" id="sec-password" name="sec-password" placeholder="Password" />
</div>
<button type="login-btn" class="btn btn-danger">Login</button>
</form>
</div>
И последнее, но не менее важное: мой javascript-код.
$(document).ready(function() {
$("#login-btn").click(login)
});
function login() {
console.info("Attempting to authenticate");
$.ajax({
type: 'POST',
url: '/login',
data: $('#login-form').serialize(),
cache: false,
dataType: "json",
crossDomain: false,
success: function (data) {
var response = jQuery.parseJSON(data);
if (response.success == true) {
console.info("Authentication Success!");
window.location.href("/");
}
else {
console.error("Unable to login");
}
},
error: function (data) {
console.error("Login failure");
}
});
}