Registro e autorização usando o Spring Security usando um aplicativo simples como exemplo

Boa tarde

Este artigo abordará a criação de um aplicativo Web simples usando o Spring Boot e o Spring Security. O aplicativo implementará o registro de novos usuários e a autorização, restringindo o acesso às páginas do site, dependendo da função do usuário.
O objetivo principal do artigo é mostrar como você pode restringir o acesso a várias páginas do site para usuários com funções diferentes.

Qual será o aplicativo?


Site com as seguintes páginas:

  • páginas acessíveis a todos os usuários: home, registro e login;
  • página disponível para usuários registrados: notícias;
  • página de administração disponível.

O que vamos usar


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

Conteúdo


  1. Descrição das principais anotações utilizadas.
  2. Crie um novo projeto no IDE.
  3. Criando uma estrutura de projeto (pacotes).
  4. Adicionando entidades, controladores, serviços, repositórios e visualizações.
  5. Lançamento de aplicativo.

1. Descrição das principais anotações utilizadas


Controller é um tipo especial de classe usado em aplicativos MVC. Parece um servlet HttpServlet normal que funciona com os objetos HttpServletRequest e HttpServletResponse, mas com recursos avançados do Spring Framework.
Repositório - indica que a classe é usada para especificar a lista
trabalho necessário para pesquisar, recuperar e armazenar dados. A anotação pode ser usada para implementar o modelo DAO.
Serviço - indica que a classe é um serviço para implementar a lógica de negócios.
Configuração - essa anotação é usada para classes que definem componentes de bean.
Ligação automática - a anotação permite definir automaticamente o valor do campo. A funcionalidade desta anotação é que não precisamos nos preocupar com a melhor maneira de passar uma cópia de outro Bean para um Bean. A própria Spring encontrará o Bean desejado e substituirá seu valor na propriedade que está marcada com a anotação.

Um pouco de informação sobre Spring Security


O objeto mais fundamental é o SecurityContextHolder . Ele armazena informações sobre o contexto de segurança atual do aplicativo, que inclui informações detalhadas sobre o usuário (principal) que trabalha com o aplicativo. O Spring Security usa um objeto de autenticação , um usuário autorizado da sessão.

Um "usuário" é apenas um objeto. Na maioria dos casos, pode ser
convertido para a classe UserDetails . Os UserDetails podem ser vistos como um adaptador entre os bancos de dados do usuário e o que o Spring Security exige dentro do SecurityContextHolder .

Para criar um UserDetails , a interface UserDetailsService é usada, com um único método:

UserDetails loadUserByUsername(String username) throws UsernameNotFoundException 

2. Criando um novo projeto no IDE


Usaremos o sistema de compilação Maven.



GroupId significa o identificador exclusivo da empresa (ou seu nome de domínio pessoal) que libera o projeto. ArtefactId é apenas o nome do nosso projeto.



Após a conclusão da criação do projeto, o arquivo pom.xml é aberto , o Idea oferece a habilitação da importação automática - não recuse. Este arquivo conterá todas as dependências (bibliotecas) usadas no projeto.



3. Criando uma estrutura de projeto (pacotes)


Imediatamente, passe à criação de pacotes. A estrutura do projeto, que deve sair, é mostrada abaixo.



Agora, brevemente, sobre o que será armazenado em cada pacote:

  • src \ main \ java \ com \ boots \ config - classes com configurações para MVC (MvcConfig) e segurança (WebSecurityConfig);
  • src \ main \ java \ com \ boots \ controller - classes com controladores;
  • src \ main \ java \ com \ boots \ entity - classes com modelos;
  • src \ main \ java \ com \ boots \ repository - interfaces de repositório ;
  • src \ main \ java \ com \ boots \ service - classes com serviços para modelos;
  • src \ main \ webapp \ resources - objetos estáticos: js, css, img;
  • src \ main \ webapp \ WEB-INF \ jsp - representações na forma de arquivos .jsp.

