[JAVA] Erstellen Sie mit Spring Security 2.1 eine einfache Demo-Site mit Spring Security

Überblick

Ich habe zuvor einen Artikel "Implementieren einer einfachen Rest-API mit Spring Security mit Spring Boot 2.0" geschrieben, aber diesmal ist es mit einem Bildschirm einfach. Ich habe eine Demo-Site erstellt und erneut einen Artikel erstellt.

Den Quellcode finden Sie unter rubytomato / demo-java12-security.

Umgebung

Referenz

Hauptfunktionen der zu erstellenden Demo-Site

Verwaltung von Authentifizierungs- / Autorisierungsinformationen

Authentifizierungs- / Autorisierungsinformationen werden in der folgenden Benutzertabelle verwaltet.

DROP TABLE IF EXISTS user;
CREATE TABLE user (
  id BIGINT AUTO_INCREMENT                    COMMENT 'Benutzeridentifikation',
  name VARCHAR(60) NOT NULL                   COMMENT 'Nutzername',
  email VARCHAR(120) NOT NULL                 COMMENT 'Mail Adresse',
  password VARCHAR(255) NOT NULL              COMMENT 'Passwort',
  roles VARCHAR(120)                          COMMENT 'rollen',
  lock_flag BOOLEAN NOT NULL DEFAULT 0        COMMENT 'Sperrflagge 1:Sperren',
  disable_flag BOOLEAN NOT NULL DEFAULT 0     COMMENT 'Ungültige Flagge 1:Ungültig',
  create_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
  update_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
  PRIMARY KEY (id)
)
ENGINE = INNODB
DEFAULT CHARSET = UTF8MB4
COMMENT = 'Benutzertabelle';

ALTER TABLE user ADD CONSTRAINT UNIQUE KEY UKEY_user_email (email);

Authentifizierung

Da bei der Anmeldung eine E-Mail-Adresse und ein Kennwort verwendet werden, bereiten Sie eine Spalte mit E-Mail-Adresse (E-Mail) und Kennwort (Kennwort) vor, um diese Informationen zu speichern. UNIQUE KEY wird in der Spalte E-Mail-Adresse festgelegt, um das Konto anhand der E-Mail-Adresse eindeutig zu identifizieren. Das Kennwort ist nicht eindeutig und speichert den vom Kennwortcodierer von Spring Security gehashten Wert.

Genehmigung

Die Zugriffssteuerung für einige Inhalte verwendet die Rolle von Spring Security. In der Rollenspalte werden die dem Konto zugewiesenen Rollenzeichenfolgen durch Kommas getrennt gespeichert. Außerdem wird das Sperrflag (lock_flag) markiert, wenn es vorübergehend einfriert, und das Deaktivierungsflag (disable_flag) wird markiert, wenn es dauerhaft einfriert. Beim Löschen eines Kontos werden die Daten physisch gelöscht, sodass kein Löschflag vorhanden ist.

Andere Attribute als Authentifizierung / Autorisierung

Darüber hinaus werden andere Attribute als Authentifizierung / Autorisierung in der Benutzerprofiltabelle gespeichert. In diesem Beispiel werden der Spitzname (Spitzname) und das Avatar-Bild (Avatar-Bild) gespeichert. Avatar-Bilder speichern Binärdaten in Spalten vom Typ BLOB und verwalten die Datei selbst nicht.

DROP TABLE IF EXISTS user_profile;
CREATE TABLE user_profile (
  id BIGINT AUTO_INCREMENT                    COMMENT 'Benutzerprofil-ID',
  user_id BIGINT NOT NULL                     COMMENT 'Benutzeridentifikation',
  nick_name VARCHAR(60)                       COMMENT 'Spitzname',
  avatar_image MEDIUMBLOB                     COMMENT 'Avatar Bild',
  create_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
  update_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
  PRIMARY KEY (id)
)
ENGINE = INNODB
DEFAULT CHARSET = UTF8MB4
COMMENT = 'Benutzerprofiltabelle';

ALTER TABLE user_profile ADD CONSTRAINT FOREIGN KEY FKEY_user_profile_id_user_id (user_id) REFERENCES user (id);

rollen

Einige Seiten verfügen über eine rollenbasierte Zugriffssteuerung. Es gibt zwei Arten von Brötchen zuzubereiten. Sie können mehrere Einstellungen für ein Konto oder gar keine festlegen.

rollen Erwartete Verwendung
Keine Rolle, Authentifizierung ist ohne Rolle möglich
ROLE_USER Rollen für allgemeine Benutzer
ROLE_ADMIN Rollen für privilegierte Benutzer

Endpunktliste

Endpunkt method Authentifizierung rollen Abbildung Nr Bemerkungen
/ GET no 1 Startseite und Anmeldeseite
/menu GET no 2 Menüseite
/signup GET no 3 Kontoregistrierungsseite
/signup POST no Ausführung des Kontoregistrierungsprozesses nach der Registrierung/Weiterleiten an
/signin GET no 4 Anmeldeseite
/login POST no Anmeldevorgang, Endpunkt, der von Spring Security nach der Anmeldung bereitgestellt wird/Weiterleiten an
/signout GET yes - 5 Abmeldeseite
/logout POST yes - Abmeldevorgang, von Spring Security bereitgestellte Endpunkte nach dem Abmelden/Weiterleiten an
/account/change/password GET yes - 6 Seite zum Ändern des Passworts
/account/change/password POST yes - Passwortänderungsprozess nach Änderung/Weiterleiten an
/account/change/role GET yes - 7 Rollenwechselseite
/account/change/role POST yes - Rollenwechselverarbeitung nach Änderung/Weiterleiten an
/account/change/profile GET yes - 8 Seite zum Ändern des Profils
/account/change/profile POST yes - Profiländerungsprozess nach Änderung/Weiterleiten an
/account/delete GET yes - 9 Seite zum Löschen des Kontos
/account/delete POST yes - Löschvorgang des Kontos nach dem Löschen/Weiterleiten an
/memo GET yes USER, ADMIN Inhaltsseite für USER- oder ADMIN-Rollen
/user GET yes USER Inhaltsseite der USER-Rolle
/admin GET yes ADMIN Inhaltsseite der ADMIN-Rolle
/error/denied GET no Fehlerseite, wenn der Zugriff verweigert wird
/error/invalid GET no Fehlerseite, wenn die Sitzung ungültig ist
/error/expired GET no Fehlerseite, Sitzung abgelaufen

Implementierung rund um Sicherheit

Datenbankzugriff

Verwenden Sie JPA für den Datenbankzugriff. Es gibt keine spezielle Implementierung in Bezug auf Spring Security für den Datenbankzugriff.

Entität

