Spring Boot Security Inicio de Sesión con MYSQL
Crearemos un inicio de sesión sencillo pero bonito como lo hicimos aqui, con un usuario con role “USER” y otro usuario “ADMIN”.
Para empezar deberás de saber como conectar tu aplicación con la base de datos MYSQL. Si no sabes como, puedes mirar mi tutorial donde lo explico. spring-boot-mysql.
Version Video.
En caso de que quieras ver el video donde programo este tutorial paso a paso aqui te lo dejo. sino solo sigue bajando.
Paso a Paso
Configurar Vista
- Crear un proyecto Spring Boot, con las respectivas dependencias.
- Web
- Thymeleaf
- Devtools
- JPA
- MYSQL
- Crear las paginas HTML para el inicio de sesión y el acceso por roles. El diseño de la pagina lo podrán ver Aqui, y el código lo pueden descargar de Aqui. Asegúrate de tener la estructura de archivos de la siguiente manera.
- menu.html: Codigo Fuente
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3"> <head> <title>Hello World!</title> </head> <body> <h1 th:inline="text">Hello [[${#httpServletRequest.remoteUser}]]!</h1> <form th:action="@{/logout}" method="post"> <input type="submit" value="Cerrar Sesion"/> </form> <h1><a th:href="@{/admin}"> admin</a> | <a th:href="@{/user}"> user</a></h1> </body> </html>
admin.html
<h1>Admin Yeah</h1>
user.html
<h1>User Yeah!</h1>
- Configura la conexión a la base de datos en el archivo application.properties: Codigo Fuente
- Crear el controlador que redirigirá los request http (En index.html asegúrate que en el form el method sea get y el action sea “/menu”):
<form class="col-12" th:action="@{/menu}" method="get">
- Inicia la aplicación con http://localhost:8080/ y deberás de tener algo parecido a la primer imagen y cuando presiones el botón “Ingresar” tengas la segunda imagen. Si tienes problemas visualizando las imágenes o el estilo css, verifica que la imagen y el link del css tenga el tag th apropiadamente.
Asegurar la aplicación
- Agrega la dependencia de Spring Boot Security a tu pom.xml: Codigo Fuente
<!-- Security --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- *** -->
- El paso anterior habilita el componente de seguridad, pero hay que configurarlo. Crear la siguiente clase que hereda de WebSecurityConfigurerAdapter. Codigo Fuente
<span class="token keyword">package</span> com<span class="token punctuation">.</span>cristianruizblog<span class="token punctuation">.</span>loginSecurity<span class="token punctuation">.</span>config<span class="token punctuation">;</span> <span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>beans<span class="token punctuation">.</span>factory<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>Autowired<span class="token punctuation">;</span> <span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>context<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>Bean<span class="token punctuation">;</span> <span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>context<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>Configuration<span class="token punctuation">;</span> <span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>security<span class="token punctuation">.</span>config<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>authentication<span class="token punctuation">.</span>builders<span class="token punctuation">.</span>AuthenticationManagerBuilder<span class="token punctuation">;</span> <span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>security<span class="token punctuation">.</span>config<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>web<span class="token punctuation">.</span>builders<span class="token punctuation">.</span>HttpSecurity<span class="token punctuation">;</span> <span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>security<span class="token punctuation">.</span>config<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>web<span class="token punctuation">.</span>configuration<span class="token punctuation">.</span>EnableWebSecurity<span class="token punctuation">;</span> <span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>security<span class="token punctuation">.</span>config<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>web<span class="token punctuation">.</span>configuration<span class="token punctuation">.</span>WebSecurityConfigurerAdapter<span class="token punctuation">;</span> <span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>security<span class="token punctuation">.</span>crypto<span class="token punctuation">.</span>bcrypt<span class="token punctuation">.</span>BCryptPasswordEncoder<span class="token punctuation">;</span> <span class="token keyword">import</span> com<span class="token punctuation">.</span>cristianruizblog<span class="token punctuation">.</span>loginSecurity<span class="token punctuation">.</span>service<span class="token punctuation">.</span>UserDetailsServiceImpl<span class="token punctuation">;</span> <span class="token annotation punctuation">@Configuration</span> <span class="token annotation punctuation">@EnableWebSecurity</span> <span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">WebSecurityConfig</span> <span class="token keyword">extends</span> <span class="token class-name">WebSecurityConfigurerAdapter</span><span class="token punctuation">{</span> <span class="token comment">//Necesario para evitar que la seguridad se aplique a los resources</span> <span class="token comment">//Como los css, imagenes y javascripts</span> String<span class="token punctuation">[</span><span class="token punctuation">]</span> resources <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">{</span> <span class="token string">"/include/**"</span><span class="token punctuation">,</span><span class="token string">"/css/**"</span><span class="token punctuation">,</span><span class="token string">"/icons/**"</span><span class="token punctuation">,</span><span class="token string">"/img/**"</span><span class="token punctuation">,</span><span class="token string">"/js/**"</span><span class="token punctuation">,</span><span class="token string">"/layer/**"</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">protected</span> <span class="token keyword">void</span> <span class="token function">configure</span><span class="token punctuation">(</span>HttpSecurity http<span class="token punctuation">)</span> <span class="token keyword">throws</span> Exception <span class="token punctuation">{</span> http <span class="token punctuation">.</span><span class="token function">authorizeRequests</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">antMatchers</span><span class="token punctuation">(</span>resources<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">permitAll</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">antMatchers</span><span class="token punctuation">(</span><span class="token string">"/"</span><span class="token punctuation">,</span><span class="token string">"/index"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">permitAll</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">antMatchers</span><span class="token punctuation">(</span><span class="token string">"/admin*"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">access</span><span class="token punctuation">(</span><span class="token string">"hasRole('ADMIN')"</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">antMatchers</span><span class="token punctuation">(</span><span class="token string">"/user*"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">access</span><span class="token punctuation">(</span><span class="token string">"hasRole('USER') or hasRole('ADMIN')"</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">anyRequest</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">authenticated</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">and</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">formLogin</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">loginPage</span><span class="token punctuation">(</span><span class="token string">"/login"</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">permitAll</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">defaultSuccessUrl</span><span class="token punctuation">(</span><span class="token string">"/menu"</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">failureUrl</span><span class="token punctuation">(</span><span class="token string">"/login?error=true"</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">usernameParameter</span><span class="token punctuation">(</span><span class="token string">"username"</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">passwordParameter</span><span class="token punctuation">(</span><span class="token string">"password"</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">and</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">logout</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">permitAll</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">logoutSuccessUrl</span><span class="token punctuation">(</span><span class="token string">"/login?logout"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> BCryptPasswordEncoder bCryptPasswordEncoder<span class="token punctuation">;</span> <span class="token comment">//Crea el encriptador de contraseñas </span> <span class="token annotation punctuation">@Bean</span> <span class="token keyword">public</span> BCryptPasswordEncoder <span class="token function">passwordEncoder</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> bCryptPasswordEncoder <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">BCryptPasswordEncoder</span><span class="token punctuation">(</span><span class="token number">4</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//El numero 4 representa que tan fuerte quieres la encriptacion.</span> <span class="token comment">//Se puede en un rango entre 4 y 31. </span> <span class="token comment">//Si no pones un numero el programa utilizara uno aleatoriamente cada vez</span> <span class="token comment">//que inicies la aplicacion, por lo cual tus contrasenas encriptadas no funcionaran bien</span> <span class="token keyword">return</span> bCryptPasswordEncoder<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token annotation punctuation">@Autowired</span> UserDetailsServiceImpl userDetailsService<span class="token punctuation">;</span> <span class="token comment">//Registra el service para usuarios y el encriptador de contrasena</span> <span class="token annotation punctuation">@Autowired</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">configureGlobal</span><span class="token punctuation">(</span>AuthenticationManagerBuilder auth<span class="token punctuation">)</span> <span class="token keyword">throws</span> Exception <span class="token punctuation">{</span> <span class="token comment">// Setting Service to find User in the database.</span> <span class="token comment">// And Setting PassswordEncoder</span> auth<span class="token punctuation">.</span><span class="token function">userDetailsService</span><span class="token punctuation">(</span>userDetailsService<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">passwordEncoder</span><span class="token punctuation">(</span><span class="token function">passwordEncoder</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span>
JavaPrimero que todo cuando copies esta clase te saldrá un error con el objeto UserDetailsServiceImpl el cual crearemos después de esta explicación.
Como segundo te explicare la configuración implementada en el metodo configure:
- El metodo authorizeRequests() permite restringir y/o dar acceso request HTTP
- antMatchers(): Lista de URL que corresponden a un RequestMapping como lo hacemos en los controladores.
- permitAll(): Especifica que estas URLs son accesibles por cualquiera.
- access(): permite el acceso cumpliendo la expresión, en este caso tenemos la expresion “hasRole()”. Donde verifica si el usuario tiene ese especifico Role.
- anyRequest(): Ya que la configuración es lineal poniendo este metodo al final interpreta los request a las URLs que no fueron descritos, y en conjunto con el metodo authenticated() permite y da acceso a cualquier usuario que este autenticado.
- El metodo fromLogin(). Permite personalizar el proceso de inicio de sesión
- loginPage(): indica la url de la pagina de inicio de sesión
- defaultSuccessUrl(): Indica a cual URL sera redirigido cuando el usuario inicie sesión.
- failureUrl(): Indica a cual URL sera redirigido cuando el inicio de sesión falla.
- usernameParameter() y passwordParameter(): Indica el nombre de los parámetros respectivamente.
- El metodo logout(): Personaliza el proceso de cierre de sesión.
- logoutSuccessUrl(): Indica la URL donde sera redirigido cuando el usuario cierre sesión.
- El paso anterior habilita el componente de seguridad, pero hay que configurarlo. Crear la siguiente clase que hereda de WebSecurityConfigurerAdapter. Codigo Fuente
Capa de servicio y repositorio
- Empezaremos creando la siguiente estructura de archivos:
- Entidades | Entity: Siguiendo los lineamientos especificados por la documentación de Spring 5.2.0 Se necesitan dos tablas, una para Usuarios y otra para los Roles (Conocido como Authorities).Documentacion Oficial. Y esta es la implementacion con Spring JPA e Hibernate.
-
- Authority: Codigo Fuente
<span class="token keyword">package</span> com<span class="token punctuation">.</span>cristianruizblog<span class="token punctuation">.</span>loginSecurity<span class="token punctuation">.</span>entity<span class="token punctuation">;</span> <span class="token keyword">import</span> javax<span class="token punctuation">.</span>persistence<span class="token punctuation">.</span>Column<span class="token punctuation">;</span> <span class="token keyword">import</span> javax<span class="token punctuation">.</span>persistence<span class="token punctuation">.</span>Entity<span class="token punctuation">;</span> <span class="token keyword">import</span> javax<span class="token punctuation">.</span>persistence<span class="token punctuation">.</span>GeneratedValue<span class="token punctuation">;</span> <span class="token keyword">import</span> javax<span class="token punctuation">.</span>persistence<span class="token punctuation">.</span>GenerationType<span class="token punctuation">;</span> <span class="token keyword">import</span> javax<span class="token punctuation">.</span>persistence<span class="token punctuation">.</span>Id<span class="token punctuation">;</span> <span class="token annotation punctuation">@Entity</span> <span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Authority</span> <span class="token punctuation">{</span> <span class="token annotation punctuation">@Id</span> <span class="token annotation punctuation">@GeneratedValue</span><span class="token punctuation">(</span>strategy<span class="token operator">=</span>GenerationType<span class="token punctuation">.</span>AUTO<span class="token punctuation">)</span> <span class="token keyword">private</span> Long id<span class="token punctuation">;</span> <span class="token annotation punctuation">@Column</span> <span class="token keyword">private</span> String authority<span class="token punctuation">;</span> <span class="token keyword">public</span> String <span class="token function">getAuthority</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> authority<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">setAuthority</span><span class="token punctuation">(</span>String authority<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>authority <span class="token operator">=</span> authority<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span>
Java - User: Codigo Fuente
<span class="token keyword">package</span> com<span class="token punctuation">.</span>cristianruizblog<span class="token punctuation">.</span>loginSecurity<span class="token punctuation">.</span>entity<span class="token punctuation">;</span> <span class="token keyword">import</span> java<span class="token punctuation">.</span>util<span class="token punctuation">.</span>Set<span class="token punctuation">;</span> <span class="token keyword">import</span> javax<span class="token punctuation">.</span>persistence<span class="token punctuation">.</span>Column<span class="token punctuation">;</span> <span class="token keyword">import</span> javax<span class="token punctuation">.</span>persistence<span class="token punctuation">.</span>Entity<span class="token punctuation">;</span> <span class="token keyword">import</span> javax<span class="token punctuation">.</span>persistence<span class="token punctuation">.</span>FetchType<span class="token punctuation">;</span> <span class="token keyword">import</span> javax<span class="token punctuation">.</span>persistence<span class="token punctuation">.</span>GeneratedValue<span class="token punctuation">;</span> <span class="token keyword">import</span> javax<span class="token punctuation">.</span>persistence<span class="token punctuation">.</span>GenerationType<span class="token punctuation">;</span> <span class="token keyword">import</span> javax<span class="token punctuation">.</span>persistence<span class="token punctuation">.</span>Id<span class="token punctuation">;</span> <span class="token keyword">import</span> javax<span class="token punctuation">.</span>persistence<span class="token punctuation">.</span>JoinColumn<span class="token punctuation">;</span> <span class="token keyword">import</span> javax<span class="token punctuation">.</span>persistence<span class="token punctuation">.</span>JoinTable<span class="token punctuation">;</span> <span class="token keyword">import</span> javax<span class="token punctuation">.</span>persistence<span class="token punctuation">.</span>ManyToMany<span class="token punctuation">;</span> <span class="token keyword">import</span> com<span class="token punctuation">.</span>cristianruizblog<span class="token punctuation">.</span>loginSecurity<span class="token punctuation">.</span>entity<span class="token punctuation">.</span>Authority<span class="token punctuation">;</span> <span class="token annotation punctuation">@Entity</span> <span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">User</span> <span class="token punctuation">{</span> <span class="token annotation punctuation">@Id</span> <span class="token annotation punctuation">@GeneratedValue</span><span class="token punctuation">(</span>strategy<span class="token operator">=</span>GenerationType<span class="token punctuation">.</span>AUTO<span class="token punctuation">)</span> <span class="token keyword">private</span> Long id<span class="token punctuation">;</span> <span class="token annotation punctuation">@Column</span> <span class="token keyword">private</span> String username<span class="token punctuation">;</span> <span class="token annotation punctuation">@Column</span> <span class="token keyword">private</span> String password<span class="token punctuation">;</span> <span class="token annotation punctuation">@Column</span> <span class="token keyword">private</span> <span class="token keyword">boolean</span> enabled<span class="token punctuation">;</span> <span class="token annotation punctuation">@ManyToMany</span><span class="token punctuation">(</span>fetch <span class="token operator">=</span> FetchType<span class="token punctuation">.</span>EAGER<span class="token punctuation">)</span> <span class="token annotation punctuation">@JoinTable</span><span class="token punctuation">(</span>name<span class="token operator">=</span><span class="token string">"authorities_users"</span><span class="token punctuation">,</span> joinColumns<span class="token operator">=</span><span class="token annotation punctuation">@JoinColumn</span><span class="token punctuation">(</span>name<span class="token operator">=</span><span class="token string">"usuario_id"</span><span class="token punctuation">)</span><span class="token punctuation">,</span> inverseJoinColumns<span class="token operator">=</span><span class="token annotation punctuation">@JoinColumn</span><span class="token punctuation">(</span>name<span class="token operator">=</span><span class="token string">"authority_id"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">private</span> Set<span class="token operator"><</span>Authority<span class="token operator">></span> authority<span class="token punctuation">;</span> <span class="token comment">//Getters y Setters</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> <span class="token keyword">int</span> <span class="token function">hashCode</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">final</span> <span class="token keyword">int</span> prime <span class="token operator">=</span> <span class="token number">31</span><span class="token punctuation">;</span> <span class="token keyword">int</span> result <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span> result <span class="token operator">=</span> prime <span class="token operator">*</span> result <span class="token operator">+</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>id <span class="token operator">==</span> null<span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token number">0</span> <span class="token operator">:</span>id<span class="token punctuation">.</span><span class="token function">hashCode</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> result<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> <span class="token keyword">boolean</span> <span class="token function">equals</span><span class="token punctuation">(</span>Object obj<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span> <span class="token operator">==</span> obj<span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>obj <span class="token operator">==</span> null<span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">!=</span> obj<span class="token punctuation">.</span><span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span> User other <span class="token operator">=</span> <span class="token punctuation">(</span>User<span class="token punctuation">)</span> obj<span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>id <span class="token operator">==</span> null<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>other<span class="token punctuation">.</span>id <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>id<span class="token punctuation">.</span><span class="token function">equals</span><span class="token punctuation">(</span>other<span class="token punctuation">.</span>id<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> String <span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token string">"User [id="</span> <span class="token operator">+</span> id <span class="token operator">+</span> <span class="token string">", username="</span> <span class="token operator">+</span> username <span class="token operator">+</span> <span class="token string">", password="</span> <span class="token operator">+</span> password <span class="token operator">+</span> <span class="token string">"]"</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span>
Java
- Authority: Codigo Fuente
-
- Repositorios | Acceso a Datos: Por el momento tenemos una sola interface que hereda de CrudRepository y donde declaramos un metodo donde se busca por username. Si necesitas un poco mas de explicación acerca de esto puedes ver estos tutoriales. JPA Parte 1 y JPA Parte 2. Codigo Fuente
<span class="token keyword">package</span> com<span class="token punctuation">.</span>cristianruizblog<span class="token punctuation">.</span>loginSecurity<span class="token punctuation">.</span>repository<span class="token punctuation">;</span> <span class="token keyword">import</span> java<span class="token punctuation">.</span>util<span class="token punctuation">.</span>Optional<span class="token punctuation">;</span> <span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>data<span class="token punctuation">.</span>repository<span class="token punctuation">.</span>CrudRepository<span class="token punctuation">;</span> <span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>stereotype<span class="token punctuation">.</span>Repository<span class="token punctuation">;</span> <span class="token keyword">import</span> com<span class="token punctuation">.</span>cristianruizblog<span class="token punctuation">.</span>loginSecurity<span class="token punctuation">.</span>entity<span class="token punctuation">.</span>User<span class="token punctuation">;</span> <span class="token annotation punctuation">@Repository</span> <span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">UserRepository</span> <span class="token keyword">extends</span> <span class="token class-name">CrudRepository</span><span class="token operator"><</span>User<span class="token punctuation">,</span> Long<span class="token operator">></span> <span class="token punctuation">{</span> <span class="token keyword">public</span> Optional<span class="token operator"><</span>User<span class="token operator">></span> <span class="token function">findByUsername</span><span class="token punctuation">(</span>String username<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
Java - Service: Necesitaremos crear la implementacion para la interface UserDetailsService, la cual esta dentro de la dependencia de Spring Security Codigo Fuente
<span class="token keyword">package</span> com<span class="token punctuation">.</span>cristianruizblog<span class="token punctuation">.</span>loginSecurity<span class="token punctuation">.</span>service<span class="token punctuation">;</span>
<span class="token keyword">import</span> java<span class="token punctuation">.</span>util<span class="token punctuation">.</span>ArrayList<span class="token punctuation">;</span>
<span class="token keyword">import</span> java<span class="token punctuation">.</span>util<span class="token punctuation">.</span>List<span class="token punctuation">;</span>
<span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>beans<span class="token punctuation">.</span>factory<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>Autowired<span class="token punctuation">;</span>
<span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>security<span class="token punctuation">.</span>core<span class="token punctuation">.</span>GrantedAuthority<span class="token punctuation">;</span>
<span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>security<span class="token punctuation">.</span>core<span class="token punctuation">.</span>authority<span class="token punctuation">.</span>SimpleGrantedAuthority<span class="token punctuation">;</span>
<span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>security<span class="token punctuation">.</span>core<span class="token punctuation">.</span>userdetails<span class="token punctuation">.</span>User<span class="token punctuation">;</span>
<span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>security<span class="token punctuation">.</span>core<span class="token punctuation">.</span>userdetails<span class="token punctuation">.</span>UserDetails<span class="token punctuation">;</span>
<span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>security<span class="token punctuation">.</span>core<span class="token punctuation">.</span>userdetails<span class="token punctuation">.</span>UserDetailsService<span class="token punctuation">;</span>
<span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>security<span class="token punctuation">.</span>core<span class="token punctuation">.</span>userdetails<span class="token punctuation">.</span>UsernameNotFoundException<span class="token punctuation">;</span>
<span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>stereotype<span class="token punctuation">.</span>Service<span class="token punctuation">;</span>
<span class="token keyword">import</span> com<span class="token punctuation">.</span>cristianruizblog<span class="token punctuation">.</span>loginSecurity<span class="token punctuation">.</span>entity<span class="token punctuation">.</span>Authority<span class="token punctuation">;</span>
<span class="token keyword">import</span> com<span class="token punctuation">.</span>cristianruizblog<span class="token punctuation">.</span>loginSecurity<span class="token punctuation">.</span>repository<span class="token punctuation">.</span>UserRepository<span class="token punctuation">;</span>
<span class="token annotation punctuation">@Service</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">UserDetailsServiceImpl</span> <span class="token keyword">implements</span> <span class="token class-name">UserDetailsService</span> <span class="token punctuation">{</span>
<span class="token annotation punctuation">@Autowired</span>
UserRepository userRepository<span class="token punctuation">;</span>
<span class="token annotation punctuation">@Override</span>
<span class="token keyword">public</span> UserDetails <span class="token function">loadUserByUsername</span><span class="token punctuation">(</span>String username<span class="token punctuation">)</span> <span class="token keyword">throws</span> UsernameNotFoundException <span class="token punctuation">{</span>
<span class="token comment">//Buscar el usuario con el repositorio y si no existe lanzar una exepcion</span>
com<span class="token punctuation">.</span>cristianruizblog<span class="token punctuation">.</span>loginSecurity<span class="token punctuation">.</span>entity<span class="token punctuation">.</span>User appUser <span class="token operator">=</span>
userRepository<span class="token punctuation">.</span><span class="token function">findByUsername</span><span class="token punctuation">(</span>username<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">orElseThrow</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token keyword">new</span> <span class="token class-name">UsernameNotFoundException</span><span class="token punctuation">(</span><span class="token string">"No existe usuario"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">//Mapear nuestra lista de Authority con la de spring security </span>
List grantList <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ArrayList</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">for</span> <span class="token punctuation">(</span>Authority authority<span class="token operator">:</span> appUser<span class="token punctuation">.</span><span class="token function">getAuthority</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment">// ROLE_USER, ROLE_ADMIN,..</span>
GrantedAuthority grantedAuthority <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">SimpleGrantedAuthority</span><span class="token punctuation">(</span>authority<span class="token punctuation">.</span><span class="token function">getAuthority</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
grantList<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>grantedAuthority<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">//Crear El objeto UserDetails que va a ir en sesion y retornarlo.</span>
UserDetails user <span class="token operator">=</span> <span class="token punctuation">(</span>UserDetails<span class="token punctuation">)</span> <span class="token keyword">new</span> <span class="token class-name">User</span><span class="token punctuation">(</span>appUser<span class="token punctuation">.</span><span class="token function">getUsername</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> appUser<span class="token punctuation">.</span><span class="token function">getPassword</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> grantList<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">return</span> user<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
Crear usuarios.
Necesitaremos insertar datos en las tablas que tenemos en la base de datos. Empezaremos por crear los usuarios y las autoridades y luego asignaremos los cruzaremos.
Usuarios: Para poder crear un usuario necesitaremos la contraseña encriptada por lo cual crearemos la siguiente clase para simular el encriptador. Codigo Fuente
<span class="token keyword">package</span> com<span class="token punctuation">.</span>cristianruizblog<span class="token punctuation">.</span>loginSecurity<span class="token punctuation">.</span>util<span class="token punctuation">;</span>
<span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>security<span class="token punctuation">.</span>crypto<span class="token punctuation">.</span>bcrypt<span class="token punctuation">.</span>BCryptPasswordEncoder<span class="token punctuation">;</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Passgenerator</span> <span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span>String <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>args<span class="token punctuation">)</span> <span class="token punctuation">{</span>
BCryptPasswordEncoder bCryptPasswordEncoder <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">BCryptPasswordEncoder</span><span class="token punctuation">(</span><span class="token number">4</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">//El String que mandamos al metodo encode es el password que queremos encriptar.</span>
System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>bCryptPasswordEncoder<span class="token punctuation">.</span><span class="token function">encode</span><span class="token punctuation">(</span><span class="token string">"1234"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
Esta clase deberá de ejecutarse como aplicación Java, y la contraseña encriptada aparecerá en tu consola.
Ahora crearemos un usuario admin y user
Reemplaza la palabra password por lo que apareció en tu consola en el paso anterior<span class="token punctuation">.</span>
<span class="token keyword">INSERT</span> <span class="token keyword">INTO</span> <span class="token keyword">user</span> <span class="token punctuation">(</span>id<span class="token punctuation">,</span>enabled<span class="token punctuation">,</span>password<span class="token punctuation">,</span>username<span class="token punctuation">)</span>
<span class="token keyword">VALUES</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">0b1</span><span class="token punctuation">,</span><span class="token string">"password"</span><span class="token punctuation">,</span><span class="token string">"admin"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">INSERT</span> <span class="token keyword">INTO</span> <span class="token keyword">user</span> <span class="token punctuation">(</span>id<span class="token punctuation">,</span>enabled<span class="token punctuation">,</span>password<span class="token punctuation">,</span>username<span class="token punctuation">)</span>
<span class="token keyword">VALUES</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span><span class="token number">0b1</span><span class="token punctuation">,</span><span class="token string">"password"</span><span class="token punctuation">,</span><span class="token string">"user"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">INSERT</span> <span class="token keyword">INTO</span> authority <span class="token punctuation">(</span>id<span class="token punctuation">,</span>authority<span class="token punctuation">)</span> <span class="token keyword">VALUES</span> <span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token string">"ROLE_ADMIN"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">INSERT</span> <span class="token keyword">INTO</span> authority <span class="token punctuation">(</span>id<span class="token punctuation">,</span>authority<span class="token punctuation">)</span> <span class="token keyword">VALUES</span> <span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span><span class="token string">"ROLE_USER"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">INSERT</span> <span class="token keyword">INTO</span> authorities_users <span class="token punctuation">(</span>usuario_id<span class="token punctuation">,</span> authority_id<span class="token punctuation">)</span> <span class="token keyword">VALUES</span> <span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">INSERT</span> <span class="token keyword">INTO</span> authorities_users <span class="token punctuation">(</span>usuario_id<span class="token punctuation">,</span> authority_id<span class="token punctuation">)</span> <span class="token keyword">VALUES</span> <span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">INSERT</span> <span class="token keyword">INTO</span> authorities_users <span class="token punctuation">(</span>usuario_id<span class="token punctuation">,</span> authority_id<span class="token punctuation">)</span> <span class="token keyword">VALUES</span> <span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
PRECAUCION:
Hay que cambiar el index.html un poco. El action hay que apuntarlo a /login y el metodo debe ser post.
<form class="col-12" th:action="@{/login}" method="post">
Ya solo falta ejecutar la aplicación con ambos usuarios y verificar que con el usuario user no se puede acceder al link de admin
Bueno esto fue todo, no dudes en dejar tu comentario ademas puedes ver mi codigo completo en mi repositorio de github o ver este ejemplo en video. Muchas gracias por llegar al final del tutorial.
GitHub: https://github.com/cruizg93/SpringBoot-Security-MySql
No tienes el script de la base de datos?. Me sale Unknown database ‘mydatabase’
OK, no sabía que spring.jpa.hibernate.ddl-auto=create crea las tabla pero no la base de datos. Solo tenía que tener la base de datos ya creada. Gracias por el tutorial.
Con mucho gusto “a”,
le tienes q poner el nombre de tu base de datos
INSERT INTO user (id, enabled, password, username)
VALUES (1, TRUE, ‘$2a$04$GR0Lqg9.0KSqaGnOXxkXCOIe3mLumryflpMUgrJAOs1fq6zHBTf4q’, ‘admin’),
(2, TRUE, ‘$2a$04$GR0Lqg9.0KSqaGnOXxkXCOIe3mLumryflpMUgrJAOs1fq6zHBTf4q’, ‘user’);
INSERT INTO authority (id, authority)
VALUES (1, ‘ROLE_ADMIN’),
(2, ‘ROLE_USER’);
INSERT INTO authorities_users (user_id, authority_id)
VALUES (1, 1),
(1, 2),
(2, 2);
— password
Buenas tardes, felicidades por su valioso aporte. Como hago si quiero implementar spring security, pero solo a mi servicio rest. especificamente quiero proteger cada endpoint por separado, por medio de los roles. he leido sobre @PreAuthorize pero no logro entenderlo del todo..
El front esta en angular.
Hola Yesid, puedes mirar este tutorial, en el punto 4 explico como agregar la seguridad por roles y como configurar la anotacion @Pre and @Post authorize.
http://cristianruizblog.com/spring-boot-aplicacion-web-parte-9/
Buenas noches segui tu tutorial y esta bien explicado, solo que lo estaba haciendo con spring boot y oracle pero a la hora de iniciar session no me hace match el usuario y contraseña, no se si me puedes ayudar, en oracle no me deja crear la tabla user.