Considere o arquivo pom.xml . Nesse arquivo, você precisa especificar um link para o arquivo pai usando a tag pai , ou seja, todas as propriedades e dependências do pai serão adicionadas a esse arquivo filho.

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

Em seguida, adicione as dependências dos módulos Spring, o driver de banco de dados PostgreSQL, servidor Tomcat, JSTL.

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

Por padrão, o maven usará a versão antiga do java 1.6. Para corrigir isso, especificamos a versão explicitamente.

 <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> 

Também adicionamos um plug-in que permite compactar arquivos jar ou war e executá-los "no local":

 <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> 


Preencha o arquivo application.properties . As três primeiras linhas contêm dados para conexão com o banco de dados (o nome do banco de dados é “primavera”, login e senha). As duas últimas linhas indicam o caminho para os arquivos .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 

A propriedade spring.jpa.show-sql exibe os corpos das consultas ao banco de dados no console.
O spring.jpa.hibernate.ddl-auto permite definir uma estratégia para criar um banco de dados com base em nossos modelos; possui valores diferentes (nenhum, criar, atualizar, etc.). atualizar neste caso significa que as tabelas e os campos do banco de dados serão criados com base em nossos modelos e serão alterados com eles.

Olhando para o futuro, precisaremos apenas criar um banco de dados com o nome spring , e as tabelas de usuário, funções e sua tabela de links, juntamente com chaves estrangeiras, serão geradas automaticamente com base nos modelos (pacote de entidades), que iremos agora criar.

4. Adicionando entidades, controladores, serviços, repositórios e visualizações


4.1 Adicionando entidades (modelos)


Um requisito obrigatório para todas as entidades: campos privados, getters e setters para todos os campos e um construtor vazio (não mostrado nos exemplos). Eles não precisam ser escritos manualmente, pressione Alt + Insert e o Idea fará isso por você.

Para importar as classes e bibliotecas necessárias, use o atalho de teclado Alt + Enter .



Usuário
 @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; } } 


Usuário No início sobre anotações: A entidade diz que os campos da classe têm um mapa no banco de dados, Tabela (name = "t_user") indica qual tabela.

O parâmetro GenerationType.IDENTITY IDENTITY significa que o banco de dados estará envolvido na geração do ID. Existem outras estratégias. SEQUENCE - usa o mecanismo de banco de dados embutido, como PostgreSQL ou Oracle, um mecanismo para gerar valores sequenciais (sequência). TABELA - usa uma tabela separada com valores-chave inicializados. Outra opção é AUTO, o próprio hibernate escolherá uma das estratégias acima, mas é recomendável especificar explicitamente a estratégia.

O campo na anotação transitória não é exibido no banco de dados. A lista de funções está associada ao usuário, um relacionamento muitos para muitos (um usuário pode ter várias funções, por um lado, e uma função pode ter vários usuários, por outro); FetchType.EAGER - download "ganancioso", ou seja, a lista de funções é carregada imediatamente com o usuário (não espera até que eles sejam contatados).

Para usar ainda mais a classe User no Spring Security, ele deve implementar a interface UserDetails . Para fazer isso, substitua todos os seus métodos. Porém, em nosso exemplo, usaremos apenas o método getAuthorities () ; ele retornará uma lista de funções de usuário. Portanto, para os métodos restantes, altere o valor de retorno para true .

Função
 @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(); } } 


Função Essa classe deve implementar a interface GrantedAuthority , na qual apenas um método getAuthority () precisa ser redefinido (retorna o nome da função). O nome da função deve corresponder ao padrão: "ROLE_NAME", por exemplo, ROLE_USER . Além do construtor padrão, você deve adicionar mais alguns construtores públicos: o primeiro aceita apenas ID, o segundo ID e nome.

Aqui você pode adicionar restrições de campo. Tamanho (min = 2) - significa que o comprimento mínimo do campo é 2; se a restrição for violada, uma mensagem será exibida.

4.2 Implementando uma camada de acesso a dados e uma camada de serviço