Die Implementierung der Entitätsklasse, die der Benutzertabelle entspricht, die Authentifizierungs- / Autorisierungsinformationen verwaltet, ist wie folgt.

User


import com.example.demo.auth.UserRolesUtil;
import lombok.Data;
import lombok.ToString;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import javax.persistence.Table;
import java.time.LocalDateTime;

@Entity
@Table(name = "user")
@Data
@ToString(exclude = {"password"})
public class User {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;
  @Column(name = "name", length = 60, nullable = false)
  private String name;
  @Column(name = "email", length = 120, nullable = false, unique = true)
  private String email;
  @Column(name = "password", length = 255, nullable = false)
  private String password;
  @Column(name = "roles", length = 120)
  private String roles;
  @Column(name = "lock_flag", nullable = false)
  private Boolean lockFlag;
  @Column(name = "disable_flag", nullable = false)
  private Boolean disableFlag;
  @Column(name = "create_at", nullable = false)
  private LocalDateTime createAt;
  @Column(name = "update_at", nullable = false)
  private LocalDateTime updateAt;

  @OneToOne(mappedBy = "user", cascade = CascadeType.ALL)
  private UserProfile userProfile;

  public void setUserProfile(UserProfile userProfile) {
    this.userProfile = userProfile;
    userProfile.setUser(this);
  }

  public String getAvatarImageBase64Encode() {
    return this.userProfile.getAvatarImageBase64Encode();
  }

  @PrePersist
  private void prePersist() {
    this.lockFlag = Boolean.FALSE;
    this.disableFlag = Boolean.FALSE;
    this.createAt = LocalDateTime.now();
    this.updateAt = LocalDateTime.now();
  }

  @PreUpdate
  private void preUpdate() {
    this.updateAt = LocalDateTime.now();
  }

  public static User of(String name, String email, String encodedPassword, String[] roles) {
    return User.of(name, email, encodedPassword, roles, new UserProfile());
  }

  public static User of(String name, String email, String encodedPassword, String[] roles, 
                        UserProfile userProfile) {
    User user = new User();
    user.setName(name);
    user.setEmail(email);
    user.setPassword(encodedPassword);
    String joinedRoles = UserRolesUtil.joining(roles);
    user.setRoles(joinedRoles);
    user.setUserProfile(userProfile);
    return user;
  }

}

UserProfile


import lombok.Data;
import lombok.ToString;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.Lob;
import javax.persistence.OneToOne;
import javax.persistence.PostLoad;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import javax.persistence.Table;
import javax.persistence.Transient;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.Base64;

@Entity
@Table(name = "user_profile")
@Data
@ToString(exclude = {"user", "avatarImage", "avatarImageBase64Encode"})
public class UserProfile {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;
  @Column(name = "nick_name", length = 60)
  private String nickName;
  @Lob
  @Column(name = "avatar_image")
  private byte[] avatarImage;
  @Column(name = "create_at", nullable = false)
  private LocalDateTime createAt;
  @Column(name = "update_at", nullable = false)
  private LocalDateTime updateAt;

  @OneToOne
  @JoinColumn(name = "user_id", nullable = false)
  private User user;

  @Transient
  private String avatarImageBase64Encode;

  public void setAvatarImage(byte[] avatarImage) {
    this.avatarImage = avatarImage;
    this.avatarImageBase64Encode = base64Encode();
  }

  String getAvatarImageBase64Encode() {
    return avatarImageBase64Encode == null ? "" : avatarImageBase64Encode;
  }

  private String base64Encode() {
    return new String(Base64.getEncoder().encode(avatarImage), StandardCharsets.US_ASCII);
  }

  @PostLoad
  private void init() {
    this.avatarImageBase64Encode = base64Encode();
  }

  @PrePersist
  private void prePersist() {
    this.avatarImage = new byte[0];
    this.createAt = LocalDateTime.now();
    this.updateAt = LocalDateTime.now();
  }

  @PreUpdate
  private void preUpdate() {
    this.updateAt = LocalDateTime.now();
  }

}

Repository

Fügen Sie eine Methode zur Suche nach E-Mail-Adresse hinzu.

UserRepository


import com.example.demo.model.User;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface UserRepository extends JpaRepository<User, Long> {
  Optional<User> findByEmail(String email);
}

Sicherheitskonfiguration

Spring Security-Konfigurationen erben von der abstrakten Klasse "WebSecurityConfigurerAdapter". Es gibt drei Hauptkonfigurationen: "AuthenticationManager", "WebSecurity" und "HttpSecurity".

import com.example.demo.auth.SimpleUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

  private SimpleUserDetailsService simpleUserDetailsService;
  private PasswordEncoder passwordEncoder;

  @Autowired
  public void setSimpleUserDetailsService(SimpleUserDetailsService simpleUserDetailsService) {
    this.simpleUserDetailsService = simpleUserDetailsService;
  }

  @Autowired
  public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
    this.passwordEncoder = passwordEncoder;
  }

  @Bean
  @Override
  public AuthenticationManager authenticationManagerBean() throws Exception {
    return super.authenticationManagerBean();
  }

  @Bean
  PasswordEncoder passwordEncoder() {
    return PasswordEncoderFactories.createDelegatingPasswordEncoder();
  }

  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    // ...Konfiguration weggelassen(Nachstehend beschrieben)...
  }

  @Override
  public void configure(WebSecurity web) throws Exception {
    // ...Konfiguration weggelassen(Nachstehend beschrieben)...
  }

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    // ...Konfiguration weggelassen(Nachstehend beschrieben)...
  }

}

UserDetails (principal)

Erstellen Sie die von Spring Security verwendete Benutzerinformationsklasse, indem Sie die Schnittstellen "UserDetails" und "CredentialsContainer" implementieren. Das Überschreiben der Methoden "equals" und "hashCode" sollte ebenfalls ordnungsgemäß erfolgen. Wenn Sie diese Methode nicht ordnungsgemäß implementieren, funktionieren mehrere Überprüfungen der Sitzungsverwaltung nicht.

SimpleLoginUser


import com.example.demo.model.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.CredentialsContainer;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;
import java.util.Objects;
import java.util.Set;

@Slf4j
public class SimpleLoginUser implements UserDetails, CredentialsContainer {
  private static final long serialVersionUID = -888887602572409628L;

  private final String username;
  private String password;
  private final Set<GrantedAuthority> authorities;
  private final boolean accountNonExpired;
  private final boolean accountNonLocked;
  private final boolean credentialsNonExpired;
  private final boolean enabled;
  private final User user;

