Registro y autorización usando Spring Security usando una aplicación simple como ejemplo

Buenas tardes

Este artículo cubrirá la creación de una aplicación web simple usando Spring Boot y Spring Security. La aplicación implementará el registro de nuevos usuarios y la autorización, restringiendo el acceso a las páginas del sitio dependiendo de la función del usuario.
El objetivo principal del artículo es mostrar cómo puede restringir el acceso a varias páginas del sitio para usuarios con diferentes roles.

¿Cuál será la aplicación?


Sitio web con las siguientes páginas:

  • páginas accesibles para todos los usuarios: inicio, registro e inicio de sesión;
  • página disponible para usuarios registrados: noticias;
  • página de administrador disponible.

Lo que usaremos


  • JDK 8+;
  • Intellij Idea;
  • Spring (Spring Boot, Spring MVC, Spring Security);
  • Hibernar
  • JSP
  • PostgreSQL

Contenido


  1. Descripción de las principales anotaciones utilizadas.
  2. Crea un nuevo proyecto en el IDE.
  3. Creación de una estructura de proyecto (paquetes).
  4. Agregar entidades, controladores, servicios, repositorios y vistas.
  5. Lanzamiento de la aplicación.

1. Descripción de las principales anotaciones utilizadas


El controlador es un tipo especial de clase utilizado en aplicaciones MVC. Parece un servlet HttpServlet normal que funciona con objetos HttpServletRequest y HttpServletResponse, pero con funciones avanzadas de Spring Framework.
Repositorio : indica que la clase se usa para especificar la lista
trabajo necesario para buscar, recuperar y almacenar datos. La anotación se puede utilizar para implementar la plantilla DAO.
Servicio : indica que la clase es un servicio para implementar la lógica empresarial.
Configuración : esta anotación se utiliza para clases que definen componentes de bean.
Cableado automático : la anotación le permite establecer automáticamente el valor del campo. La funcionalidad de esta anotación es que no necesitamos preocuparnos sobre la mejor manera de transmitir al Bean una instancia de otro Bean. Spring misma encontrará el Bean deseado y sustituirá su valor en la propiedad que está marcada con la anotación.

Un poco de información sobre Spring Security


El objeto más fundamental es el SecurityContextHolder . Almacena información sobre el contexto de seguridad actual de la aplicación, que incluye información detallada sobre el usuario (principal) que trabaja con la aplicación. Spring Security utiliza un objeto de autenticación , un usuario de sesión autorizado.

Un "usuario" es solo un Objeto. En la mayoría de los casos, puede ser
emitir a la clase UserDetails . UserDetails puede considerarse como un adaptador entre bases de datos de usuarios y lo que Spring Security requiere dentro de SecurityContextHolder .

Para crear un UserDetails , se usa la interfaz UserDetailsService , con un único método:

UserDetails loadUserByUsername(String username) throws UsernameNotFoundException 

2. Crear un nuevo proyecto en el IDE


Usaremos el sistema de compilación Maven.



GroupId significa el identificador único de la empresa (o su nombre de dominio personal) que lanza el proyecto. ArtefactId es solo el nombre de nuestro proyecto.



Una vez completada la creación del proyecto, se abre el archivo pom.xml, Idea ofrecerá habilitar la importación automática, no lo rechace. Este archivo contendrá todas las dependencias (bibliotecas) utilizadas en el proyecto.



3. Crear una estructura de proyecto (paquetes)


Inmediatamente pase a crear paquetes. La estructura del proyecto, que debería resultar, se muestra a continuación.



Ahora brevemente sobre lo que se almacenará en cada paquete:

  • src \ main \ java \ com \ boots \ config - clases con configuraciones para MVC (MvcConfig) y seguridad (WebSecurityConfig);
  • src \ main \ java \ com \ boots \ controller - clases con controladores;
  • src \ main \ java \ com \ boots \ entity - clases con modelos;
  • src \ main \ java \ com \ boots \ repository - interfaces de repositorio ;
  • src \ main \ java \ com \ boots \ service - clases con servicios para modelos;
  • src \ main \ webapp \ resources - objetos estáticos: js, css, img;
  • src \ main \ webapp \ WEB-INF \ jsp : representaciones en forma de archivos .jsp.