O Spring Data fornece um conjunto de implementações prontas para criar uma camada que fornece acesso ao banco de dados. A interface JpaRepository fornece um conjunto de métodos padrão (findBy, save, deleteById, etc.) para trabalhar com o banco de dados.

UserRepository. Criamos a interface do usuário no pacote do repositório e herdamos o JpaRepository <User, Long> , especificamos a classe User e seu tipo de ID é Long .

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

T.O. simplesmente criando uma interface e herdando o JpaRepository, você pode executar consultas padrão no banco de dados. Se você precisar de um método específico, basta adicioná-lo à interface, com base nas dicas do Idea. Por exemplo, precisamos de um método para procurar um usuário em um banco de dados por nome. Escrevemos o tipo do objeto retornado e, em seguida, o IDE oferece opções possíveis. I.e. nesse caso, o nome do método determina o corpo da solicitação.



Se necessário, você pode usar a anotação de Consulta sobre o método e gravar consultas em HQL ou SQL (você precisa adicionar nativeQuery = true).

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

RoleRepository. Criamos da mesma maneira, não precisamos de nossos próprios métodos aqui.

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

UserService. Contém métodos para a lógica de negócios do aplicativo. Esta classe implementa a interface UserDetailsService (necessária para o Spring Security), na qual você precisa substituir um método loadUserByUsername () .

Nesta classe, você pode ver outra maneira de executar uma consulta SQL - usando o EntityManager.

Serviço ao usuário
 @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 o método saveUser (Usuário usuário) .

 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; } 

Primeiro, uma pesquisa é realizada no banco de dados pelo nome do usuário; se já existir um usuário com o mesmo nome, o método termina o trabalho. Se o nome de usuário não for usado, a função ROLE_USER será adicionada. Para não armazenar a senha em formato bruto, ela é previamente hash usando bCryptPasswordEncoder . Em seguida, o novo usuário é salvo no banco de dados.

4.3 Adicionando controladores


Para páginas que não são processadas pelo servidor de forma alguma, mas simplesmente retornam a página, o mapeamento pode ser configurado na configuração. A página de login é tratada pelo controlador Spring Security por padrão, portanto, um controlador separado não é necessário para ele.

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

RegistrationController. Um controlador separado é necessário para a página de registro. Para processar uma solicitação GET, a anotação @GetMapping ("/ registration") é usada para POST - @PostMapping ("/ registration") .

RegistrationController
 @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 adicionar ou obter algo da página, passamos ao modelo. Em uma solicitação GET, um novo objeto vazio da classe User é adicionado à página. Isso é feito para não obter dados do formulário de registro, um de cada vez, durante uma solicitação POST (nome de usuário, senha, passwordComfirm), mas obter imediatamente o objeto userForm preenchido.

O método addUser () espera como parâmetro o objeto de usuário (userForm) que foi adicionado durante a solicitação GET. Anotação Válida verifica se as restrições definidas nos campos são cumpridas, neste caso com pelo menos 2 caracteres. Se as restrições não foram cumpridas, bindingResult conterá erros.

Se a senha e sua confirmação não corresponderem, adicione uma mensagem à página e devolva-a. No final, tentamos salvar adicionar o usuário ao banco de dados.

O método saveUser () retorna false se um usuário com o mesmo nome já existir e true se o usuário for salvo no banco de dados. Se a tentativa de salvar falhar, adicionamos uma mensagem de erro e retornamos a página. Se o usuário for salvo com sucesso, vá para a página principal.

Admincontroller Somente usuários administrativos têm acesso à página de administração. Não há nada novo no método userList () ; ele recebe os dados de todos os usuários e os adiciona à 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"; } } 


O método deleteUser () usa a anotação RequestParam, ou seja, a visualização terá um formulário que deve passar dois parâmetros - userId e action. O link terá o formato http: // localhost: 8080 / admin? UserId = 24 & action = delete quando essa solicitação for executada, o usuário com id = 24 será excluído.

Outra opção para passar parâmetros para uma URL é usar PathVariable . Usando esta anotação, obtemos as partes individuais da URL, para o método getUser () , a URL terá a seguinte aparência: http: // localhost: 8080 / admin / gt / 24 , após a transição, uma lista de todos os usuários com id> 24 será exibida.

