Registrierung und Autorisierung mit Spring Security am Beispiel einer einfachen Anwendung

Guten Tag!

Dieser Artikel behandelt das Erstellen einer einfachen Webanwendung mit Spring Boot und Spring Security. Die Anwendung implementiert die Registrierung neuer Benutzer und die Autorisierung, wodurch der Zugriff auf die Seiten der Site abhängig von der Rolle des Benutzers eingeschränkt wird.
Das Hauptziel des Artikels ist es zu zeigen, wie Sie den Zugriff auf verschiedene Seiten der Site für Benutzer mit unterschiedlichen Rollen einschränken können.

Wie wird die Bewerbung sein?


Website mit folgenden Seiten:

  • Für alle Benutzer zugängliche Seiten: Startseite, Registrierung und Anmeldung;
  • Seite für registrierte Benutzer verfügbar: Nachrichten;
  • Admin-Seite verfügbar.

Was wir verwenden werden


  • JDK 8+;
  • Intellij Idee;
  • Feder (Spring Boot, Spring MVC, Spring Security);
  • Ruhezustand
  • JSP
  • PostgreSQL

Inhalt


  1. Beschreibung der wichtigsten verwendeten Anmerkungen.
  2. Erstellen Sie ein neues Projekt in der IDE.
  3. Erstellen einer Projektstruktur (Pakete).
  4. Hinzufügen von Entitäten, Controllern, Diensten, Repositorys und Ansichten.
  5. Anwendungsstart.

1. Beschreibung der wichtigsten verwendeten Anmerkungen


Controller ist ein spezieller Klassentyp, der in MVC-Anwendungen verwendet wird. Es sieht aus wie ein reguläres HttpServlet-Servlet, das mit HttpServletRequest- und HttpServletResponse-Objekten zusammenarbeitet, jedoch mit erweiterten Funktionen von Spring Framework.
Repository - Gibt an, dass die Klasse zum Angeben der Liste verwendet wird
notwendige Arbeit zum Suchen, Abrufen und Speichern von Daten. Anmerkungen können verwendet werden, um die DAO-Vorlage zu implementieren.
Service - Gibt an, dass die Klasse ein Service zum Implementieren von Geschäftslogik ist.
Konfiguration - Diese Annotation wird für Klassen verwendet, die Bean-Komponenten definieren.
Autowired - Mit Annotation können Sie den Wert des Felds automatisch festlegen. Die Funktionalität dieser Annotation besteht darin, dass wir uns keine Gedanken darüber machen müssen, wie eine Kopie einer anderen Bean am besten an eine Bean übergeben werden kann. Spring selbst findet die gewünschte Bean und ersetzt ihren Wert in der Eigenschaft, die mit der Anmerkung markiert ist.

Ein paar Informationen zu Spring Security


Das grundlegendste Objekt ist der SecurityContextHolder . Es speichert Informationen zum aktuellen Sicherheitskontext der Anwendung, einschließlich detaillierter Informationen zum Benutzer (Prinzipal), der mit der Anwendung arbeitet. Spring Security verwendet ein Authentifizierungsobjekt , einen autorisierten Sitzungsbenutzer.

Ein „Benutzer“ ist nur ein Objekt. In den meisten Fällen kann es sein
in die UserDetails- Klasse umgewandelt. UserDetails können als Adapter zwischen Benutzerdatenbanken und den Anforderungen von Spring Security in SecurityContextHolder betrachtet werden .

Um UserDetails zu erstellen, wird die UserDetailsService- Schnittstelle mit einer einzigen Methode verwendet:

UserDetails loadUserByUsername(String username) throws UsernameNotFoundException 

2. Erstellen Sie ein neues Projekt in der IDE


Wir werden das Maven-Build-System verwenden.



GroupId bezeichnet die eindeutige Kennung des Unternehmens (oder Ihres persönlichen Domainnamens), das das Projekt freigibt. ArtefactId ist nur der Name unseres Projekts.



Nachdem die Erstellung des Projekts abgeschlossen ist, wird die Datei pom.xml geöffnet . Idea bietet an, den automatischen Import zu aktivieren - nicht ablehnen. Diese Datei enthält alle im Projekt verwendeten Abhängigkeiten (Bibliotheken).



3. Erstellen einer Projektstruktur (Pakete)


Fahren Sie sofort mit dem Erstellen von Paketen fort. Die Projektstruktur, die sich herausstellen sollte, ist unten dargestellt.



