J'ai une application simple qui est divisée en 2 parties:
Les demandes sont reçues par un serveur nginx qui écoute sur le port 80.
J'ai créé un formulaire de connexion personnalisé (dans la partie frontend) et j'essaie de configurer le serveur Spring-boot.
Il y a beaucoup d'exemples où je peux voir comment définir une URL "succès de connexion" et une URL "erreur de connexion" mais je ne veux pas que Spring-security redirige l'utilisateur. Je veux que Spring-security réponde avec HTTP 200 si la connexion a réussi ou HTTP 40x si la connexion a échoué.
En d'autres termes: je veux que le backend ne réponde qu'avec JSON, jamais HTML.
Jusqu'à présent, lorsque je soumets le formulaire de connexion, la demande est redirigée et j'obtiens le formulaire de connexion Spring par défaut comme réponse.
J'ai essayé d'utiliser .formLogin().loginProcessingUrl("/login");
au lieu de loginPage("")
:
@Configuration
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected static class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password("password").roles("ADMIN");
}
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginProcessingUrl("/login");
Grâce à M. Deinum et grâce à ce guide , j'ai pu trouver la solution.
Tout d'abord, j'ai eu un problème de configuration avec le formulaire de connexion lui-même. Comme le backend a un chemin de contexte défini sur /api
, le formulaire personnalisé aurait dû soumettre les paramètres du formulaire à /api/login
mais je soumettais en fait les données à /api/login/
(remarquez le supplément /
à la fin).
En conséquence, j'essayais sans le savoir d'accéder à une ressource protégée! Par conséquent, la demande a été traitée par défaut, AuthenticationEntryPoint
dont le comportement par défaut consiste à rediriger l'utilisateur vers la page de connexion.
Comme solution, j'ai implémenté un AuthenticationEntryPoint personnalisé:
private AuthenticationEntryPoint authenticationEntryPoint() {
return new AuthenticationEntryPoint() {
@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
httpServletResponse.getWriter().append("Not authenticated");
httpServletResponse.setStatus(401);
}
};
}
Puis utilisé dans la configuration:
http
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint())
et j'ai fait de même pour les autres gestionnaires:
@Configuration
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected static class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password("password").roles("ADMIN");
}
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.successHandler(successHandler())
.failureHandler(failureHandler())
.and()
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler())
.authenticationEntryPoint(authenticationEntryPoint())
.and()
.csrf().csrfTokenRepository(csrfTokenRepository()).and().addFilterAfter(csrfHeaderFilter(), CsrfFilter.class)
;
}
private AuthenticationSuccessHandler successHandler() {
return new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
httpServletResponse.getWriter().append("OK");
httpServletResponse.setStatus(200);
}
};
}
private AuthenticationFailureHandler failureHandler() {
return new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
httpServletResponse.getWriter().append("Authentication failure");
httpServletResponse.setStatus(401);
}
};
}
private AccessDeniedHandler accessDeniedHandler() {
return new AccessDeniedHandler() {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
httpServletResponse.getWriter().append("Access denied");
httpServletResponse.setStatus(403);
}
};
}
private AuthenticationEntryPoint authenticationEntryPoint() {
return new AuthenticationEntryPoint() {
@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
httpServletResponse.getWriter().append("Not authenticated");
httpServletResponse.setStatus(401);
}
};
}
private Filter csrfHeaderFilter() {
return new OncePerRequestFilter() {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class
.getName());
if (csrf != null) {
Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
String token = csrf.getToken();
if (cookie == null || token != null
&& !token.equals(cookie.getValue())) {
cookie = new Cookie("XSRF-TOKEN", token);
cookie.setPath("/");
response.addCookie(cookie);
}
}
filterChain.doFilter(request, response);
}
};
}
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
}
Cet article est collecté sur Internet, veuillez indiquer la source lors de la réimpression.
En cas d'infraction, veuillez [email protected] Supprimer.
laisse moi dire quelques mots