  public SimpleLoginUser(User user) {
    if ((Objects.isNull(user.getEmail()) || "".equals(user.getEmail())) ||
        (Objects.isNull(user.getPassword()) || "".equals(user.getPassword()))) {
      throw new IllegalArgumentException(
          "Cannot pass null or empty values to constructor");
    }
    this.username = user.getEmail();
    this.password = user.getPassword();
    this.authorities = UserRolesUtil.toSet(user.getRoles());
    this.accountNonExpired = true;
    this.accountNonLocked = !user.getLockFlag();
    this.credentialsNonExpired = true;
    this.enabled = !user.getDisableFlag();
    this.user = user;
  }

  @Override
  public String getUsername() {
    return this.username;
  }

  @Override
  public String getPassword() {
    return this.password;
  }

  @Override
  public Collection<? extends GrantedAuthority> getAuthorities() {
    return this.authorities;
  }

  @Override
  public boolean isAccountNonExpired() {
    return this.accountNonExpired;
  }

  @Override
  public boolean isAccountNonLocked() {
    return this.accountNonLocked;
  }

  @Override
  public boolean isCredentialsNonExpired() {
    return this.credentialsNonExpired;
  }

  @Override
  public boolean isEnabled() {
    return this.enabled;
  }

  @Override
  public void eraseCredentials() {
    this.password = null;
  }

  public User getUser() {
    return user;
  }

  @Override
  public boolean equals(Object rhs) {
    if (!(rhs instanceof SimpleLoginUser)) {
      return false;
    }
    return this.username.equals(((SimpleLoginUser)rhs).username);
  }

  @Override
  public int hashCode() {
    return this.username.hashCode();
  }

}

Wenn Sie keine spezielle Implementierung haben, ist es einfach, die Referenzimplementierung "org.springframework.security.core.userdetails.User" zu erben.

public class SimpleLoginUser extends User {

  public SimpleLoginUser(String username, String password, Collection<? extends GrantedAuthority> authorities) {
    super(username, password, authorities);
  }

  public SimpleLoginUser(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
    super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
  }

}

** Wird von der Handler-Methode des Controllers empfangen **

Sie können ein Objekt der Benutzerinformationsklasse in der Handler-Methode des Endpunkts empfangen, für den eine Authentifizierung erforderlich ist. Fügen Sie dem empfangenen Argument die Annotation @ AuthenticationPrincipal hinzu.

@PostMapping(value = "change/password")
public String changePassword(@AuthenticationPrincipal SimpleLoginUser loggedinUser,
                             @Validated ChangePasswordForm changePasswordForm, BindingResult result, Model model) {

  User user = loggedinUser.getUser();

  //...Kürzung...

}

Sie können die Benutzerentität auch direkt empfangen.

@AuthenticationPrincipal(expression = "user") User user

** Zugriff von Vorlage (Thymeleaf) **

Sie können mit getPrincipal () auf die Benutzerinformationsklasse (SimpleLoginUser) zugreifen.