Nun kurz darüber, was in jedem Paket gespeichert wird:

  • src \ main \ java \ com \ boots \ config - Klassen mit Konfigurationen für MVC (MvcConfig) und Sicherheit (WebSecurityConfig);
  • src \ main \ java \ com \ boots \ controller - Klassen mit Controllern;
  • src \ main \ java \ com \ boots \ entity - Klassen mit Modellen;
  • src \ main \ java \ com \ boots \ repository - Repository- Schnittstellen;
  • src \ main \ java \ com \ boots \ service - Klassen mit Diensten für Modelle;
  • src \ main \ webapp \ resources - statische Objekte: js, css, img;
  • src \ main \ webapp \ WEB-INF \ jsp - Darstellungen in Form von .jsp-Dateien.

Betrachten Sie die Datei pom.xml . In dieser Datei müssen Sie mithilfe des übergeordneten Tags einen Link zur übergeordneten Datei angeben, d. H. Alle Eigenschaften und Abhängigkeiten des übergeordneten Elements werden dieser untergeordneten Datei hinzugefügt.

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

Fügen Sie als Nächstes die Abhängigkeiten für die Spring-Module, den PostgreSQL-Datenbanktreiber, den Tomcat-Server und JSTL hinzu.

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

Standardmäßig verwendet maven die alte Version von Java 1.6. Um dies zu beheben, geben wir die Version explizit an.

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

Wir haben auch ein Plugin hinzugefügt, mit dem Sie Jar- oder Kriegsarchive packen und "an Ort und Stelle" ausführen können:

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

Vollständige pom.xml
 <?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> 


Füllen Sie die Datei application.properties aus . Die ersten drei Zeilen enthalten Daten für die Verbindung zur Datenbank (der Datenbankname lautet „spring“, login und password). Die letzten beiden Zeilen geben den Pfad zu den .jsp-Dateien an:
 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 

Die Eigenschaft spring.jpa.show-sql zeigt die Körper von Datenbankabfragen an der Konsole an.
Mit spring.jpa.hibernate.ddl-auto können Sie eine Strategie für die Erstellung einer Datenbank festlegen, die auf unseren Modellen basiert und unterschiedliche Werte hat (keine, erstellen, aktualisieren usw.). update bedeutet in diesem Fall, dass die Datenbanktabellen und -felder auf Basis unserer Modelle erstellt und mit diesen geändert werden.

In Zukunft müssen wir nur eine Datenbank mit dem Namen spring erstellen, und die Benutzertabellen, Rollen und ihre Verknüpfungstabelle sowie Fremdschlüssel werden automatisch auf der Grundlage der Modelle (Entitätspaket) generiert, die wir jetzt erstellen.

4. Hinzufügen von Entitäten, Controllern, Diensten, Repositorys und Ansichten


4.1. Entitäten hinzufügen (Modelle)


Eine obligatorische Anforderung für alle Entitäten: private Felder, Getter und Setter für alle Felder und ein leerer Konstruktor (in den Beispielen nicht gezeigt). Sie müssen nicht manuell geschrieben werden, drücken Sie Alt + Einfügen und Idea erledigt dies für Sie.

Verwenden Sie die Tastenkombination Alt + Eingabetaste, um die erforderlichen Klassen und Bibliotheken zu importieren.



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


Benutzer Zu Beginn zu Anmerkungen: Entity gibt an, dass die Felder der Klasse eine Zuordnung in der Datenbank haben. Table (name = "t_user") gibt an, welche Tabelle vorhanden ist.

Der Parameter GenerationType.IDENTITY IDENTITY bedeutet, dass die Datenbank die ID generiert. Es gibt andere Strategien. SEQUENCE - verwendet die integrierte Datenbank-Engine wie PostgreSQL oder Oracle, einen Mechanismus zum Generieren sequentieller Werte (Sequenzen). TABLE - Verwendet eine separate Tabelle mit initialisierten Schlüsselwerten. Eine andere Option ist AUTO. Im Ruhezustand wird eine der oben genannten Strategien ausgewählt. Es wird jedoch empfohlen, die Strategie explizit anzugeben.

Das Feld unter der transienten Annotation wird in der Datenbank nicht angezeigt. Die Liste der Rollen ist dem Benutzer zugeordnet, eine Viele-zu-Viele-Beziehung (ein Benutzer kann auf der einen Seite mehrere Rollen haben und eine Rolle kann auf der anderen Seite mehrere Benutzer haben). FetchType.EAGER - "gieriger" Download, d.h. Die Liste der Rollen wird sofort mit dem Benutzer geladen (wartet nicht, bis er kontaktiert wird).

Um die User- Klasse in Spring Security weiter verwenden zu können, muss die UserDetails- Schnittstelle implementiert werden. Überschreiben Sie dazu alle Methoden. In unserem Beispiel wird jedoch nur die Methode getAuthorities () verwendet. Sie gibt eine Liste der Benutzerrollen zurück. Ändern Sie daher für die übrigen Methoden den Rückgabewert auf true .

