Tous les articles ont été brièvement présentés, mais lorsque j'ai déplacé mes mains, il y avait beaucoup de choses que j'ai faites de manière inattendue. .. .. J'ai pensé que ce serait utile pour les débutants du printemps comme moi. Je vous serais reconnaissant de bien vouloir le signaler.
■ Qu'est-ce que Spring Security -L'authentification DB, l'authentification LDAP, l'authentification CAS, l'authentification JAAS, l'authentification X509 et l'authentification de base sont prises en charge comme méthodes d'authentification fournies par Security. Cette fois, l'authentification DB est effectuée.
■ Résultats attendus
・ 1. Ajout du contenu de build.gradle ・ 2. Paramètres d'authentification dans Security Config ・ 3. Implémentation de l'interface UserDetailsService (acquisition des informations d'authentification (créer un royaume)) ・ 4. Créer une classe LoginUser ・ 5. Création du contrôleur ・ 6. Création de HTML
Il y a six étapes principales.
J'ai posté la démo sur [github] plus tôt, veuillez donc vous y référer si vous le souhaitez.
■■■ Informations de connexion ■■■
Destination de la demande: http: // localhost: 8080 / loginForm
email:[email protected]
password:password
■■■■■■■■■■■■■
*** Je me suis référé à l'exemple de projet d'utilisation approfondie de la suprématie sur site Spring Boot2 **.
■ Informations environnementales jdk:11 Base de données: postgresql (docker donc aucune installation requise) ┗ À propos du docker de cette démo [here] IDE:IntelliJ Système de construction: gradle
Expliquons le code source.
build.gradle
plugins {
id 'org.springframework.boot' version '2.1.3.RELEASE'
id 'java'
}
apply plugin: 'io.spring.dependency-management'
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'mysql:mysql-connector-java'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
compile "org.springframework.boot:spring-boot-starter-validation"
runtimeOnly 'org.springframework.boot:spring-boot-devtools'
annotationProcessor "org.seasar.doma.boot:doma-spring-boot-starter:1.1.1"
compile("org.seasar.doma.boot:doma-spring-boot-starter:1.1.1") {
exclude group: "org.springframework.boot"
}
compile 'org.apache.commons:commons-lang3'
//spring security
compile 'org.springframework.boot:spring-boot-starter-security'
// thymeleaf(rouleau/Contrôle des modèles Thymeleaf par permissions)
compile "org.thymeleaf.extras:thymeleaf-extras-springsecurity5"
compile "org.modelmapper:modelmapper:0.7.5"
}
apply plugin: 'idea'
idea.module.inheritOutputDirs = true
processResources.destinationDir = compileJava.destinationDir
compileJava.dependsOn processResources
Seules les parties liées à la sécurité des ressorts sont commentées. Ceci est juste un ajout.
SecurityConfig.java
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import javax.sql.DataSource;
import static com.example.demo.common.WebConst.*;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
DataSource dataSource;
@Autowired
UserDetailsService userDetailsService;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
*Ne pas authentifier les fichiers statiques
* @param web
* @throws Exception
*/
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/favicon.ico", "/css/**", "/js/**", "/images/**", "/fonts/**", "/shutdown" /* for Demo */);
}
/**
*Paramètres qui utilisent un domaine d'authentification unique qui implémente l'interface UserDetailsService
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/loginFrom").permitAll()//Formulaire de connexion autorisé
.antMatchers("/user/**").permitAll()
.antMatchers("/new").permitAll()//Pour le test(Enregistrement de l'utilisateur)* Effacer lorsque vous avez terminé
.antMatchers("/index").permitAll()//Pour le test(Écran de transition après l'enregistrement de l'utilisateur) * Effacer une fois terminé
.antMatchers("/user/create").permitAll()//Fonction pour le test * Effacer une fois terminé
.anyRequest().authenticated();//L'accès n'est pas autorisé dans tous les autres cas sans authentification
http.formLogin()
.loginProcessingUrl("/login")//URL pour se connecter
.loginPage("/loginFrom")//URL de l'écran de connexion
.failureUrl("/login?error")//URL lorsque l'authentification échoue
.successForwardUrl("/success")//URL lorsque l'authentification est réussie
.usernameParameter("email")//Nom du paramètre utilisateur
.passwordParameter("password");//Nom du paramètre de mot de passe
http.logout()
.logoutUrl("/logout**")//URL à la déconnexion (non implémentée cette fois)
.logoutSuccessUrl("/login");//URL lorsque la déconnexion est réussie
}
}
・ @ Configuration
indique qu'il s'agit d'une classe de configuration (car elle définit un bean? Ambigu ici)
-Importer la classe de configuration fournie par Spring Security avec @ EnableWebSecurity
-La définition Bean de PasswordEncoder
.
-Il hérite de WebSecurityConfigurerAdapter
et remplace la méthode configure ()
. Dans celui-ci, définissez l '** URL du processus d'authentification ** et définissez les paramètres.
-Lorsque vous envoyez une demande à ** l'URL de traitement d'authentification **, le traitement est passé à la classe interne appelée AuthenticationConfiguration et il semble que le traitement d'authentification réel est effectué. (Extrait de la classe AuthenticationConfiguration ci-dessous)
AuthenticationConfiguration.java
@Configuration
@Import(ObjectPostProcessorConfiguration.class)
public class AuthenticationConfiguration {
...réduction...
@Override
public <T extends UserDetailsService> DaoAuthenticationConfigurer<AuthenticationManagerBuilder, T> userDetailsService(
T userDetailsService) throws Exception {
return super.userDetailsService(userDetailsService)
.passwordEncoder(this.defaultPasswordEncoder);
}
...
┗ Le concept de royaume Selon ce
Dans le système Web, la plage à laquelle la même politique d'authentification est appliquée est appelée un domaine, et le nom qui identifie chaque domaine est appelé un nom de domaine.
Puisqu'il y a une description, je pense qu'il suffit de reconnaître le traitement pour chaque utilisateur (non limité à) pour passer pour l'authentification.
Pour décrire brièvement ce que fait le royaume créé cette fois-ci en japonais, ** [Rechercher dans la base de données en utilisant le nom d'utilisateur comme clé, renvoyer les informations de l'utilisateur s'il existe et lever une exception s'il n'existe pas. ] ** est.
UserDaoRealm.java
package com.example.demo.security;
import com.example.demo.common.security.BaseRealm;
import com.example.demo.domain.dao.UserDao;
import com.example.demo.domain.dao.UserRoleDao;
import com.example.demo.domain.dto.User;
import com.example.demo.domain.dto.UserRole;
import lombok.extern.slf4j.Slf4j;
import org.seasar.doma.jdbc.NoResultException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
@Component
@Slf4j
public class UserDaoRealm extends BaseRealm {
@Autowired
UserDao userDao;
@Autowired
UserRoleDao userRoleDao;
@Override
protected UserDetails getLoginUser(String email) {
User user = null;
List<GrantedAuthority> authorityList = null;
try{
user = new User();
user.setEmail(email);
//Obtenir un utilisateur et enregistrer dans la session
user = userDao.select(user)
.orElseThrow(() -> new UsernameNotFoundException("no user found. [id=]" + email + "]"));
//Obtenir l'autorité de contact
List<UserRole> userRoles = userRoleDao.selectByUserId(user.getId(), toList());
//Mettez un préfixe sur la clé de rôle et assemblez-le
Set<String> roleKeys = userRoles.stream().map(UserRole::getRole_key).collect(toSet());
//Récupérer les clés d'autorisation
Set<String> permissionKeys = userRoles.stream().map(UserRole::getPermissionKey).collect(toSet());
//Transmettez les rôles et les autorisations en tant qu'autorité accordée
Set<String> authorities = new HashSet<>();
authorities.addAll(roleKeys);
authorities.addAll(permissionKeys);
authorityList = AuthorityUtils.createAuthorityList(authorities.toArray(new String[0]));
}catch (Exception e){
//Ne rien faire si aucune exception n'est levée
//Sinon, enveloppez-le dans une exception d'erreur d'authentification
if(!(e instanceof NoResultException)){
throw new UsernameNotFoundException("could not select user,", e);
}
}
return new LoginUser(user, authorityList);
}
}
-La classe abstraite BaseRealm
est créée en implémentant ʻUserDetailsService. Il hérite de
BaseRealm et crée ʻUserDaoRealm
.
-Doma est utilisé pour le processus d'acquisition d'informations de userDao. Pour plus d'informations, [ici]
-Implémenter LoginUser
avec NO.4.
LoginUser.java
package com.example.demo.security;
import com.example.demo.domain.dto.User;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import java.util.Collection;
@Data
public class LoginUser extends org.springframework.security.core.userdetails.User {
/**
*constructeur
*
* @param user
* @param authorities
*/
public LoginUser(User user, Collection<? extends GrantedAuthority> authorities){
super(String.valueOf(user.getEmail()), user.getPassword(), authorities);
}
}
Il hérite simplement de ʻorg.springframework.security.core.userdetails.User` et définit le constructeur.
Tout ce que vous avez à faire est de mapper l'URL suivante sur le contrôleur. · Formulaire de connexion ・ Processus de connexion (pour vérifier l'entrée, puis la transmettre à la sécurité du ressort) · Connexion réussie
LoginController.java
package com.example.demo.web.controller;
import com.example.demo.common.controller.AbstractHtmlController;
import com.example.demo.common.controller.BaseController;
import com.example.demo.web.form.LoginForm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import static com.example.demo.common.WebConst.*;
@Controller
public class LoginController extends AbstractHtmlController {
@Override
public String getFunctionName() {
return "A_LOGIN";
}
@ModelAttribute
LoginForm loginForm(){return new LoginForm();}
/**
*Affichage de l'écran de connexion
* @Afficher l'écran de connexion lors de l'utilisation de la méthode return get
*/
@GetMapping("/loginForm")
public String loginFrom(){
return "login/login";
}
/**
*Vérifiez l'entrée
*
* @param form
* @param br
* @return
*/
@PostMapping("/login")
public String index(@Validated @ModelAttribute LoginForm form, BindingResult br) {
//S'il y a une erreur de vérification d'entrée, revenez à l'écran d'origine
if (br.hasErrors()) {
return "login/login";
}
//20190309 Si la vérification d'entrée réussit, transférez au processus d'authentification défini dans Security Config
//20190309 Doit être la méthode Post, vous devez donc utiliser forward
return "forward:" + LOGIN_PROCESSING_URL;
}
/**
*Connexion réussie
*/
@PostMapping("/success")
public String loginsuccess(@ModelAttribute LoginForm loginForm, Model model,RedirectAttributes redirectAttributes){
model.addAttribute("msg","loginSuccess");
return "/login/success";
}
}
login.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Springboot</title>
<meta charset="utf-8" />
</head>
<body>
<h1 th:text="S'identifier"></h1>
<div th:if="${param.error}" class="alert alert-danger">
Le pseudo ou mot de passe est incorect.
</div>
<form th:action="@{'/login'}" action="../user/index.html" th:object = "${loginForm}" method="post">
<div class="form-group" th:classappend="${#fields.hasErrors('email')}? 'has-error'">
<label for="email" class="control-label">email</label>
<input id="email" type="text" class="form-control" th:field="*{email}" name="email">
<span th:if="${#fields.hasErrors('email')}" th:errors="*{email}" class="error-massages">error!</span>
</div>
<div class="form-group" th:classappend="${#fields.hasErrors('password')}? 'has-error'">
<label for="password" class="control-label">password</label>
<input id="password" type="password" class="form-control" th:field="*{password}" name="password">
<span th:if="${#fields.hasErrors('password')}" th:errors="*{password}" class="error-massages">error!</span>
</div>
<input type="submit" class="btn btn-default" value="S'identifier"/>
</form>
</body>
</html>
À th: if =" $ {param.error} "
, le paramètre d'erreur est reçu et une chaîne de caractères est sortie.
Lorsque le bouton de connexion est pressé avec th: action =" @ {'/ login'} "
, le processus de connexion est passé à la sécurité du printemps.
Cette fois, je me suis concentré sur la connexion de sécurité importante et printanière. La prochaine fois, j'aimerais essayer ** de changer l'écran à afficher en fonction de l'autorité **. Merci de votre attention.