${#authentication.getPrincipal()}

or

${#authentication.principal}

Sie können auch auf die Entitätsklasse Benutzer zugreifen.

${#authentication.getPrincipal().user}

or

${#authentication.principal.user}

UserDetailsService

Erstellen Sie einen spezifischen Code für Spring Security, um die für die Authentifizierung / Autorisierung erforderlichen Benutzerinformationen (SimpleLoginUser) abzurufen, indem Sie die Schnittstelle UserDetailsService implementieren. Die einzige Methode, die überschrieben werden muss, ist "loadUserByUsername ()", die in diesem Beispiel die Benutzerentität von der E-Mail-Adresse abruft und Benutzerinformationen (SimpleLoginUser) basierend auf der Benutzerentität generiert.

SimpleUserDetailsService


import com.example.demo.repository.UserRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Slf4j
public class SimpleUserDetailsService implements UserDetailsService {
  private final UserRepository userRepository;

  public SimpleUserDetailsService(UserRepository userRepository) {
    this.userRepository = userRepository;
  }

  @Transactional(readOnly = true)
  @Override
  public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
    log.debug("loadUserByUsername(email):[{}]", email);
    return userRepository.findByEmail(email)
        .map(SimpleLoginUser::new)
        .orElseThrow(() -> new UsernameNotFoundException("User not found by email:[" + email + "]"));
  }

}

AuthenticationManager

Überschreiben Sie den Standardauthentifizierungsmanager für die Konfiguration. AuthenticationManager delegiert den eigentlichen Authentifizierungsprozess an AuthenticationProvider. Es gibt verschiedene Implementierungen von AuthenticationProvider, und die Implementierung des Anbieters, der Benutzerinformationen aus der Datenbank abruft, lautet "DaoAuthenticationProvider".

userDetailsService () undpasswordEncoder ()sind die Konfigurationen von DaoAuthenticationProvider. Setzen Sie "userDetailsService ()" auf die Klasse ("SimpleUserDetailsService"), die Benutzerinformationen abruft, die die "UserDetailsService" -Schnittstelle implementieren, und setzen Sie "passwordEncoder ()" auf den Kennwortcodierer (Standard ist "BCryptPasswordEncoder").

Wenn Sie "eraseCredentials (true)" festlegen, wird das Kennwort der Benutzerinformationsklasse ("SimpleLoginUser") nach der Authentifizierung auf null gesetzt.

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  auth
    .eraseCredentials(true)
    // ### DaoAuthenticationConfigurer
    .userDetailsService(simpleUserDetailsService)
    // ### DaoAuthenticationConfigurer
    .passwordEncoder(passwordEncoder);
}

Übrigens ist im Fall einer Authentifizierung, die Benutzerinformationen im Speicher hat, die Implementierung wie folgt.

auth
  // ### InMemoryUserDetailsManagerConfigurer
  .inMemoryAuthentication()
  .withUser("user")
    .password(passwordEncoder.encode("passxxx"))
    .roles("USER")
  .and()
  .withUser("admin")
    .password(passwordEncoder.encode("passyyy"))
    .roles("ADMIN");

WebSecurity

WebSecurity


@Override
public void configure(WebSecurity web) throws Exception {
  // @formatter:off
  web
    .debug(false)
    // ### IgnoredRequestConfigurer
    .ignoring()
      .antMatchers("/images/**", "/js/**", "/css/**")
  ;
  // @formatter:on
}

HttpSecurity

Führt die Hauptkonfiguration von Spring Security durch. Da die Menge an Code (Einstellung) zugenommen hat, habe ich sie einzeln aufgelistet, aber die Gliederung ist wie folgt.

HttpSecurity


@Override
protected void configure(HttpSecurity http) throws Exception {
  // @formatter:off
  http
    .authorizeRequests()
      //...Konfiguration der Anforderungsautorisierung weggelassen(Nachstehend beschrieben)...
      .and()
    .exceptionHandling()
      //...Konfiguration für Zugriff verweigert weggelassen(Nachstehend beschrieben)...
      .and()
    .formLogin()
      //...Anmeldekonfiguration weggelassen(Nachstehend beschrieben)...
      .and()
    .logout()
      //...Abmeldekonfiguration weggelassen(Nachstehend beschrieben)...
      .and()
    .csrf()
      //...CSRF-Konfiguration weggelassen(Nachstehend beschrieben)...
      .and()
    .rememberMe()
      //...Remember-Ich Konfiguration weggelassen(Nachstehend beschrieben)...
      .and()
    .sessionManagement()
      //...Sitzungsverwaltungskonfiguration weggelassen(Nachstehend beschrieben)...
  ;
  // @formatter:on
}

Autorisierung anfordern

// ### ExpressionUrlAuthorizationConfigurer
.authorizeRequests()
  .mvcMatchers("/", "/signup", "/menu").permitAll()
  .mvcMatchers("/error/**").permitAll()
  .mvcMatchers("/memo/**").hasAnyRole("USER", "ADMIN")
  .mvcMatchers("/account/**").fullyAuthenticated()
  .mvcMatchers("/admin/**").hasRole("ADMIN")
  .mvcMatchers("/user/**").hasRole("USER")
  .anyRequest().authenticated()

permitAll Jeder, auch anonym, hat Zugriff.

authenticated Authentifizierten Konten wird Zugriff gewährt.

fullyAuthenticated Konten, die durch andere Methoden als die automatische Anmeldung (Remember-Me) authentifiziert wurden, erhalten Zugriff.

hasRole/hasAnyRole Konten mit der angegebenen Rolle erhalten Zugriff.

Zugriff verweigert Handhabung

Legen Sie die Übergangsziel-URL fest, wenn der Zugriff mit "accessDeniedPage ()" verweigert wird. Wenn Sie auf eine Seite zugreifen, für die eine Authentifizierung in einem anonymen Status erforderlich ist, wird Ihnen der Zugriff nicht verweigert, sondern Sie werden zur Anmeldeseite weitergeleitet. Wenn die Authentifizierung erfolgreich ist, werden Sie zu dieser Seite weitergeleitet.

// ### ExceptionHandlingConfigurer
.exceptionHandling()

  // #accessDeniedUrl: the URL to the access denied page (i.e. /errors/401)
  .accessDeniedPage("/error/denied")

  // #accessDeniedHandler: the {@link AccessDeniedHandler} to be used
  //.accessDeniedHandler(accessDeniedHandler)

AccessDeniedHandler

Sie können anpassen, indem Sie "AccessDeniedHandler" anstelle von "accessDeniedPage ()" implementieren. Der folgende Code basiert auf org.springframework.security.web.access.AccessDeniedHandlerImpl.

private AccessDeniedHandler accessDeniedHandler = (req, res, accessDeniedException) -> {
  if (res.isCommitted()) {
      log.debug("Response has already been committed. Unable to redirect to ");
      return;
  }
  req.setAttribute(WebAttributes.ACCESS_DENIED_403, accessDeniedException);
  res.setStatus(HttpStatus.FORBIDDEN.value());
  RequestDispatcher dispatcher = req.getRequestDispatcher("/error/denied");
  dispatcher.forward(req, res);
};
Fehlerseite beim Verweigern des Zugriffs
<div class="row justify-content-center" th:if="${SPRING_SECURITY_403_EXCEPTION != null}">
    <div class="col">
        <div class="alert alert-warning" role="alert">
            <h5 class="alert-heading">Zugriff verweigert</h5>
            <p class="mb-0" th:text="${SPRING_SECURITY_403_EXCEPTION.message}">message</p>
        </div>
    </div>
</div>

access_denied.png

Anmelden

Legen Sie die URL der Seite mit dem Anmeldeformular in loginPage () fest. Wenn die Authentifizierung erfolgreich ist und Sie im zweiten Argument von "defaultSuccessUrl ()" "true" setzen, werden Sie immer zur URL des ersten Arguments weitergeleitet. Wenn Sie "false" festlegen, werden Sie zu der URL weitergeleitet, zu der Sie vor der Authentifizierung wechseln wollten. Wenn die Authentifizierung fehlschlägt, wird die von failUrl () festgelegte URL angezeigt.

// ### FormLoginConfigurer
.formLogin()
   // #loginPage: the login page to redirect to if authentication is required (i.e."/login")
  .loginPage("/signin")

   // #loginProcessingUrl: the URL to validate username and password
  .loginProcessingUrl("/login")
  .usernameParameter("email")
  .passwordParameter("password")

  // #defaultSuccessUrl: the default success url
  // #alwaysUse: true if the {@code defaultSuccesUrl} should be used after authentication despite if a protected page had been previously visited
  .defaultSuccessUrl("/", false)

  // #successHandler: the {@link AuthenticationSuccessHandler}.
  //.successHandler(successHandler)

  // #authenticationFailureUrl: the URL to send users if authentication fails (i.e."/login?error").
  .failureUrl("/signin?error")

  // #authenticationFailureHandler: the {@link AuthenticationFailureHandler} to use
  //.failureHandler(failureHandler)

  .permitAll()

Anstelle von "defaultSuccessUrl ()" und "failUrl ()" können Sie "AuthenticationSuccessHandler" bzw. "AuthenticationFailureHandler" implementieren und anpassen.

AuthenticationSuccessHandler

private AuthenticationSuccessHandler successHandler = (req, res, auth) -> {
  //Kundenspezifische Bearbeitung
};

AuthenticationFailureHandler

private AuthenticationFailureHandler failureHandler = (req, res, exception) -> {
  //Kundenspezifische Bearbeitung
};
Bilden

Die Aktion des Anmeldeformulars ist die in "loginProcessingUrl ()" festgelegte URL. Zusätzlich zu der für die Authentifizierung erforderlichen E-Mail-Adresse und dem Kennwort gibt es ein Kontrollkästchen, mit dem Sie auswählen können, ob Sie sich automatisch mit der Funktion REMEMBER-ME anmelden möchten.

<form class="text-center" action="#" th:action="@{/login}" method="post">
    <div class="md-form">
        <input type="text" id="email" name="email" class="form-control">
        <label for="email">E-mail</label>
    </div>
    <div class="md-form">
        <input type="password" id="password" name="password" class="form-control">
        <label for="password">Password</label>
    </div>
    <div class="d-flex justify-content-around">
        <div>
            <div class="custom-control custom-checkbox">
                <input type="checkbox" id="remember-me" name="remember-me" value="on" class="custom-control-input">
                <label for="remember-me" class="custom-control-label">Remember me</label>
            </div>
        </div>
        <div>
            <p>Not a member? <a href="/app/signup" th:href="@{/signup}">Register</a></p>
        </div>
    </div>
    <button class="btn indigo accent-4 text-white btn-block my-4" type="submit">Sign in</button>
</form>

signin_form.png

Nachrichtenanzeige, wenn die Authentifizierung fehlschlägt

Wenn die Authentifizierung fehlschlägt, leiten Sie mit dem Parameter error ( / signin? Error) zur Anmeldeseite um. Darüber hinaus hat Spring Security ein Objekt der AuthenticationException-Klasse (oder eine Klasse, die es erbt, die Ausnahme, die ausgelöst wird, wenn die Anmeldeinformationen falsch sind, ist "BadCredentialsException") mit dem Namen "SPRING_SECURITY_LAST_EXCEPTION" im Sitzungsattribut festgelegt.

In der Vorlage mit Thymeleaf wird die Nachricht aus diesen beiden Informationen angezeigt.

<div class="row justify-content-center" th:if="${param['error'] != null && session['SPRING_SECURITY_LAST_EXCEPTION'] != null}">
    <div class="col">
        <div class="alert alert-warning" role="alert">
            <h5 class="alert-heading">Zertifizierung fehlgeschlagen</h5>
            <p class="mb-1" th:text="${session['SPRING_SECURITY_LAST_EXCEPTION'].message}">message</p>
        </div>
    </div>
</div>

signin_form_error.png

Nachrichtenanzeige, wenn das Konto gesperrt ist

Wenn Sie versuchen, sich mit 1 im lock_flag der Benutzertabelle anzumelden, wird die in der Abbildung gezeigte Meldung angezeigt.

lock.png

Bitte beachten Sie, dass diese Flags bei der Anmeldung überprüft werden und nicht durch das Markieren des angemeldeten Kontos beeinflusst werden.

Ausloggen

Die Abmeldung erfolgt über das Formular auf der Abmeldeseite (/ abmelden). Dies liegt daran, dass bei aktivierter CSRF die Abmeldung (/ logout) auch vom POST angefordert werden muss.

Wenn die Abmeldung erfolgreich ist, werden Sie zur obersten Seite (/) weitergeleitet, und die Sitzung wird geändert und die Cookies werden gelöscht.

// ### LogoutConfigurer
.logout()
  // #logoutUrl: the URL that will invoke logout.
  .logoutUrl("/logout")

  // #logoutSuccessUrl: the URL to redirect to after logout occurred
  //.logoutSuccessUrl("/")

  // #logoutSuccessHandler: the {@link LogoutSuccessHandler} to use after a user
  .logoutSuccessHandler(logoutSuccessHandler)

  // #invalidateHttpSession: true if the {@link HttpSession} should be invalidated (default), or false otherwise.
  .invalidateHttpSession(false)

  // #cookieNamesToClear: the names of cookies to be removed on logout success.
  .deleteCookies("JSESSIONID", "XSRF-TOKEN")

Übrigens können Sie sich anstelle von "LogoutSuccessHandler ()" in der Konfiguration mit GET abmelden, indem Sie Folgendes implementieren.

//.logoutSuccessHandler(logoutSuccessHandler)
.logoutRequestMatcher(new AntPathRequestMatcher("/logout", "GET"))

LogoutSuccessHandler

Implementieren Sie die Verarbeitung nach erfolgreicher Abmeldung mit "LogoutSuccessHandler ()". In diesem Beispiel wird die Sitzungs-ID geändert und die Seite auf die oberste Seite umgeleitet.

private LogoutSuccessHandler logoutSuccessHandler = (req, res, auth) -> {
  if (res.isCommitted()) {
    log.debug("Response has already been committed. Unable to redirect to ");
    return;
  }
  if (req.isRequestedSessionIdValid()) {
    log.debug("requestedSessionIdValid session id:{}", req.getRequestedSessionId());
    req.changeSessionId();
  }
  RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
  redirectStrategy.sendRedirect(req, res, "/");
};

Dieses Mal habe ich einen Handler implementiert, aber Sie können die Übergangsziel-URL auch nach dem Abmelden mit "logoutSuccessUrl ()" in der Konfiguration angeben.

// #logoutSuccessUrl: the URL to redirect to after logout occurred
.logoutSuccessUrl("/")
Bilden

Die Aktion des Abmeldeformulars ist die in "logoutUrl ()" festgelegte URL.

<form class="text-center" action="#" th:action="@{/logout}" method="post">
    <button class="btn indigo accent-4 text-white btn-block my-4" type="submit">Sign out</button>
</form>

signout_form.png

CSRF

Wenn CSRF aktiviert ist, wird das CSRF-Token automatisch im POST-Methodenformular ausgeblendet. Standardmäßig wird das CSRF-Token in einer Sitzung gespeichert, aber die folgende Implementierung ändert das Speicherziel in ein Cookie.

// ### CsrfConfigurer
.csrf()
  .csrfTokenRepository(new CookieCsrfTokenRepository())

Beispiel) Der Parametername lautet "_csrf".

<input type="hidden" name="_csrf" value="18331a72-184e-4651-ae0b-e044283a20b3">

Beispiel) Der Cookie-Name des CSRF-Tokens lautet "XSRF_TOKEN". Standardmäßig hat es das HttpOnly-Attribut.