Rolle
 @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(); } } 


Rolle Diese Klasse muss die GrantedAuthority- Schnittstelle implementieren, in der nur eine getAuthority () -Methode neu definiert werden muss (gibt den Namen der Rolle zurück). Der Rollenname muss mit dem Muster "ROLE_NAME" übereinstimmen, z. B. ROLE_USER . Zusätzlich zum Standardkonstruktor müssen Sie einige weitere öffentliche Konstruktoren hinzufügen: Der erste akzeptiert nur die ID, der zweite die ID und den Namen.

Hier können Sie Feldeinschränkungen hinzufügen. Größe (min = 2) - bedeutet, dass die minimale Feldlänge 2 ist. Wenn die Einschränkung verletzt wird, wird eine Meldung angezeigt.

4.2. Implementieren einer Datenzugriffsschicht und einer Dienstschicht


Spring Data bietet eine Reihe von vorgefertigten Implementierungen zum Erstellen eines Layers, der den Zugriff auf die Datenbank ermöglicht. Die JpaRepository- Schnittstelle bietet eine Reihe von Standardmethoden (findBy, save, deleteById usw.) für die Arbeit mit der Datenbank.

UserRepository. Wir erstellen die Benutzeroberfläche im Repository-Paket und erben das JpaRepository <User, Long> , geben die User- Klasse an und geben als ID-Typ Long an .

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

T.O. Durch einfaches Erstellen einer Schnittstelle und Vererben von JpaRepository können Sie Standarddatenbankabfragen durchführen. Wenn Sie eine bestimmte Methode benötigen, fügen Sie diese einfach auf der Grundlage von Idea-Tipps zur Benutzeroberfläche hinzu. Zum Beispiel benötigen wir eine Methode, um einen Benutzer in einer Datenbank nach Namen zu suchen. Wir schreiben den Typ des zurückgegebenen Objekts, und dann bietet die IDE mögliche Optionen an. Das heißt In diesem Fall bestimmt der Methodenname den Anforderungshauptteil.



Bei Bedarf können Sie die Query- Annotation über die Methode verwenden und Abfragen in HQL oder SQL schreiben (Sie müssen nativeQuery = true hinzufügen).

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

RoleRepository. Wir schaffen auf die gleiche Weise, wir brauchen hier keine eigenen Methoden.

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

UserService. Enthält Methoden für die Geschäftslogik der Anwendung. Diese Klasse implementiert die (für Spring Security erforderliche) UserDetailsService- Schnittstelle, in der Sie eine loadUserByUsername () -Methode überschreiben müssen .

In dieser Klasse sehen Sie eine andere Möglichkeit, eine SQL-Abfrage auszuführen - mit dem EntityManager.

Benutzerservice
 @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(); } } 


Betrachten Sie die Methode saveUser (User user) .

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

Zunächst wird in der Datenbank nach Benutzernamen gesucht. Wenn bereits ein Benutzer mit demselben Namen vorhanden ist, wird die Methode beendet. Wird der Benutzername nicht verwendet, wird die Rolle ROLE_USER hinzugefügt. Um das Kennwort nicht in einer unformatierten Form zu speichern, wurde es zuvor mit bCryptPasswordEncoder gehasht. Dann wird der neue Benutzer in der Datenbank gespeichert.

4.3. Controller hinzufügen


Für Seiten, die nicht vom Server verarbeitet werden, sondern nur die Seite zurückgeben, kann die Zuordnung in der Konfiguration konfiguriert werden. Die Anmeldeseite wird standardmäßig vom Spring Security-Controller verwaltet, sodass kein separater Controller erforderlich ist.

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

RegistrationController. Für die Registrierungsseite wird ein separater Controller benötigt. Um eine GET-Anfrage zu verarbeiten, wird die Annotation @GetMapping ("/ registration") für POST - @PostMapping ("/ registration") verwendet .

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:/"; } } 


Um etwas von der Seite hinzuzufügen oder zu erhalten, wenden wir uns dem Modell zu. In einer GET-Anforderung wird der Seite ein neues leeres Objekt der Benutzerklasse hinzugefügt. Dies geschieht, um während einer POST-Anforderung (Benutzername, Passwort, Passwortbestätigung) nicht nacheinander Daten aus dem Registrierungsformular abzurufen, sondern umgehend das ausgefüllte userForm-Objekt abzurufen.

Die Methode addUser () erwartet als Parameter das Benutzerobjekt (userForm), das während der GET-Anforderung hinzugefügt wurde. Anmerkung Valid prüft, ob die für die Felder geltenden Einschränkungen erfüllt sind. In diesem Fall muss die Länge mindestens 2 Zeichen betragen. Wenn die Einschränkungen nicht erfüllt wurden, enthält bindingResult Fehler.