Considere el archivo pom.xml . En este archivo, debe especificar un enlace al archivo principal utilizando la etiqueta principal , es decir, Todas las propiedades y dependencias del padre se agregarán a este archivo hijo.

 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.9.RELEASE</version> </parent> 

A continuación, agregue las dependencias para los módulos Spring, el controlador de base de datos PostgreSQL, el servidor Tomcat, JSTL.

 <properties> <java.version>1.8</java.version> </properties> 

Por defecto, maven usará la versión anterior de java 1.6, para solucionar esto especificamos la versión explícitamente.

 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <version>42.2.8</version> <scope>runtime</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> <version>9.0.27</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-taglibs</artifactId> <version>5.2.0.RELEASE</version> </dependency> </dependencies> 

También agregamos un complemento que le permite empacar archivos jar o war y ejecutarlos "en su lugar":

 <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> 

Pom.xml completo
 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>ark</groupId> <artifactId>spring</artifactId> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.6.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> <version>9.0.27</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-taglibs</artifactId> <version>5.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> </dependencies> <properties> <java.version>1.8</java.version> </properties> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> 


Complete el archivo application.properties . Las primeras 3 líneas contienen datos para conectarse a la base de datos (el nombre de la base de datos es "spring", nombre de usuario y contraseña). Las últimas 2 líneas indican la ruta a los archivos .jsp:
 spring.datasource.url=jdbc:postgresql://localhost/spring spring.datasource.username=postgres spring.datasource.password=password spring.jpa.show-sql=true spring.jpa.generate-ddl=false spring.jpa.hibernate.ddl-auto=update spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true spring.mvc.view.prefix = /WEB-INF/jsp/ spring.mvc.view.suffix = .jsp 

La propiedad spring.jpa.show-sql muestra los cuerpos de las consultas de la base de datos en la consola.
spring.jpa.hibernate.ddl-auto le permite establecer una estrategia para crear una base de datos basada en nuestros modelos; tiene valores diferentes (ninguno, crear, actualizar, etc.). actualizar en este caso significa que las tablas y los campos de la base de datos se crearán sobre la base de nuestros modelos y cambiarán con ellos.

Mirando hacia el futuro, solo necesitaremos crear una base de datos con el nombre spring , y las tablas de usuario, roles y su tabla de enlaces junto con claves externas se generarán automáticamente en función de los modelos (paquete de entidad), que ahora procederemos a crear.

4. Agregar entidades, controladores, servicios, repositorios y vistas.


4.1. Agregar entidades (modelos)


Un requisito obligatorio para todas las entidades: campos privados, captadores y establecedores para todos los campos y un constructor vacío (no se muestra en los ejemplos). No necesitan escribirse manualmente, presione Alt + Insertar y Idea lo hará por usted.

Para importar las clases y bibliotecas necesarias, use el método abreviado de teclado Alt + Intro .