xsrf-token.png

Sie können den Cookie-Namen mit dem folgenden Code ändern.

CookieCsrfTokenRepository customCsrfTokenRepository() {
  CookieCsrfTokenRepository cookieCsrfTokenRepository = new CookieCsrfTokenRepository();
  cookieCsrfTokenRepository.setCookieName("NEW-TOKEN-NAME");
  return cookieCsrfTokenRepository;
}

Remember-Me

Wenn Sie Remember-Me im Anmeldeformular aktivieren und sich anmelden, wird ein Cookie mit dem Namen REMEMBER ME ausgegeben. Wenn Sie mit diesem Cookie auf die Demo-Site zugreifen, werden Sie automatisch authentifiziert, wenn es anonym ist.

Der Authentifizierungsstatus von Spring Security enthält "authentifiziert" und "vollständig authentifiziert". Wenn Sie automatisch mit Remember-Me angemeldet sind, wird er als "authentifiziert" und "RememberMe" identifiziert und im Falle einer Formularauthentifizierung als "authentifiziert" und "authentifiziert". Als vollständig authentifiziert identifiziert, können automatische Anmeldung und Formularauthentifizierung unterschieden werden.

// ### RememberMeConfigurer
.rememberMe()
  // #alwaysRemember: set to true to always trigger remember me, false to use the remember-me parameter.
  .alwaysRemember(false)
  // #rememberMeParameter: the HTTP parameter used to indicate to remember the user
  .rememberMeParameter("remember-me")
  // #useSecureCookie: set to {@code true} to always user secure cookies, {@code false} to disable their use.
  .useSecureCookie(true)
  // #rememberMeCookieName: the name of cookie which store the token for remember
  .rememberMeCookieName("REMEMBERME")
  // # Allows specifying how long (in seconds) a token is valid for
  .tokenValiditySeconds(daysToSeconds(3))
  // #key: the key to identify tokens created for remember me authentication
  .key("PgHahck5y6pz7a0Fo#[G)!kt")