Configurações de segurança

WebSecurityConfig. Contém 2 beans BCryptPasswordEncoder e AuthenticationManager , que foram atendidos anteriormente na classe userService.
Além disso, o método configure () configura o acesso a vários recursos do site. Como parâmetros do método antMatchers () , passamos os caminhos para os quais queremos definir um limite. Em seguida, indicamos aos usuários com qual papel essa página / páginas estará disponível.

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 Adicionando vistas


index.jsp Página inicial, abaixo estão 2 opções - para um convidado e um usuário 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 do conteúdo da página para usuários autorizados (link para a página de registro e autorização), você pode usar a marca de autorização na biblioteca de marcas do Spring Security. O parâmetro de acesso aceita várias expressões, por exemplo, você pode definir uma restrição dependendo da função do usuário hasRole ('ADMIN') .

registration.jsp Página de registro.


registration.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> 


Nesta página, a tag do formulário é usada na biblioteca de tags, com a ajuda do atributo do modelo userForm é vinculado (nós a adicionamos à página durante a solicitação GET no controlador) e os formulários:

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

Você também deve especificar o caminho para ligar as propriedades userForm:

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

login.jsp Página de login .



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 já mencionado, é tratada pelo controlador Spring por padrão. É importante especificar a ação: action = "/ login" e o nome da entrada.

página de administração 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 A página de notícias é estática. É usado apenas para demonstrar os direitos do usuário, portanto, o conteúdo de sua escolha.

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 o aplicativo


Na classe principal Application, adicione o seguinte:

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

Antes de prosseguir para a próxima etapa, verifique se a estrutura do seu projeto corresponde à apresentada no início.

É hora de criar um banco de dados vazio chamado spring , você precisa fazer isso antes do primeiro aplicativo ser iniciado e apenas uma vez.

Você pode executar o aplicativo e ver como o banco de dados é alterado - 3 tabelas vazias serão criadas nele. Você precisa adicionar funções de usuário à tabela t_role:
Consulta SQL adicionando funções
 INSERT INTO public.t_role(id, name) VALUES (1, 'ROLE_USER'), (2, 'ROLE_ADMIN'); 


Agora você pode tentar se registrar. O aplicativo não fornece um método para registrar um usuário administrador, mas é necessário para demonstração. Portanto, depois de registrar um novo usuário, adicione uma entrada à tabela de funções do usuário que fornece esta função:

Consulta SQL adicionando função de administrador
 INSERT INTO public.t_user_roles(user_id, roles_id) VALUES (1, 2); 




Se, após adicionar direitos de administrador, você não puder acessar a página do administrador (erro 403), acesse o site. Somente o usuário com a função de administrador tem acesso

à página http: // localhost: 8080 / admin . http: // localhost: 8080 / news que qualquer usuário registrado verá. Você também pode tentar acessar as páginas de registro e login, sendo autorizado no site.

Conclusão


Como resultado, foi criada uma aplicação web na qual podemos controlar o acesso do usuário às páginas do site usando funções. Você pode atribuir várias funções a um usuário. Por exemplo, ao registrar um novo usuário, adicionamos a função Usuário, que é básica para todos, e, ao solicitar privilégios adicionais pelo usuário, podemos atribuir a ele a função Escritor, que permitirá adicionar novas notícias ao site.

Os arquivos css e js foram criados, mas seu conteúdo não foi apresentado. Se desejar, você pode adicionar design, por exemplo, usando o Bootstrap e interatividade usando js.

Faça o download do projeto finalizado.

Fontes utilizadas


  1. Registro e login com Spring Boot, Spring Security, Spring Data JPA, Hibernate, MySQL, JSP, Bootstrap e Docker Compose
  2. O outro lado da primavera
  3. Revisão técnica Spring Security / Spring Security
  4. Spring
  5. : Spring 3 MVC + Spring Security + Hibernate

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


All Articles