<%@page contentType="text/html; charset=UTF-8" %> <%@taglib prefix = "c" uri = "http://java.sun.com/jsp/jstl/core" %> <!DOCTYPE html> <html> <head> <title>Login</title> </head> <body> <c:if test="${param.error!=null}"> <div> Invalid username and password. </div> </c:if> <c:if test="${param.logout!=null}"> <div> You have been logged out. </div> </c:if> <c:url value="/login" var="loginURL" /> <form action="${loginURL}" method="post"> <div><label> User Name : <input name="username" type="text" /> </label></div> <div><label> Password: <input name="password" type="password" /> </label></div> <div><input type="submit" value="Sign In"/></div> <%-- CSRF Token para evitar ataques CSRF --%> <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/> </form> </body> </html>
Lo del CSRF Token es mejor activarlo, no como pone en otros sitios donde se desactiva. Es cuestión de hacer el servicio más seguro.
En este formulario aparece lo de "param.logout!=null" y es porque resulta que por defecto, al hacer logout, se redireccionará a la url "/login?logout". Se podría cambiar, pero de momento así se queda.
También aparece lo del "param.error!=null" por el mismo motivo, cuando hay un fallo de autenticación pues se redirige a "/login?error".
Después, tenemos que añadir un controlador que lleve a esa vista "/login":
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; @Configuration public class MvcConfig implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry){ registry.addViewController("/login").setViewName("login"); } }
No es necesario por tanto crear una clase @Controller para algo tan nimio. Después, en la configuración de seguridad debemos permitir el acceso a todo el mundo al login y al logout también, sino no podrán acceder a dichas secciones:
import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.password.PasswordEncoder; @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { private final Log logger = LogFactory.getLog(SecurityConfig.class); @Autowired private PasswordEncoder passwordEncoder; @Override protected void configure(HttpSecurity httpsec) throws Exception{ httpsec.authorizeRequests() .antMatchers("/").permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage("/login") .defaultSuccessUrl("/restricted") .permitAll() .and() .logout() .permitAll() .and() .csrf(); } @Autowired protected void configureGlobal (AuthenticationManagerBuilder auth) { try { auth.inMemoryAuthentication() .withUser("test").password(passwordEncoder.encode("test")).roles("USER"); } catch (Exception e) { logger.error(e.toString()); } } }
Significativo es también el uso de defaultSuccessUrl para indicar cual es la página por defecto a la que hay que llevar después de hacer login correcto.
Ahora hay que hacer posible el logout. Para poder hacer el logout con la protección frente a ataques CSRF, creamos un pequeño formulario para introducir el token CSRF. Así evitamos que alguien vaya desconectando sesiones ilícitamente.
Podría hacerse en un JSP/JSTL aparte que se incluiría después (logoutForm.jsp por ejemplo):
<c:url value="/logout" var="logoutURL" /> <form style="display:inline-block" action="${logoutURL}" method="post"> <button style="border-radius: 5px; padding: 2px 10px;" type="submit">salir</button> <%-- CSRF Token para evitar ataques CSRF --%> <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/> </form>
Después lo integraremos en la página restringida. Veamos ahora como podría ser el controlador de la página restringida "/restricted":
import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @Controller @RequestMapping("/restricted") public class RestrictedController { @GetMapping public String accessToRestricted(@AuthenticationPrincipal UserDetails userDetails, Model m) { if (userDetails!=null) { m.addAttribute("userName", userDetails.getUsername()); return "restricted"; } return "redirect:/"; } }
Este controlador puede acceder a la información de autenticación a través de @AuthenticationPrincipal, cosas como por ejemplo el nombre de usuario (aquí se verifica que userDetails sea distinto de null, lo cual es redundante con respecto a la configuración de seguridad, pero previene de un fallo de configuración de seguridad). Es cuestionable, porque luego para cualquier cambio hay que acordarse que está ahí :-(, perdiendo la ventaja de la configuración de seguridad centralizada.
Y luego pues su vista (restricted.jsp):
<!DOCTYPE html> <html> <head> <title>Restricted Area</title> </head> <body> Bienvenido, ${userName}. El mundo es maravilloso, ¿quieres <%@include file="logoutForm.jsp"%>? </body> </html>
Y esto es todo... 'ta chulo.
No hay comentarios :
Publicar un comentario