Unterscheiden Sie zwischen authentifiziert und vollständig authentifiziert

Für den Zugriff auf / account / ** ist eine Formularauthentifizierung erforderlich, wenn die Konfiguration der Anforderungsautorisierung wie folgt lautet. Wenn Sie versuchen, während der automatischen Anmeldung durch Remember-Me darauf zuzugreifen, werden Sie zur Anmeldeseite weitergeleitet und müssen die Formularauthentifizierung explizit durchführen.

.authorizeRequests()
  .mvcMatchers("/account/**").fullyAuthenticated()
  .mvcMatchers("/admin/**").hasRole("ADMIN")
  .mvcMatchers("/user/**").hasRole("USER")
  .anyRequest().authenticated()

Darüber hinaus wird das Steuerelement, das eine bestimmte Rolle hat und diese nur zulässt, wenn es sich um eine Formularauthentifizierung handelt, wie folgt implementiert. In diesem Beispiel ist der Zugriff auf "/ admin / **" zulässig, wenn Sie über die ADMIN-Rolle und die Formularauthentifizierung verfügen.

  .mvcMatchers("/admin/**").access("hasRole('ADMIN') and isFullyAuthenticated()")

Sitzungsverwaltung

// ### SessionManagementConfigurer
.sessionManagement()

  .sessionFixation()
    .changeSessionId()

  // #invalidSessionUrl: the URL to redirect to when an invalid session is detected
  .invalidSessionUrl("/error/invalid")

  // ### ConcurrencyControlConfigurer
  // #maximumSessions: the maximum number of sessions for a user
  .maximumSessions(1)
    // #maxSessionsPreventsLogin: true to have an error at time of authentication, else false (default)
    .maxSessionsPreventsLogin(false)
    // #expiredUrl: the URL to redirect to
    .expiredUrl("/error/expired")
Ziel umleiten, wenn die Sitzung deaktiviert ist

Verwenden Sie "invalidSessionUrl ()", um die Übergangsziel-URL festzulegen, wenn die Sitzung ungültig ist, dh wenn das Sitzungscookie gelöscht wird, die Sitzungs-ID manipuliert wird oder auf der Serverseite keine Sitzungsinformationen vorhanden sind.

Ablaufdatum der Sitzung

Sie können das Ablaufdatum der Sitzung in der Anwendungseinstellungsdatei festlegen. Wenn Sie Folgendes festlegen, ist die Sitzung ungültig, wenn 30 Minuten oder länger keine Operation ausgeführt wird. Wenn Sie danach arbeiten, werden Sie zu der mit "invalidSessionUrl ()" festgelegten URL umgeleitet.

server:
  servlet:
    session:
      timeout: 30m

Wenn sowohl "invalidSessionUrl ()" als auch "expiredUrl ()" gesetzt sind, scheint "invalidSessionUrl ()" Vorrang zu haben.

Kontrolle über mehrere Anmeldungen

Wenn Sie mehrere Anmeldungen mit demselben Konto steuern möchten, können Sie die Anzahl der gleichzeitigen Sitzungen mit "maximumSessions ()" und "maxSessionsPreventsLogin ()" steuern.

Verwenden Sie "maximumSessions ()", um die Anzahl der Sitzungen festzulegen, die gleichzeitig angemeldet werden können, und "maxSessionsPreventsLogin ()", um das Steuerungsverhalten festzulegen.

maxSessionsPreventsLogin Verhalten
true Sie können sich nicht über MaximumSessions hinaus anmelden, solange die Sitzung, in der Sie sich zuerst angemeldet haben, gültig ist
false (default) Später angemeldete Sitzungen sind gültig, und früher angemeldete Sitzungen, die die maximale Anzahl von Sitzungen überschreiten, werden ungültig.

Implementierung der Kontoregistrierung und -löschung

Die Registrierung und Löschung von Konten kann in der Spring Security-Konfiguration nicht festgelegt werden, daher wird sie von Grund auf neu implementiert.

Kontoregistrierung

Die Implementierung dieser Demo-Site erstellt ein Konto, sobald Sie sich registrieren. Normalerweise ist es erforderlich, sich vorübergehend zu registrieren, ohne sich plötzlich zu registrieren, eine Aktivierungs-E-Mail an die E-Mail-Adresse zum Zeitpunkt der vorübergehenden Registrierung zu senden und dann die Hauptregistrierung durch den aktivierenden Benutzer durchzuführen.

Bilden

Dies ist ein Formular zur Registrierung eines Kontos.

<form class="text-center" action="#" th:action="@{/signup}" th:object="${signupForm}" method="post">
    <div class="md-form">
        <input id="username" class="form-control" type="text" name="username" th:field="*{username}">
        <label for="username">username</label>
        <div th:if="${#fields.hasErrors('username')}" th:errors="*{username}" class="text-danger">error</div>
    </div>
    <div class="md-form">
        <input id="email" class="form-control" type="text" name="email" th:field="*{email}">
        <label for="email">email</label>
        <div th:if="${#fields.hasErrors('email')}" th:errors="*{email}" class="text-danger">error</div>
    </div>
    <div class="md-form">
        <input id="password" class="form-control" type="text" name="password" th:field="*{password}">
        <label for="password">password</label>
        <div th:if="${#fields.hasErrors('password')}" th:errors="*{password}" class="text-danger">
            error
        </div>
    </div>
    <div class="md-form">
        <input id="repassword" class="form-control" type="text" name="repassword" th:field="*{repassword}">
        <label for="repassword">(re) password</label>
        <div th:if="${#fields.hasErrors('repassword')}" th:errors="*{repassword}" class="text-danger">error</div>
    </div>
    <div class="text-left justify-content-start">
        <p>roles</p>
        <div class="custom-control custom-checkbox">
            <input id="roles_1" class="custom-control-input" type="checkbox" name="roles" value="ROLE_USER" th:field="*{roles}">
            <label for="roles_1" class="custom-control-label">ROLE_USER</label>
        </div>
        <div class="custom-control custom-checkbox">
            <input id="roles_2" class="custom-control-input" type="checkbox" name="roles" value="ROLE_ADMIN" th:field="*{roles}">
            <label for="roles_2" class="custom-control-label">ROLE_ADMIN</label>
        </div>
        <div th:if="${#fields.hasErrors('roles')}" th:errors="*{roles}" class="text-danger">error</div>
    </div>
    <button class="btn indigo accent-4 text-white btn-block my-4" type="submit">Sign up</button>