Wenn das Kennwort und die Bestätigung nicht übereinstimmen, fügen Sie der Seite eine Nachricht hinzu und senden Sie sie zurück. Am Ende versuchen wir zu speichern und fügen den Benutzer der Datenbank hinzu.

Die saveUser () -Methode gibt false zurück, wenn ein Benutzer mit demselben Namen bereits vorhanden ist, und true, wenn der Benutzer in der Datenbank gespeichert ist. Wenn der Speicherversuch fehlschlägt, fügen wir eine Fehlermeldung hinzu und senden die Seite zurück. Wenn der Benutzer erfolgreich gespeichert wurde, wechseln Sie zur Hauptseite.

Admincontroller Nur Admin-Benutzer haben Zugriff auf die Admin-Seite. Die Methode userList () enthält nichts Neues: Sie empfängt die Daten aller Benutzer und fügt sie der Seite hinzu.

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


Die Methode deleteUser () verwendet die Annotation RequestParam, d. H. Die Ansicht hat ein Formular, das zwei Parameter übergeben muss - userId und action. Der Link hat die Form http: // localhost: 8080 / admin? UserId = 24 & action = delete. Wenn diese Anforderung ausgeführt wird, wird der Benutzer mit der ID = 24 gelöscht.

Eine andere Möglichkeit, Parameter an eine URL zu übergeben, ist die Verwendung von PathVariable . Mit dieser Annotation erhalten wir die einzelnen Teile der URL, für die Methode getUser () sieht die URL folgendermaßen aus: http: // localhost: 8080 / admin / gt / 24 , nach dem Übergang wird eine Liste aller Benutzer mit der ID> 24 angezeigt.

Sicherheitseinstellungen

WebSecurityConfig. Enthält 2 BCryptPasswordEncoder- und AuthenticationManager- Beans , die zuvor in der userService-Klasse erfüllt wurden.
Darüber hinaus konfiguriert die configure () -Methode den Zugriff auf verschiedene Site-Ressourcen. Als Parameter der antMatchers () -Methode übergeben wir die Pfade, für die wir ein Limit festlegen möchten. Dann geben wir den Benutzern an, mit welcher Rolle diese Seite / Seiten verfügbar sein werden.

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. Ansichten hinzufügen


index.jsp Startseite, unten sind 2 Optionen - für einen Gast und für einen autorisierten Benutzer.





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> 


Um einen Teil des Inhalts der Seite für autorisierte Benutzer auszublenden (Link zur Registrierungs- und Autorisierungsseite), können Sie das Authorize- Tag aus der Spring Security-Tag-Bibliothek verwenden. Der Zugriffsparameter akzeptiert mehrere Ausdrücke. Sie können beispielsweise eine Einschränkung in Abhängigkeit von der Benutzerrolle hasRole ('ADMIN') festlegen .

registration.jsp Registrierungsseite.


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> 


Auf dieser Seite wird das Formular-Tag aus der Tag-Bibliothek verwendet, mit dessen Hilfe das userForm-Modellattribut verknüpft wird (wir haben es der Seite mit einer GET-Anforderung im Controller hinzugefügt) und die Formulare:

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

Sie müssen auch den Pfad angeben, um die userForm-Eigenschaften zu binden:

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

login.jsp Anmeldeseite .



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> 


Diese Seite wird, wie bereits erwähnt, standardmäßig vom Spring-Controller behandelt. Es ist wichtig, die Aktion anzugeben: action = "/ login" und den Namen der Eingabe.

admin.jsp Admin- Seite.



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 Die Newsseite ist statisch. Es dient nur zur Veranschaulichung von Benutzerrechten, also des Inhalts Ihrer Wahl.

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.


main Application :

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

, , , .

spring , .

, – 3 . t_role:
SQL
 INSERT INTO public.t_role(id, name) VALUES (1, 'ROLE_USER'), (2, 'ROLE_ADMIN'); 


. -, . , - , :

SQL
 INSERT INTO public.t_user_roles(user_id, roles_id) VALUES (1, 2); 




( 403) – .

http://localhost:8080/admin . http://localhost:8080/news . , .

Fazit


, . . , User, Writer, .

css js , . , , Bootstrap js.

.


  1. Registration and Login with Spring Boot, Spring Security, Spring Data JPA, Hibernate, MySQL, JSP, Bootstrap and Docker Compose
  2. Spring
  3. Spring Security/ Spring Security
  4. Spring
  5. : Spring 3 MVC + Spring Security + Hibernate

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


All Articles