Usuario
 @Entity @Table(name = "t_user") public class User implements UserDetails { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Size(min=2, message = "  5 ") private String username; @Size(min=2, message = "  5 ") private String password; @Transient private String passwordConfirm; @ManyToMany(fetch = FetchType.EAGER) private Set<Role> roles; public User() { } public Long getId() { return id; } public void setId(Long id) { this.id = id; } @Override public String getUsername() { return username; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } public void setUsername(String username) { this.username = username; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { return getRoles(); } @Override public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getPasswordConfirm() { return passwordConfirm; } public void setPasswordConfirm(String passwordConfirm) { this.passwordConfirm = passwordConfirm; } public Set<Role> getRoles() { return roles; } public void setRoles(Set<Role> roles) { this.roles = roles; } } 


Usuario Al principio sobre las anotaciones: la entidad dice que los campos de la clase tienen un mapa en la base de datos, Table (name = "t_user") indica qué tabla.

El parámetro GenerationType.IDENTITY IDENTITY significa que la base de datos se dedicará a generar la identificación. Hay otras estrategias SECUENCIA: utiliza el motor de base de datos incorporado, como PostgreSQL u Oracle, un mecanismo para generar valores secuenciales (secuencia). TABLE: utiliza una tabla separada con valores de clave inicializados. Otra opción es AUTO, la hibernación en sí elegirá una de las estrategias anteriores, pero se recomienda especificar la estrategia explícitamente.

El campo debajo de la anotación Transitoria no se muestra en la base de datos. La lista de roles está asociada con el usuario, una relación de muchos a muchos (un usuario puede tener varios roles por un lado y un rol puede tener varios usuarios por el otro); FetchType.EAGER : descarga "codiciosa", es decir la lista de roles se carga inmediatamente con el usuario (no espera hasta que se contacte con ellos).

Para seguir utilizando la clase Usuario en Spring Security, debe implementar la interfaz UserDetails . Para hacer esto, anule todos sus métodos. Pero en nuestro ejemplo, solo usaremos el método getAuthorities () , devuelve una lista de roles de usuario. Por lo tanto, para los métodos restantes, cambie el valor de retorno a verdadero .

Papel
 @Entity @Table(name = "t_role") public class Role implements GrantedAuthority { @Id private Long id; private String name; @Transient @ManyToMany(mappedBy = "roles") private Set<User> users; public Role() { } public Role(Long id) { this.id = id; } public Role(Long id, String name) { this.id = id; this.name = name; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Set<User> getUsers() { return users; } public void setUsers(Set<User> users) { this.users = users; } @Override public String getAuthority() { return getName(); } } 


Papel Esta clase debe implementar la interfaz GrantedAuthority , en la que solo se necesita redefinir un método getAuthority () (devuelve el nombre del rol). El nombre del rol debe coincidir con el patrón: "ROLE_NAME", por ejemplo, ROLE_USER . Además del constructor predeterminado, debe agregar un par de constructores públicos más: el primero solo acepta la identificación, la segunda identificación y el nombre.

Aquí puede agregar restricciones de campo. Tamaño (min = 2) : significa que la longitud mínima del campo es 2; si se viola la restricción, se mostrará un mensaje.

4.2. Implementación de una capa de acceso a datos y una capa de servicio


Spring Data proporciona un conjunto de implementaciones listas para crear una capa que proporciona acceso a la base de datos. La interfaz JpaRepository proporciona un conjunto de métodos estándar (findBy, save, deleteById, etc.) para trabajar con la base de datos.

UserRepository. Creamos la interfaz de usuario en el paquete del repositorio y heredamos el JpaRepository <Usuario, Largo> , especificamos la clase Usuario y su tipo de identificación es Largo .

 public interface UserRepository extends JpaRepository<User, Long> { User findByUsername(String username); } 

T.O. simplemente creando una interfaz y heredando JpaRepository, puede realizar consultas estándar a la base de datos. Si necesita un método específico, simplemente agréguelo a la interfaz, según los consejos de Idea. Por ejemplo, necesitamos un método para buscar un usuario en una base de datos por su nombre. Escribimos el tipo del objeto devuelto, y luego el IDE ofrece posibles opciones. Es decir en este caso, el nombre del método determina el cuerpo de la solicitud.



Si es necesario, puede usar la anotación de consulta sobre el método y escribir consultas en HQL o SQL (debe agregar nativeQuery = true).

 @Query(value = "SELECT nextval(pg_get_serial_sequence('t_user', 'id'))", nativeQuery = true) Long getNextId(); 

RoleRepository. Creamos de la misma manera, no necesitamos nuestros propios métodos aquí.

 public interface RoleRepository extends JpaRepository<Role, Long> { } 

Servicio de usuario. Contiene métodos para la lógica de negocios de la aplicación. Esta clase implementa la interfaz UserDetailsService (necesaria para Spring Security), en la que debe anular un método loadUserByUsername () .

En esta clase, puede ver otra forma de ejecutar una consulta SQL, utilizando el EntityManager.

Servicio al usuario
 @Service public class UserService implements UserDetailsService { @PersistenceContext private EntityManager em; @Autowired UserRepository userRepository; @Autowired RoleRepository roleRepository; @Autowired BCryptPasswordEncoder bCryptPasswordEncoder; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = userRepository.findByUsername(username); if (user == null) { throw new UsernameNotFoundException("User not found"); } return user; } public User findUserById(Long userId) { Optional<User> userFromDb = userRepository.findById(userId); return userFromDb.orElse(new User()); } public List<User> allUsers() { return userRepository.findAll(); } public boolean saveUser(User user) { User userFromDB = userRepository.findByUsername(user.getUsername()); if (userFromDB != null) { return false; } user.setRoles(Collections.singleton(new Role(1L, "ROLE_USER"))); user.setPassword(bCryptPasswordEncoder.encode(user.getPassword())); userRepository.save(user); return true; } public boolean deleteUser(Long userId) { if (userRepository.findById(userId).isPresent()) { userRepository.deleteById(userId); return true; } return false; } public List<User> usergtList(Long idMin) { return em.createQuery("SELECT u FROM User u WHERE u.id > :paramId", User.class) .setParameter("paramId", idMin).getResultList(); } } 


Considere el método saveUser (usuario usuario) .

 public boolean saveUser(User user) { User userFromDB = userRepository.findByUsername(user.getUsername()); if (userFromDB != null) { return false; } user.setRoles(Collections.singleton(new Role(1L, "ROLE_USER"))); user.setPassword(bCryptPasswordEncoder.encode(user.getPassword())); userRepository.save(user); return true; } 

Primero, se realiza una búsqueda en la base de datos por nombre de usuario, si ya existe un usuario con el mismo nombre, el método finaliza el trabajo. Si no se toma el nombre de usuario, se agrega el rol ROLE_USER. Para no almacenar la contraseña en una forma sin procesar, se codifica previamente utilizando bCryptPasswordEncoder . Luego, el nuevo usuario se guarda en la base de datos.

4.3. Agregar controladores


Para las páginas que el servidor no procesa de ninguna manera, pero que simplemente devuelven la página, la asignación se puede configurar en la configuración. El controlador de Spring Security maneja la página de inicio de sesión de manera predeterminada, por lo que no se requiere un controlador por separado.

 @Configuration public class MvcConfig implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/login").setViewName("login"); registry.addViewController("/news").setViewName("news"); } } 

RegistrationController. Se necesita un controlador separado para la página de registro. Para procesar una solicitud GET, se utiliza la anotación @GetMapping ("/ registro") , para POST - @PostMapping ("/ registro") .

Control de registro
 @Controller public class RegistrationController { @Autowired private UserService userService; @GetMapping("/registration") public String registration(Model model) { model.addAttribute("userForm", new User()); return "registration"; } @PostMapping("/registration") public String addUser(@ModelAttribute("userForm") @Valid User userForm, BindingResult bindingResult, Model model) { if (bindingResult.hasErrors()) { return "registration"; } if (!userForm.getPassword().equals(userForm.getPasswordConfirm())){ model.addAttribute("passwordError", "  "); return "registration"; } if (!userService.saveUser(userForm)){ model.addAttribute("usernameError", "     "); return "registration"; } return "redirect:/"; } } 


Para agregar u obtener algo de la página, pasamos al modelo. En una solicitud GET, se agrega un nuevo objeto vacío de la clase Usuario a la página. Esto se hace para no obtener datos del formulario de registro uno a la vez durante una solicitud POST (nombre de usuario, contraseña, PasswordComfirm), pero inmediatamente se obtiene el objeto userForm completo.

El método addUser () espera como parámetro el objeto de usuario (userForm) que se agregó durante la solicitud GET. Anotación Comprobaciones válidas para ver si se cumplen las restricciones establecidas en los campos, en este caso de al menos 2 caracteres de longitud. Si no se cumplieron las restricciones , indingResult contendrá errores.

Si la contraseña y su confirmación no coinciden, agregue un mensaje a la página y devuélvala. Al final, intentamos guardar agregar el usuario a la base de datos.

El método saveUser () devuelve falso si ya existe un usuario con el mismo nombre y verdadero si el usuario se guarda en la base de datos. Si el intento de guardar falla, agregamos un mensaje de error y devolvemos la página. Si el usuario se ha guardado correctamente, vaya a la página principal.

Admincontroller Solo los usuarios administradores tienen acceso a la página de administración. No hay nada nuevo en el método userList () ; recibe los datos de todos los usuarios y los agrega a la página.

Admincontroller
 @Controller public class AdminController { @Autowired private UserService userService; @GetMapping("/admin") public String userList(Model model) { model.addAttribute("allUsers", userService.allUsers()); return "admin"; } @PostMapping("/admin") public String deleteUser(@RequestParam(required = true, defaultValue = "" ) Long userId, @RequestParam(required = true, defaultValue = "" ) String action, Model model) { if (action.equals("delete")){ userService.deleteUser(userId); } return "redirect:/admin"; } @GetMapping("/admin/gt/{userId}") public String gtUser(@PathVariable("userId") Long userId, Model model) { model.addAttribute("allUsers", userService.usergtList(userId)); return "admin"; } } 


El método deleteUser () usa la anotación RequestParam, es decir la vista tendrá un formulario que debe pasar dos parámetros: userId y action. El enlace tendrá la forma http: // localhost: 8080 / admin? UserId = 24 & action = delete cuando se ejecute esta solicitud, el usuario con id = 24 será eliminado.

Otra opción para pasar parámetros a una URL es usar PathVariable . Usando esta anotación, obtenemos las partes individuales de la URL, para el método getUser () , la URL se verá así: http: // localhost: 8080 / admin / gt / 24 , después de la transición, se mostrará una lista de todos los usuarios con id> 24.

Configuraciones de seguridad

WebSecurityConfig. Contiene 2 beans BCryptPasswordEncoder y AuthenticationManager , que se cumplieron previamente en la clase userService.
Además, el método configure () configura el acceso a varios recursos del sitio. Como parámetros del método antMatchers () , pasamos las rutas para las que queremos establecer un límite. Luego indicamos a los usuarios con qué rol estará disponible esta página / páginas.

WebSecurityConfig
 @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired UserService userService; @Bean public BCryptPasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(HttpSecurity httpSecurity) throws Exception { httpSecurity .csrf() .disable() .authorizeRequests() //      .antMatchers("/registration").not().fullyAuthenticated() //       .antMatchers("/admin/**").hasRole("ADMIN") .antMatchers("/news").hasRole("USER") //    .antMatchers("/", "/resources/**").permitAll() //     .anyRequest().authenticated() .and() //     .formLogin() .loginPage("/login") //       .defaultSuccessUrl("/") .permitAll() .and() .logout() .permitAll() .logoutSuccessUrl("/"); } @Autowired protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userService).passwordEncoder(bCryptPasswordEncoder()); } } 


4.4. Agregar vistas


Página de inicio de index.jsp , a continuación hay 2 opciones: para un invitado y para un usuario autorizado.





index.jsp
 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %> <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <!DOCTYPE HTML> <html> <head> <title></title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <link rel="stylesheet" type="text/css" href="${contextPath}/resources/css/style.css"> </head> <body> <div> <h3>${pageContext.request.userPrincipal.name}</h3> <sec:authorize access="!isAuthenticated()"> <h4><a href="/login"></a></h4> <h4><a href="/registration"></a></h4> </sec:authorize> <sec:authorize access="isAuthenticated()"> <h4><a href="/logout"></a></h4> </sec:authorize> <h4><a href="/news"> ( )</a></h4> <h4><a href="/admin"> ( )</a></h4> </div> </body> </html> 


Para ocultar parte del contenido en la página para usuarios autorizados (enlace a la página de registro y autorización), puede usar la etiqueta de autorización de la biblioteca de etiquetas de Spring Security. El parámetro de acceso acepta varias expresiones, por ejemplo, puede establecer una restricción dependiendo del rol de usuario hasRole ('ADMIN') .

registration.jsp Página de registro.


registro.jsp
 <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> </head> <body> <div> <form:form method="POST" modelAttribute="userForm"> <h2></h2> <div> <form:input type="text" path="username" placeholder="Username" autofocus="true"></form:input> <form:errors path="username"></form:errors> ${usernameError} </div> <div> <form:input type="password" path="password" placeholder="Password"></form:input> </div> <div> <form:input type="password" path="passwordConfirm" placeholder="Confirm your password"></form:input> <form:errors path="password"></form:errors> ${passwordError} </div> <button type="submit"></button> </form:form> <a href="/"></a> </div> </body> </html> 


En esta página, la etiqueta del formulario se usa desde la biblioteca de etiquetas, con la ayuda del atributo del modelo userForm se incluye (lo agregamos a la página durante la solicitud GET en el controlador) y los formularios:

 <form:form method="POST" modelAttribute="userForm"> 

También debe especificar la ruta para enlazar las propiedades del formulario de usuario:

 <form:input type="text" path="username" placeholder="Username"></form:input> 

login.jsp Página de inicio de sesión .



login.jsp
 <%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %> <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Log in with your account</title> </head> <body> <sec:authorize access="isAuthenticated()"> <% response.sendRedirect("/"); %> </sec:authorize> <div> <form method="POST" action="/login"> <h2>  </h2> <div> <input name="username" type="text" placeholder="Username" autofocus="true"/> <input name="password" type="password" placeholder="Password"/> <button type="submit">Log In</button> <h4><a href="/registration"></a></h4> </div> </form> </div> </body> </html> 


Esta página, como ya se mencionó, es manejada por el controlador Spring de manera predeterminada. Es importante especificar la acción: action = "/ login" y el nombre de la entrada.

página de administración admin.jsp .



admin.jsp
 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Log in with your account</title> <link rel="stylesheet" type="text/css" href="${contextPath}/resources/css/style.css"> </head> <body> <div> <table> <thead> <th>ID</th> <th>UserName</th> <th>Password</th> <th>Roles</th> </thead> <c:forEach items="${allUsers}" var="user"> <tr> <td>${user.id}</td> <td>${user.username}</td> <td>${user.password}</td> <td> <c:forEach items="${user.roles}" var="role">${role.name}; </c:forEach> </td> <td> <form action="${pageContext.request.contextPath}/admin" method="post"> <input type="hidden" name="userId" value="${user.id}"/> <input type="hidden" name="action" value="delete"/> <button type="submit">Delete</button> </form> </td> </tr> </c:forEach> </table> <a href="/"></a> </div> </body> </html> 


news.jsp La página de noticias es estática. Se utiliza solo para demostrar los derechos del usuario, por lo que el contenido que elija.

news.jsp
 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> </head> <body> <div> <h2> <br>    .</h2> <a href="/"></a> </div> </body> </html> 


5. Iniciando la aplicación


En la clase de aplicación principal, agregue lo siguiente:

 @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } 

Antes de continuar con el siguiente paso, asegúrese de que la estructura de su proyecto coincida con la presentada al principio.

Es hora de crear una base de datos vacía llamada spring , debe hacer esto antes del primer lanzamiento de la aplicación y solo una vez.

Puede ejecutar la aplicación y ver cómo cambia la base de datos: se crearán 3 tablas vacías en ella. Debe agregar roles de usuario a la tabla t_role:
Consulta SQL agregando roles
 INSERT INTO public.t_role(id, name) VALUES (1, 'ROLE_USER'), (2, 'ROLE_ADMIN'); 


Ahora puedes intentar registrarte. La aplicación no proporciona un método para registrar un usuario administrador, pero es necesaria para la demostración. Por lo tanto, después de registrar un nuevo usuario, agregue una entrada a la tabla de roles de usuario que otorgue este rol:

Consulta SQL que agrega rol de administrador
 INSERT INTO public.t_user_roles(user_id, roles_id) VALUES (1, 2); 




Si después de agregar derechos de administrador no puede acceder a la página del administrador (error 403), vaya al sitio web. Solo el usuario con el rol de administrador tiene acceso

a la página http: // localhost: 8080 / admin . http: // localhost: 8080 / news que cualquier usuario registrado verá. También puede intentar ir a las páginas de registro e inicio de sesión, estando autorizado en el sitio.

Conclusión


Como resultado, se creó una aplicación web en la que podemos controlar el acceso de los usuarios a las páginas del sitio utilizando roles. Puede asignar múltiples roles a un usuario. Por ejemplo, al registrar un nuevo usuario, agregamos el rol de Usuario, que es básico para todos, y luego, cuando solicitemos privilegios adicionales por parte del usuario, podemos asignarle el rol de Escritor, que le permitirá agregar nuevas noticias al sitio.

Se crearon los archivos css y js, pero no se presentaron sus contenidos. Si lo desea, puede agregar diseño, por ejemplo, usando Bootstrap e interactividad usando js.

Descargue el proyecto terminado.

Fuentes utilizadas


  1. Registro e inicio de sesión con Spring Boot, Spring Security, Spring Data JPA, Hibernate, MySQL, JSP, Bootstrap y Docker Compose
  2. La otra cara de la primavera
  3. Spring Security / Revisión técnica de Spring Security
  4. Spring
  5. : Spring 3 MVC + Spring Security + Hibernate

Source: https://habr.com/ru/post/482552/


All Articles