</form>

signup_form.png

Registrierungsprozess

Eine Implementierung der Account Service Class. DI das Repository für den Datenbankzugriff, PasswordEncoder für das Hashing von Kennwörtern und AuthenticationManager für die Authentifizierung im Konstruktor.

@Service
@Slf4j
public class AccountServiceImpl implements AccountService {
  private final UserRepository userRepository;
  private final PasswordEncoder passwordEncoder;
  private final AuthenticationManager authenticationManager;

  public AccountServiceImpl(UserRepository userRepository, PasswordEncoder passwordEncoder, AuthenticationManager authenticationManager) {
    this.userRepository = userRepository;
    this.passwordEncoder = passwordEncoder;
    this.authenticationManager = authenticationManager;
  }

  @Transactional
  @Override
  public void register(String name, String email, String rawPassword, String[] roles) {
    log.info("user register name:{}, email:{}, roles:{}", name, email, roles);
    String encodedPassword = passwordEncoder.encode(rawPassword);
    User storedUser = userRepository.saveAndFlush(User.of(name, email, encodedPassword, roles));
    authentication(storedUser, rawPassword);
  }

  private void authentication(User user, String rawPassword) {
    log.info("authenticate user:{}", user);
    SimpleLoginUser loginUser = new SimpleLoginUser(user);
    UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, rawPassword, loginUser.getAuthorities());
    authenticationManager.authenticate(authenticationToken);
    if (authenticationToken.isAuthenticated()) {
      SecurityContextHolder.getContext().setAuthentication(authenticationToken);
    } else {
      throw new RuntimeException("login failure");
    }
  }

}

** Passwort-Hashing ** Das Kennwort verwendet den vom Spring Security-Kennwortcodierer gehashten Wert.

String encodedPassword = passwordEncoder.encode(rawPassword);

** Entitätspersistenz ** Die Persistenz der Kontodaten verwendet ein Repository. Die Benutzerentität, die beibehalten werden soll, wird für den nachfolgenden Authentifizierungsprozess verwendet.

User storedUser = userRepository.saveAndFlush(User.of(name, email, encodedPassword, roles));

** Zertifizierung ** Programmgesteuerte Authentifizierung mit den registrierten Kontoinformationen. Erstellen Sie ein Objekt mit Benutzerinformationen (SimpleLoginUser), das von Spring Security verwendet wird, aus der Benutzerentität, die beibehalten werden soll. Außerdem wird aus diesem Benutzerinformationsobjekt ein Authentifizierungstoken generiert.

Das erste Argument des Konstruktors "UsernamePasswordAuthenticationToken" ist das "Principal" -Objekt (dh Benutzerinformationen), das zweite Argument ist das "Credentials" -Objekt (dh das Passwort) und das dritte Argument ist "Authorities".

SimpleLoginUser loginUser = new SimpleLoginUser(user);
UsernamePasswordAuthenticationToken authenticationToken =
  new UsernamePasswordAuthenticationToken(loginUser, rawPassword, loginUser.getAuthorities());

Authentifizieren Sie sich mit dem generierten Authentifizierungstoken und legen Sie bei Erfolg das Authentifizierungstoken in "SecurityContextHolder" fest. Damit ist die Registrierung und Authentifizierung des Kontos abgeschlossen, und die nachfolgenden Vorgänge sind dieselben wie bei der Authentifizierung des Formulars.

authenticationManager.authenticate(authenticationToken);
if (authenticationToken.isAuthenticated()) {
  SecurityContextHolder.getContext().setAuthentication(authenticationToken);
} else {
  throw new RuntimeException("authenticate failure user:[" + user.toString() + "]");
}

Konto löschen

Bilden

Da aus dem Formular keine Informationen eingegeben werden können, handelt es sich um ein Formular mit nur einer Schaltfläche zum Senden. (CSRF-Token werden automatisch gesetzt.)

<form class="text-center" action="#" th:action="@{/account/delete}" th:object="${deleteForm}" method="post">
    <button class="btn indigo accent-4 text-white btn-block my-4" type="submit">Delete</button>
</form>

delete_form.png

Prozess löschen

In der Kontodienstklasse wird lediglich die Benutzerentität gelöscht, und die Authentifizierungsinformationen werden auf der Seite des aufrufenden Controllers zerstört.

@Service
@Slf4j
public class AccountServiceImpl implements AccountService {
  //...Kürzung...

  @Transactional
  @Override
  public void delete(final User user) {
    log.info("delete user:{}", user);
    userRepository.findById(user.getId())
        .ifPresentOrElse(findUser -> {
              userRepository.delete(findUser);
              userRepository.flush();
            },
            () -> {
              log.error("user not found:{}", user);
            });
  }

}

Anmeldeinformationen werden durch Ausführen der Abmeldeverarbeitung zerstört (Sitzung wird zerstört, Cookies werden gelöscht).

@PostMapping(value = "delete")
public String delete(@AuthenticationPrincipal SimpleLoginUser user, HttpServletRequest request,
                     DeleteForm deleteForm) {
  log.info("delete form user:{}", user.getUser());
  accountService.delete(user.getUser());
  try {
    request.logout();
  } catch (ServletException e) {
    throw new RuntimeException("delete and logout failure", e);
  }
  return "redirect:/";
}

Ergänzung

Spring Security Erweiterung von Thymeleaf

<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>

namespace

<html lang="ja"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/extras/spring-security">

Anonyme Benutzerbeurteilung

<p sec:authorize="isAnonymous()">anonymous user</p>
<p sec:authorize="!isAnonymous()">non anonymous user</p>

Beurteilung des authentifizierten Benutzers

<p sec:authorize="isAuthenticated()">authenticated user</p>
<p sec:authorize="!isAuthenticated()">non authenticated user</p>

Beurteilung von Benutzern mit Rollen

<p sec:authorize="hasRole('USER')">USER role authenticated user</p>

Benutzerinformationen ausgeben

<p sec:authentication="name">name</p>

Oder

<p sec:authentication="principal.username">username</p>

Principal ist in diesem Fall eine Instanz der Benutzerinformationsklasse (SimpleLoginUser).

Eine andere Art zu schreiben

Zusätzlich zu der obigen Beschreibungsmethode können Sie auch die Ausdrucksdienstprogrammobjekte "# authorisation" und "# authentication" verwenden.

#authorization

Ein Dienstprogrammobjekt, das den Authentifizierungsstatus überprüft.

<p th:if=${#authorization.expression('isAnonymous()')}></p>

#authentication

Stellt ein Spring Security-Authentifizierungsobjekt dar.

<p th:text="${#authentication.name}">name</p>

Seite Screenshot

Ich habe Bootstrap 4.3.1 und Material Design für Bootstrap 4.8.10 als CSS-Framework verwendet.

** Nr.1 Top ** 1_top.png

Debug-Informationen werden auf der oberen Seite ausgegeben. Insbesondere wird der Status mit dem folgenden Code angezeigt, damit der Benutzer, der auf die obere Seite zugreift, überprüfen kann, in welchem Authentifizierungsstatus er sich befindet.

<tr>
    <th scope="row">Anonymous</th>
    <th:block th:switch="${#authorization.expression('isAnonymous()')}">
        <td th:case="${true}"><span class="badge badge-info">yes</span></td>
        <td th:case="${false}">no</td>
    </th:block>
</tr>
<tr>
    <th>Authenticated</th>
    <th:block th:switch="${#authorization.expression('isAuthenticated()')}">
        <td th:case="${true}"><span class="badge badge-info">yes</span></td>
        <td th:case="${false}">no</td>
    </th:block>
</tr>
<tr>
    <th>FullyAuthenticated</th>
    <th:block th:switch="${#authorization.expression('isFullyAuthenticated()')}">
        <td th:case="${true}"><span class="badge badge-info">yes</span></td>
        <td th:case="${false}">no</td>
    </th:block>
</tr>
<tr>
    <th>RememberMe</th>
    <th:block th:switch="${#authorization.expression('isRememberMe()')}">
        <td th:case="${true}"><span class="badge badge-info">yes</span></td>
        <td th:case="${false}">no</td>
    </th:block>
</tr>

** Menü Nr. 2 ** 2_menu.png

** Nr.3 anmelden ** 3_signup.png

** Anmeldung Nr. 4 ** 4_signin.png

** Nr.5 abmelden ** 5_signout.png

** Nr.6 Passwort ändern ** 6_password_change.png

** Rollenwechsel Nr. 7 ** 7_role_change.png

** Nr.8 Profiländerung ** 8_profile_change.png

** Nr.9 Konto löschen ** 9_delete.png

Recommended Posts

Erstellen Sie mit Spring Security 2.1 eine einfache Demo-Site mit Spring Security
Erstellen Sie mit Spring Boot eine einfache Such-App
Implementieren Sie eine einfache Rest-API mit Spring Security & JWT mit Spring Boot 2.0
Erstellen Sie mit Spring Batch eine einfache On-Demand-Charge
Erstellen Sie eine Website mit Spring Boot + Gradle (jdk1.8.x)
Erstellen Sie einen Web-API-Server mit Spring Boot
Erstellen Sie mit Docker eine Spring Boot-Entwicklungsumgebung
Erstellen Sie mit Spring Boot 2.0 einen Spring Cloud Config Server mit Sicherheit
Erstellen Sie mit Spring Boot einen Mikrodienst
Erstellen Sie eine App mit Spring Boot 2
Erstellen Sie eine App mit Spring Boot
Erstellen wir eine einfache API mit EC2 + RDS + Spring Boot ①
Implementieren Sie einen einfachen Web-REST-API-Server mit Spring Boot + MySQL
Erstellen Sie mit Dropwizard eine einfache Webanwendung
Erstellen eines einfachen Balkendiagramms mit MPAndroidChart
Ich habe ein einfaches Suchformular mit Spring Boot + GitHub Search API erstellt.
Erreichen Sie die BASIC-Authentifizierung mit Spring Boot + Spring Security
Erstellen Sie mit dem Befehl cURL + tar ein Spring Boot-App-Entwicklungsprojekt
Schritte zum Erstellen einer einfachen Kamel-App mit Apache Camel Spring Boot-Startern
Hash-Passwörter mit Spring Boot + Spring Security (mit Salt, mit Stretching)
Erstellen Sie mit Java + MySQL ein einfaches Bulletin Board
Erstellen Sie eine Spring Boot-Anwendung mit IntelliJ IDEA
Erstellen Sie eine CRUD-App mit Spring Boot 2 + Thymeleaf + MyBatis
Erstellen Sie Ihr eigenes Dienstprogramm mit Thymeleaf mit Spring Boot
Erstellen Sie eine Spring Boot-Umgebung mit Windows + VS-Code
[Einführung in Spring Boot] Authentifizierungsfunktion mit Spring Security
Mit Spring Boot herunterladen
Erstellen einer EC-Site mit Rails 5 ⑨ ~ Erstellen einer Warenkorbfunktion ~
So erstellen Sie ein Spring Boot-Projekt in IntelliJ
[Spring Boot] So erstellen Sie ein Projekt (für Anfänger)
Spring Boot mit Spring Security Filter-Einstellungen und Suchtpunkten
[JUnit 5] Schreiben Sie einen Validierungstest mit Spring Boot! [Parametrisierungstest]
Ein Memorandum beim Erstellen eines REST-Service mit Spring Boot
Erstellen Sie Restapi mit Spring Boot (bis zum Ausführen der App)
Ich habe jetzt einen Test mit Spring Boot + JUnit 5 geschrieben
Ich habe versucht, mit Java und Spring eine Funktion / einen Bildschirm für den Administrator einer Einkaufsseite zu erstellen
Generieren Sie mit Spring Boot einen Barcode
Hallo Welt mit Spring Boot
Implementieren Sie GraphQL mit Spring Boot
Beginnen Sie mit Spring Boot
Führen Sie LIFF mit Spring Boot aus
SNS-Login mit Spring Boot
Datei-Upload mit Spring Boot
Spring Boot beginnt mit dem Kopieren
Erstellen Sie einen Spielplatz mit Xcode 12
Anmeldefunktion mit Spring Security
Hallo Welt mit Spring Boot
Setzen Sie Cookies mit Spring Boot
Verwenden Sie Spring JDBC mit Spring Boot
Modul mit Spring Boot hinzufügen
Erste Schritte mit Spring Boot
Versuchen Sie es mit Spring Boot Security
Mail mit Spring Boot verschicken
Erstellen Sie eine einfache CRUD mit SpringBoot + JPA + Thymeleaf ~ ~ Validierung hinzufügen ~
Eine Geschichte voller Grundlagen von Spring Boot (gelöst)
Erstellen Sie eine Hello World-Webanwendung mit Spring Framework + Jetty
Ich habe mit Spring Boot ein einfaches MVC-Beispielsystem erstellt
Neuer Mitarbeiter hat versucht, mit Spring Security eine Authentifizierungs- / Autorisierungsfunktion von Grund auf neu zu erstellen
Erstellen Sie einen einfachen Webserver mit der Java-Standardbibliothek com.sun.net.httpserver