Si vous voulez écrire du code rapidement sans histoire longue, ignorez [1. Introduction](# 1-Introduction). Pour les débutants de Spring, allez dans [2. Créer un projet](# 2-Créer un projet). Pour ceux qui souhaitent implémenter une simple fonction d'authentification / autorisation, allez dans [3. Implémentation d'une simple fonction d'authentification / autorisation](# 3-Implémentation d'une simple fonction d'authentification / autorisation). Ceux qui souhaitent implémenter la fonction d'authentification / autorisation pour une entreprise réelle doivent commencer à lire [4. Implémentation de la fonction d'authentification / autorisation pour une entreprise réelle](# 4-Implémentation de la fonction d'authentification / autorisation pour une entreprise réelle).
Tout d'abord, je vais vous expliquer l'authentification / l'autorisation. L'explication simple est la suivante. (Page de référence pour ceux qui veulent en savoir plus: Authentification et autorisation familières)
--Authentification Vérifiez avec qui vous communiquez </ font>.
--Autorisation Accordez l'autorisation d'accès aux ressources pour des conditions spécifiques telles que l'autorisation d'accès à l'écran et l'autorisation de traitement </ font>.
Lors de la mise en œuvre de cette fonction d'authentification / autorisation dans une application Web créée avec Spring Framework, elle peut être facilement implémentée à l'aide de Spring Security. Il existe de nombreux articles qui implémentent des fonctions d'authentification / autorisation à l'aide de Spring Security, mais je pense qu'il existe de nombreux articles tels que les suivants.
Cependant, l'utilisation de la base de données et le hachage des mots de passe sont monnaie courante </ font>. Lorsque j'ai créé un système Web avec des fonctions d'authentification / autorisation, j'ai eu beaucoup de mal à lire de nombreux articles et à décoder le code source sur GitHub. Sur la base de cette expérience, même les débutants en matière de sécurité Spring et d'authentification / autorisation peuvent utiliser la base de données et le hachage et ont besoin d'un article qui peut implémenter la fonction si vous lisez ceci </ font> J'ai pensé et écrit cet article. Aussi, dans le système utilisé sur le terrain, vous vous demandez comment sont implémentés le code source et les tables de base de données liés à l'authentification / autorisation. Par conséquent, j'ai écrit cet article avec l'intention d'étudier le type de fonction d'authentification / d'autorisation implémentée en faisant référence à notre exemple de construction de système </ font>.
――Vous n'avez pas besoin de lire le code source de GitHub, lisez simplement cet article, et même les débutants peuvent implémenter des fonctions d'authentification / autorisation à l'aide de Spring Security. Nous résumerons le contenu de l'enquête et des informations sur l'authentification et l'autorisation en se référant à notre exemple de construction de système comme méthode de mise en œuvre de la fonction d'authentification et d'autorisation pour les affaires réelles.
--Les personnes qui souhaitent implémenter des fonctions d'authentification / d'autorisation à l'aide de Spring Security
――Deux implémentations, une implémentation simple et une implémentation pour une entreprise réelle.
** Implémentation simple ** </ font> est une implémentation du traitement d'authentification / autorisation pour les débutants. Seule la partie implémentation du matériel de référence Spring Security est résumée par l'auteur. C'est parfait pour les débutants de Spring Security d'avoir une meilleure compréhension en l'exécutant sur leur propre appareil. ** La mise en œuvre pour les affaires réelles ** </ font> est basée sur des auditions internes sur la manière dont le traitement d'authentification / autorisation est réalisé dans un système réel. Nous mettrons en œuvre un traitement d'authentification / autorisation proche du système actuel. C'est une implémentation pour ceux qui développent le traitement d'authentification / autorisation en entreprise.
Les trois objectifs suivants s'appliquent à la fois à la mise en œuvre simple et à la mise en œuvre pratique </ font>.
1-7. GitHub Même si vous ne lisez pas le code source sur GitHub, vous pouvez implémenter la fonction en lisant cet article, mais pour ceux qui disent "Il est plus rapide de lire GitHub", je posterai le code source sur GitHub.
https://github.com/YukiYamagata/SpringSecurity-Qiita
--BasicAuth est le code source de la fonction d'authentification / autorisation d'implémentation simple.
Il est devenu.
"Fenêtre" -> "Perspective" -> "Ouvrir la perspective" -> "Autre"
Sélectionnez "Java EE" pour ouvrir
"Fichier" -> "Nouveau" -> "Projet Spring Starter"
Modifiez la valeur de chaque élément comme indiqué ci-dessous et cliquez sur "Suivant".
article | valeur |
---|---|
Nom | Valeur arbitraire |
Moule | Gradle (Buildship 3.x) |
Version Java | 11 |
Livrables | Valeur arbitraire |
Appuyez sur "Terminer"
Puisque les propriétés et yml ne sont décrits que légèrement différemment, il n'y a pas de problème même s'ils sont laissés en tant que "application.properties". J'aime savoir si je dois le changer. Toutefois, dans cet article, le code source est basé sur application.yml.
La figure ci-dessous est une image du système Web créé. Le flux de traitement détaillé du traitement de l'authentification et du traitement des autorisations est [3-5-1. Aperçu de la fonction d'authentification Spring Security](# 3-5-1-spring-security-Aperçu de la fonction d'authentification) et [3-6-1. Spring Aperçu de la fonction d'autorisation de sécurité](# 3-6-1-spring-security-aperçu de la fonction d'autorisation).
La figure ci-dessous montre la structure du projet du système.
Écrivez build.gradle pour définir les dépendances.
build.gradle
plugins {
id 'org.springframework.boot' version '2.3.3.RELEASE'
id 'io.spring.dependency-management' version '1.0.10.RELEASE'
id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5'
runtimeOnly 'com.h2database:h2'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
testImplementation 'org.springframework.security:spring-security-test'
}
test {
useJUnitPlatform()
}
H2DB est une base de données en mémoire écrite en Java. Les caractéristiques de H2DB sont les trois suivantes.
Puisqu'il s'agit d'une base de données en mémoire, les données seront perdues une fois le processus terminé. Il peut également être utilisé comme base de données embarquée, mais cette fois, il sera installé afin d'améliorer le contenu de la base de données pour les affaires réelles dans [4. Implémentation de la fonction d'authentification / autorisation pour les affaires réelles](# 4-Implémentation de la fonction d'authentification / autorisation pour les affaires réelles). Utilisé comme base de données de mémoire. Puisqu'il est en mémoire, la valeur initiale de H2DB doit être définie en exécutant une instruction SQL à chaque démarrage du traitement. Dans le cas de Spring Boot, en décrivant l'instruction SQL dans "schema.sql" et "data.sql", l'instruction SQL peut être exécutée automatiquement à chaque démarrage du processus. </ font>
Décrivez les paramètres H2DB dans application.yml.
application.yml
##datasource
spring:
datasource:
driver-class-name: org.h2.Driver
url: jdbc:h2:mem:testdb
username: sa
password: dm
Décrivez la structure de table de la base de données en SQL.
schema.sql
CREATE TABLE IF NOT EXISTS users (
username VARCHAR(18) NOT NULL PRIMARY KEY,
password VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL,
rolename VARCHAR(10) NOT NULL
);
Insérez des données dans H2DB. La chaîne de 60 caractères commençant par "\ $ 2a \ $ 10 \ $" est le mot de passe haché. Les détails seront expliqués dans [3-7. Hashing Passwords](# 3-7-Hashing Passwords), veuillez donc copier et coller ici.
Mot de passe simple | Mot de passe haché |
---|---|
admin | $2a$10$bCR1jXhdqbh1oC8ckXplxePYW5Kyb/VjN28MZx2PwXf1ybzLIFUQG |
user | $2a$10$yyT1siJCep647RT/I7KjcuUB5noFVU6RBo0FUXUJX.hb2MIlWTbDe |
data.sql
INSERT INTO users(username, password, name, rolename) VALUES ('admin', '$2a$10$bCR1jXhdqbh1oC8ckXplxePYW5Kyb/VjN28MZx2PwXf1ybzLIFUQG', 'admin-name', 'ADMIN');
INSERT INTO users(username, password, name, rolename) VALUES ('user' , '$2a$10$yyT1siJCep647RT/I7KjcuUB5noFVU6RBo0FUXUJX.hb2MIlWTbDe', 'user-name' , 'USER' );
Créez une application qui peut afficher et passer aux cinq pages ci-dessous.
Le traitement de l'application pour réaliser cette transition de page est la partie entourée de vert clair dans la figure ci-dessous. Pour vous concentrer sur le traitement d'authentification / autorisation, créez uniquement Controller et View pour le traitement de l'application. (Service, Dao, etc. ne sont pas créés.)
LoginController.java
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class LoginController {
@GetMapping("/loginForm")
String loginForm() {
return "login";
}
}
HomeController.java
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class HomeController {
@GetMapping("/home")
public String home() {
return "home";
}
}
AdminPageController.java
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class AdminPageController {
@GetMapping("/adminPage")
public String adminPage() {
return "adminPage";
}
}
UserPageController.java
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class UserPageController {
@GetMapping("/userPage")
public String userPage() {
return "userPage";
}
}
AccessDeniedPageController.java
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class AccessDeniedPageController {
@GetMapping("/accessDeniedPage")
public String accessDeniedPage() {
return "accessDeniedPage";
}
}
Créez un fichier HTML. Le code source utilise Thymeleaf comme moteur de modèle. Si vous n'avez pas de dossier "templates", ajoutez-en un sous src / main / resources.
login.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<meta charset="UTF-8">
<title>S'identifier</title>
</head>
<body>
<h1>S'identifier</h1>
<div th:if="${param.error}" > <!-- (1)Jugement des paramètres de la demande-->
<div>
Le nom d'utilisateur ou le mot de passe est différent.<!-- (2)Message d'erreur d'authentification-->
</div>
</div>
<form method="post" th:action="@{/authenticate}"> <!-- (3)Spécifiez le chemin du processus de connexion-->
<label>UserName:</label>
<input type="text" id="userName" name="userName"> <!-- (4)Entrée du nom d'utilisateur-->
<br>
<label>Password:</label>
<input type="password" id="password" name="password"> <!-- (5)Section de saisie du mot de passe-->
<br>
<input type="submit" id="submit" value="S'identifier">
</form>
</body>
</html>
Numéro d'article | La description |
---|---|
(1) | Jugez le message d'erreur défini dans le paramètre de demande. 3-5-6. JavaConfig(Ajouter le fichier) "WebSecurityConfig.java"DansModifiez le processus de jugement en fonction de la valeur définie dans "failureUrl"Veuillez noter que c'est nécessaire. |
(2) | Message d'exception à afficher lorsqu'une erreur d'authentification se produit |
(3) | Spécifiez la destination de la transition pour effectuer le traitement d'authentification dans l'attribut d'action du formulaire. Le chemin de destination de la transition est3-5-6. JavaConfig(Ajouter le fichier) "WebSecurityConfig.java"Dans"Correspond à la valeur spécifiée dans loginProcessingUrl »Il y a un besoin. Spécifiez "POST" pour la méthode HTTP. Dans ce cas${pageContext.request.contextPath}/Le processus d'authentification est exécuté en accédant à authenticate. |
(4) | Un élément qui est traité comme un "nom d'utilisateur" dans le processus d'authentification. L'attribut de nom est3-5-6. JavaConfig(Ajouter le fichier) "WebSecurityConfig.java"Dans"Correspond à la valeur spécifiée dans usernameParameter »Il y a un besoin. |
(5) | Un élément qui est traité comme un "mot de passe" dans le processus d'authentification. L'attribut de nom est3-5-6. JavaConfig(Ajouter le fichier) "WebSecurityConfig.java"Dans"Correspond à la valeur spécifiée par passwordParameter »Il y a un besoin. |
home.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<meta charset="UTF-8">
<title>Home</title>
</head>
<body>
<h1>Home</h1>
<form method="get" th:action="@{/adminPage}">
<input type="submit" value="Vers AdminPage">
</form>
<form method="get" th:action="@{/userPage}">
<input type="submit" value="Vers UserPage">
</form>
<form method="post" th:action="@{/logout}"> <!-- (1)Spécifiez le chemin de traitement de déconnexion-->
<input type="submit" value="Se déconnecter">
</form>
</body>
</html>
Numéro d'article | La description |
---|---|
(1) | Spécifiez le chemin pour exécuter le processus de déconnexion dans l'attribut action du formulaire. Le chemin du processus de déconnexion est la valeur par défaut de Spring Security,/Spécifiez la déconnexion. Spécifiez "POST" pour la méthode HTTP. |
adminPage.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<meta charset="UTF-8">
<title>AdminPage</title>
</head>
<body>
<h1>Admin Page</h1>
<table border="1">
<tr>
<th>article</th>
<th>Contenu</th>
</tr>
<tr>
<td>UserName</td>
<td><span sec:authentication="principal.username"></span></td> <!-- (1)Afficher le nom d'utilisateur-->
</tr>
<tr>
<td>Name</td>
<td><span sec:authentication="principal.name"></span></td> <!-- (2)Afficher le nom-->
</tr>
<tr>
<td>Role</td>
<td><span sec:authentication="principal.authorities"></span></td> <!-- (3)Afficher le rôle-->
</tr>
</table>
<form method="get" th:action="@{/home}">
<input type="submit" value="À la maison">
</form>
<form method="post" th:action="@{/logout}">
<input type="submit" value="Se déconnecter">
</form>
</body>
</html>
Numéro d'article | La description |
---|---|
(1) | Utilisez Thymeleaf pour accéder à l'objet d'authentification et afficher le nom d'utilisateur. |
(2) | Utilisez Thymeleaf pour accéder à l'objet d'authentification et afficher le nom. |
(3) | Utilisez Thymeleaf pour accéder à l'objet d'authentification et afficher le rôle. |
userPage.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<meta charset="UTF-8">
<title>UserPage</title>
</head>
<body>
<h1>User Page</h1>
<table border="1">
<tr>
<th>article</th>
<th>Contenu</th>
</tr>
<tr>
<td>UserName</td>
<td><span sec:authentication="principal.username"></span></td>
</tr>
<tr>
<td>Name</td>
<td><span sec:authentication="principal.name"></span></td>
</tr>
<tr>
<td>Role</td>
<td><span sec:authentication="principal.authorities"></span></td>
</tr>
</table>
<form method="get" th:action="@{/home}">
<input type="submit" value="À la maison">
</form>
<form method="post" th:action="@{/logout}">
<input type="submit" value="Se déconnecter">
</form>
</body>
</html>
accessDeniedPage.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<meta charset="UTF-8">
<title>Access Denied</title>
</head>
<body>
<h1>Accès refusé</h1>
<form method="get" th:action="@{/home}">
<input type="submit" value="À la maison">
</form>
<form method="post" th:action="@{/logout}">
<input type="submit" value="Se déconnecter">
</form>
</body>
</html>
La partie entourée de rose dans la figure ci-dessous montre la fonction d'authentification.
Le déroulement du processus d'authentification est le suivant. (Le numéro de traitement correspond au numéro de la figure ci-dessus)
(※1) UserDetailsService C'est une interface qui est responsable de l'acquisition des informations d'identification (nom d'utilisateur et mot de passe) requises pour le processus d'authentification et l'état de l'utilisateur. (※2) UserDetails Une interface qui fournit les informations d'identification et l'état de l'utilisateur, créée à partir de ʻUserDetailsService`.
Lors de l'exécution du traitement d'authentification à l'aide de DB, créez la classe d'implémentation UserDetailsService (AccountUserDetailsService dans cet article) et la classe d'implémentation UserDetails (AccountUserDetails dans cet article) en fonction des exigences de l'application. </ font> Doit être.
MyUser.java
package com.example.demo.entity;
import java.io.Serializable;
public class MyUser implements Serializable{
private String userName; //Dans H2DB, la table des utilisateurs"username"Champ à stocker
private String password; //Dans H2DB, la table des utilisateurs"password"Champ à stocker
private String name; //Dans H2DB, la table des utilisateurs"name"Champ à stocker
private String roleName; //Dans H2DB, la table des utilisateurs"roleName"Champ à stocker
/**
* getter, setter
*/
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
}
Accédez à la base de données à l'aide de Spring JDBC.
UserDao.java
package com.example.demo.repository;
import com.example.demo.entity.MyUser;
public interface UserDao {
MyUser findUserByUserName(String userName);
}
UserDaoImpl.java
package com.example.demo.repository;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import com.example.demo.entity.MyUser;
@Repository
public class UserDaoImpl implements UserDao {
private final JdbcTemplate jdbcTemplate;
@Autowired
public UserDaoImpl(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
/**
*Exécutez l'instruction SELECT avec userName comme condition de recherche pour rechercher les utilisateurs enregistrés dans la base de données.
* @param userName
* @return User
*/
@Override
public MyUser findUserByUserName(String userName) {
String sql = "SELECT username, password, name, rolename FROM users WHERE username = ?";
//Obtenez un utilisateur
Map<String, Object> result = jdbcTemplate.queryForMap(sql, userName);
//Classe d'entité(Type d'utilisateur)Conversion en
MyUser user = convMapToUser(result);
return user;
}
/**
*Résultat de l'exécution de l'instruction SQL SELECT(Map<String, Object>)Au type d'utilisateur
* @param Map<String, Object>
* @return User
*/
private MyUser convMapToUser(Map<String, Object> map) {
MyUser user = new MyUser();
user.setUserName((String) map.get("username"));
user.setPassword((String) map.get("password"));
user.setName((String) map.get("name"));
user.setRoleName((String) map.get("rolename"));
return user;
}
}
AccountUserDetailsService.java
package com.example.demo.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
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 com.example.demo.entity.MyUser;
import com.example.demo.repository.UserDao;
@Service
public class AccountUserDetailsService implements UserDetailsService {
private final UserDao userDao;
@Autowired
public AccountUserDetailsService(UserDao userDao) {
this.userDao = userDao;
}
@Override
public UserDetails loadUserByUsername(String userName)
throws UsernameNotFoundException { // --- (1)Méthode pour récupérer les informations de compte de la base de données
if (userName == null || "".equals(userName)) {
throw new UsernameNotFoundException(userName + "is not found");
}
//Obtenir une exception d'utilisateur se produit s'il n'y a pas de nom d'utilisateur
try {
//Obtenir un utilisateur
MyUser myUser = userDao.findUserByUserName(userName);
if (myUser != null) {
return new AccountUserDetails(myUser); // --- (2)Générer une classe d'implémentation pour UserDetails
} else {
throw new UsernameNotFoundException(userName + "is not found");
}
} catch (EmptyResultDataAccessException e) {
throw new UsernameNotFoundException(userName + "is not found");
}
}
}
Numéro d'article | La description |
---|---|
(1) | Recherchez les informations de compte dans la base de données. Lancer une exception UsernameNotFoundException si les informations de compte ne sont pas trouvées |
(2) | Si les informations de compte sont trouvées, la classe d'implémentation de UserDetails(AccountUserDetails)Générer. |
AccountUserDetails.java
package com.example.demo.service;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import com.example.demo.entity.MyUser;
public class AccountUserDetails implements UserDetails {
private final MyUser myUser;
public AccountUserDetails(MyUser myUser) {
this.myUser = myUser;
}
public MyUser getUser() { // --- (1)Méthode pour renvoyer MyUser qui est Entity
return myUser;
}
public String getName() { // --- (2)Méthode pour renvoyer le nom
return this.myUser.getName();
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() { // --- (3)Méthode pour renvoyer la liste des autorisations données à l'utilisateur
return AuthorityUtils.createAuthorityList("ROLE_" + this.myUser.getRoleName());
}
@Override
public String getPassword() { // --- (4)Méthode pour renvoyer le mot de passe enregistré
return this.myUser.getPassword();
}
@Override
public String getUsername() { // --- (5)Méthode pour renvoyer le nom d'utilisateur
return this.myUser.getUserName();
}
@Override
public boolean isAccountNonExpired() { // --- (6)Méthode pour déterminer le statut d'expiration d'un compte
return true;
}
@Override
public boolean isAccountNonLocked() { // --- (7)Méthode pour déterminer l'état de verrouillage d'un compte
return true;
}
@Override
public boolean isCredentialsNonExpired() { // --- (8)Une méthode pour déterminer l'état d'expiration des informations d'identification
return true;
}
@Override
public boolean isEnabled() { // --- (9)Méthode pour déterminer s'il s'agit d'un utilisateur valide
return true;
}
}
Numéro d'article | La description |
---|---|
(1) | Une méthode qui renvoie MyUser, qui est une entité. Préparez une méthode getter afin que les informations de compte soient accessibles dans le processus après un processus d'authentification réussi. |
(2) | Une méthode qui renvoie le nom. |
(3) | Une méthode qui renvoie la liste d'autorisations donnée à l'utilisateur. Cette méthode est utilisée dans le processus d'autorisation. Dans le processus d'autorisation de Spring Security, "ROLE_Les informations d'autorité commençant par "sont traitées comme un rôle. Par conséquent, "ROLE_"Est ajouté. |
(4) | Une méthode pour renvoyer le mot de passe enregistré. Le mot de passe renvoyé par cette méthode est utilisé pour la comparaison avec le mot de passe spécifié par le client. |
(5) | Une méthode qui renvoie un nom d'utilisateur. |
(6) | Une méthode pour déterminer le statut d'expiration d'un compte. S'il est dans la date d'expiration, true est renvoyé, et s'il a expiré, false est renvoyé. Dans ce programme, seul vrai est renvoyé par souci de simplicité. |
(7) | Une méthode pour déterminer l'état de verrouillage d'un compte. S'il n'est pas verrouillé, ture est retourné, et si le compte est verrouillé, false est retourné. Dans ce programme, seul vrai est renvoyé par souci de simplicité. |
(8) | Une méthode qui détermine l'état d'expiration des informations d'identification. S'il est dans la date d'expiration, true est renvoyé, et s'il a expiré, false est renvoyé. Dans ce programme, seul vrai est renvoyé par souci de simplicité. |
(9) | Une méthode qui détermine si l'utilisateur est valide. S'il est valide, il renvoie vrai, et s'il s'agit d'un utilisateur non valide, il renvoie faux. Dans ce programme, seul vrai est renvoyé par souci de simplicité. |
WebSecurityConfig.java
package com.example.demo.config;
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.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import com.example.demo.service.AccountUserDetailsService;
@Configuration
@EnableWebSecurity // --- (1)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
AccountUserDetailsService userDetailsService;
PasswordEncoder passwordEncoder() {
//Hash des mots de passe à l'aide de l'algorithme BCrypt
return new BCryptPasswordEncoder(); // --- (2)Utiliser l'algorithme BCrypt
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//Définir le UserDetailsService implémenté dans AuthenticationManagerBuilder
auth.userDetailsService(userDetailsService) // --- (3)Définir le UserDetailsService créé
.passwordEncoder(passwordEncoder()); // --- (2)Spécifiez comment hacher le mot de passe(Algorithme BCrypt)
}
@Override
protected void configure(HttpSecurity http) throws Exception {
//Paramètres d'autorisation
http.authorizeRequests()
.antMatchers("/loginForm").permitAll() // --- (4) /loginForm permet l'accès de tous les utilisateurs
.anyRequest().authenticated(); // --- (5) /Demander l'authentification sauf loginForm
//Paramètres de connexion
http.formLogin() // --- (6)Activer l'authentification par formulaire
.loginPage("/loginForm") // --- (7)Chemin pour afficher le formulaire de connexion
.loginProcessingUrl("/authenticate") // --- (8)Chemin du processus d'authentification par formulaire
.usernameParameter("userName") // --- (9)Nom du paramètre de demande de nom d'utilisateur
.passwordParameter("password") // --- (10)Nom du paramètre de demande de mot de passe
.defaultSuccessUrl("/home") // --- (11)Chemin par défaut de la transition lorsque l'authentification est réussie
.failureUrl("/loginForm?error=true"); // --- (12)Chemin vers la transition lorsque l'authentification échoue
//Paramètres de déconnexion
http.logout()
.logoutSuccessUrl("/loginForm") // --- (13)Chemin vers la transition lorsque la déconnexion est réussie
.permitAll(); // --- (14)Autoriser l'accès à tous les utilisateurs
}
}
Numéro d'article | La description |
---|---|
(1) | @EnableWebSecurity Si est spécifié, la définition du bean du composant requis pour utiliser Spring Security est automatiquement effectuée. |
(2) | Utilisez l'algorithme BCrypt pour définir le hachage du mot de passe. |
(3) | AuthenticationManagerBuilder Créé enUserDetailsService Mettre en place. |
(4) | /loginForm permet l'accès de tous les utilisateurs. |
(5) | /Autre que loginForm, il nécessite une authentification. Les utilisateurs non authentifiés sont redirigés vers l'écran de connexion. Les utilisateurs authentifiés mais non autorisés se voient refuser l'accès. |
(6) | formLogin L'authentification par formulaire est activée lorsque vous appelez la méthode. |
(7) | Spécifiez le chemin pour afficher le formulaire de connexion. Si un utilisateur anonyme tente d'accéder à une page nécessitant une authentification, il sera redirigé vers le chemin spécifié ici. |
(8) | Spécifiez le chemin pour le traitement de l'authentification par formulaire.3-4-4. View(Ajouter le fichier)S'identifier.En htmlFaire correspondre l'attribut d'action de la balise formIl y a un besoin. |
(9) | Spécifiez le nom du paramètre de demande du nom d'utilisateur qui est l'information d'identification.3-4-4. View(Ajouter le fichier)S'identifier.En htmlFaites correspondre l'attribut name de la balise d'entrée pour saisir le nom d'utilisateurIl y a un besoin. |
(10) | Spécifiez le nom du paramètre de demande du mot de passe qui est les informations d'identification.3-4-4. View(Ajouter le fichier)S'identifier.En htmlFaites correspondre l'attribut name de la balise d'entrée pour saisir le mot de passeIl y a un besoin. |
(11) | Spécifiez le chemin par défaut de la transition lorsque l'authentification est réussie. |
(12) | Spécifiez le chemin vers lequel effectuer la transition en cas d'échec de l'authentification. |
(13) | Spécifiez le chemin vers lequel effectuer la transition lorsque la déconnexion est réussie. |
(14) | Autorisez tous les utilisateurs à accéder aux déconnexions et aux chemins qui transitent après une déconnexion réussie. |
La partie entourée de bleu clair dans la figure ci-dessous montre la fonction d'autorisation.
--Si l'accès est accordé Passez au filtre suivant.
Le fichier auquel le code est ajouté est surligné en bleu.
Si vous essayez d'accéder à une page qui n'est pas autorisée, vous serez redirigé vers une page qui indique que l'accès a été refusé.
WebSecurityConfig.java
/*réduction*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) //Postscript--- (1)Activer le traitement des autorisations de méthode
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
/*réduction*/
protected void configure(HttpSecurity http) throws Exception {
//Paramètres d'autorisation
http.exceptionHandling() //Postscript
.accessDeniedPage("/accessDeniedPage") //Postscript--- (2)Chemin vers la transition lorsque l'accès est refusé
.and() //Postscript
.authorizeRequests()
.antMatchers("/loginForm").permitAll()
.anyRequest().authenticated();
/*réduction*/
Numéro d'article | La description |
---|---|
(1) | @PreAuthorize Ou@PostAuthorize Activez le processus d'autorisation de méthode en. |
(2) | Accès refusé(Erreur d'autorisation)Une fois terminé, spécifiez le chemin vers lequel effectuer la transition. |
Décrivez «@ PreAuthorize» dans chaque méthode pour définir l'autorité d'accès.
AdminPageController.java
/*réduction*/
@GetMapping("/adminPage")
@PreAuthorize("hasRole('ROLE_ADMIN')") //Postscript--- (1) ROLE_Autoriser l'accès uniquement aux utilisateurs ADMIN
public String adminPage() {
/*réduction*/
article | La description |
---|---|
(1) | 「ROLE_Seuls les utilisateurs qui ont "ADMIN" sont autorisés à accéder. |
UserPageController.java
/*réduction*/
@GetMapping("/userPage")
@PreAuthorize("hasRole('ROLE_USER')") //Postscript--- (1) ROLE_Autoriser l'accès uniquement aux utilisateurs USER
public String userPage() {
/*réduction*/
article | La description |
---|---|
(1) | 「ROLE_Seuls les utilisateurs qui ont "USER" ont accès. |
Sur la page "home", les boutons "to adminPage" et "to userPage" seront affichés en fonction des informations de connexion.
home.html
<!--réduction-->
<body>
<h1>Home</h1>
<div sec:authorize="hasRole('ADMIN')"> <!--Postscript(1) ROLE_Afficher uniquement les utilisateurs ADMIN-->
<form method="get" th:action="@{/adminPage}">
<input type="submit" value="Vers AdminPage">
</form>
</div>
<div sec:authorize="hasRole('USER')"> <!--Postscript(2) ROLE_Afficher uniquement les utilisateurs USER-->
<form method="get" th:action="@{/userPage}">
<input type="submit" value="Vers UserPage">
</form>
</div>
<div sec:authorize="isAuthenticated()"> <!--Postscript(3)Afficher uniquement les utilisateurs connectés-->
<form method="post" th:action="@{/logout}">
<input type="submit" value="Se déconnecter">
</form>
</div>
</body>
</html>
article | La description |
---|---|
(1) | 「ROLE_Seuls les utilisateurs qui ont "ADMIN" sont affichés. |
(2) | 「ROLE_Seuls les utilisateurs qui ont "USER" sont affichés. |
(3) | Vous êtes connecté(Agréé)Seul l'utilisateur est affiché. |
Le hachage est la conversion d'une chaîne en une autre valeur (valeur de hachage) à l'aide d'un algorithme spécifique (fonction de hachage). Il est souvent utilisé pour stocker les mots de passe. La différence entre le chiffrement et le hachage est de savoir s'il peut être restauré (déchiffré) à sa valeur d'origine. Les valeurs chiffrées peuvent être déchiffrées, mais les valeurs hachées ne peuvent pas être déchiffrées </ font>. Par conséquent, il est extrêmement difficile pour un tiers de déterminer les données d'origine à partir de la valeur de hachage </ font>. Spring Security recommande d'utiliser BCryptPasswordEncoder sauf si vous avez des exigences spécifiques pour le hachage.
Si vous souhaitez en savoir plus sur le hachage, veuillez vous référer à la page suivante. [Connaissances de base] Qu'est-ce que le cryptage? Qu'est-ce que le hachage? Comment éviter les fuites de mot de passe?
Je pense qu'il est préférable de créer le programme suivant avec un projet nouvellement créé. (Spring Security doit toujours être défini comme une dépendance dans le nouveau projet.) Vous pouvez créer un programme avec un projet existant, mais l'auteur pense qu'il est préférable de créer un nouveau projet car plusieurs fonctions principales peuvent être créées dans le même système. Remplacez la valeur rawPassword (admin dans votre code) par la valeur que vous souhaitez définir comme mot de passe.
GeneratePassword.java
package com.example.demo.password;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
public class GeneratePassword {
public static void main(String[] args) {
//Entrez le mot de passe que vous souhaitez hacher
String rawPassword = "admin";
//Mot de passe de hachage
String password = getEncodePassword(rawPassword);
//Afficher la valeur hachée
System.out.println(password);
}
private static String getEncodePassword(String rawPassword) {
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder ();
return passwordEncoder.encode(rawPassword);
}
}
Exécutez le programme créé dans [3-7-2. Création d'un programme de hachage de mot de passe](# 3-7-2-Créer un programme de hachage de mot de passe) dans une application Java.
La valeur affichée sur la console est le mot de passe haché.
Présentation de la mise en œuvre de l'authentification multifacteur (MFA). Ces dernières années, de nombreux systèmes ont introduit l'authentification multifacteur pour améliorer la sécurité. De plus, étant donné que MFA est installé dans beaucoup de nos systèmes, j'aimerais écrire un article sur la façon d'implémenter MFA en tant qu'implémentation pratique. La mise en œuvre de MFA sera un peu longue et sera expliquée en détail dans l'article ci-dessous. Il s'agit d'un article d'implémentation de MFA utilisant un mot de passe et un mot de passe à usage unique. En cours de création (veuillez patienter un peu plus longtemps)
Après avoir entendu parler du système actuel de l'entreprise, l'un d'entre eux était de «stocker les informations de contrôle d'accès dans la base de données et d'effectuer le traitement des autorisations». (Dans [3. Implémentation de la fonction d'authentification / autorisation simple](# 3-Implémentation de la fonction d'authentification / d'autorisation simple), le traitement des autorisations a été effectué à l'aide d'annotations.) Je voudrais écrire un article sur la façon de mettre en œuvre le traitement des autorisations basé sur les informations de contrôle d'accès stockées dans cette base de données en tant qu'implémentation pour une entreprise réelle.
Dans l'implémentation simple, la fonction d'autorisation a été implémentée en utilisant @ PreAuthorize
. Cependant, dans un système réel, il semble que la fonction d'autorisation soit souvent implémentée à l'aide de DB. La raison en est que gère facilement les pages qui permettent d'accéder à Role </ font>.
Lorsque @ PreAuthorize
est utilisé, des annotations sont ajoutées pour chaque contrôleur et méthode, il est donc distribué dans divers fichiers et il devient difficile de savoir quelle page est autorisée à quel rôle. Cela peut entraîner une très mauvaise visibilité et une inefficacité lors de la relecture ultérieure du code ou de la modification du système.
D'autre part, en créant le tableau suivant dans DB et en gérant les informations de contrôle d'accès uniquement dans DB, il est beaucoup plus facile de gérer les rôles et les pages qui permettent l'accès </ font> > Devient.
Voici un exemple d'une partie du tableau utilisée cette fois. Le «*» dans le rôle est un caractère générique, ce qui signifie accorder des autorisations à tous les rôles (y compris les utilisateurs non authentifiés). Cela signifie également autoriser / adminPage
à ʻADMIN Role et
/ userPage à ʻUSER
Role.
Role | Authorize Page |
---|---|
* | /loginForm |
ADMIN | /adminPage |
USER | /userPage |
Les transitions de page sont les mêmes que dans l'implémentation simple, comme illustré dans la figure ci-dessous.
La partie entourée de bleu dans la figure ci-dessous montre le processus d'approbation des affaires réelles. Créez un processus de vote, un service et un DAO dans le processus d'autorisation. La fonction d'autorisation est réalisée en interrogeant la base de données pour voir si la page (URL) à laquelle les informations de l'utilisateur client (rôle) essaient d'accéder est autorisée.
Le déroulement du traitement des autorisations est le suivant. (Le numéro de traitement correspond au numéro de la figure ci-dessus)
** (* 2) **. (Dans "Affirmative Based", il est possible de définir plusieurs classes d'implémentation de ʻAccessDecisionVoter
et d'appeler chaque processus de vote, mais cette implémentation ne définit que "MyVoter".)) lorsque l'autorisation d'accès (
true) est renvoyée de" MyVoter ", et refusez (ʻACCESS_DENIED
) lorsque l'accès refusé ( false
) est renvoyé "AffirmativeBased Retourner à. (Détails ** (* 2) **)), et lève une exception ʻAccessDeniedException
si elle est refusée (ʻACCESS_DENIED`). (Détails ** (* 1) **) est renvoyé. ʻAccessDeniedException
est intercepté par le "Filtre de traduction d'exception".(※1) AccessDecisionManager
Une interface qui vérifie si vous avez accès à la ressource à laquelle vous essayez d'accéder.
Il existe trois types de classes d'implémentation fournies par Spring Security, mais toutes appellent la méthode vote
de l'interface appelée ʻAccessDecisionVoter pour déterminer s'il faut accorder des droits d'accès. ʻAccessDecisionVoter
vote pour "accorder", "refuser" ou "retirer", et la classe d'implémentation de ʻAccessDecisionManager agrège les résultats du vote pour déterminer le droit d'accès final. S'il détermine que vous n'avez pas de droits d'accès, il lèvera ʻAccessDeniedException
et refusera l'accès.
Le tableau ci-dessous montre les classes d'implémentation fournies par Spring Security.
Classe d'implémentation | La description |
---|---|
AffirmativeBased | AccessDecisionVoter VoterAccorder l’accès lors du vote d’une «subvention»Classe d'implémentation.デフォルトで使用されるClasse d'implémentation. |
ConsensusBased | TousAccessDecisionVoter VoterDonner l'accès lorsque le nombre de votes "accordés" est élevéClasse d'implémentation. |
UnanimousBased | AccessDecisionVoter VoterAucun droit d'accès lorsqu'un seul "refus" est votéClasse d'implémentation. |
(※2) AccessDecisionVoter
ʻAccessDecisionVoter est une interface pour voter d'accorder ou non l'accès en se référant à la politique d'accès spécifiée pour la ressource à laquelle vous essayez d'accéder. ʻAccessDecisionVoter
vote pour "accorder", "rejeter" ou retirer.
Le tableau ci-dessous présente les principales classes d'implémentation fournies par Spring Security.
Classe d'implémentation | La description |
---|---|
WebExpressionVoter | Spring Expression Language (SpEL)Informations d'identification de l'utilisateur utilisant(Authentication )Et demander des informations(HttpServletRequest )Une classe d'implémentation qui fait référence et vote. |
RoleVoter | Une classe d'implémentation qui fait référence au rôle et aux votes de l'utilisateur. |
RoleHierarchyVoter | Une classe d'implémentation qui vote en se référant au rôle en couches que possède l'utilisateur. |
AuthenticatedVoter | Une classe d'implémentation qui vote en se référant à l'état d'authentification. |
Dans cette implémentation, une nouvelle classe appelée "MyVoter" qui implémente ʻAccessDecisionVoter est créée, et seul ce "MyVoter" est défini pour le processus de vote appelé par ʻAffirmativeBased
.
Sur la base de ce qui a été implémenté dans [3. Implémentation de la fonction d'authentification / autorisation simple](# 3-Implémentation de la fonction d'authentification / autorisation simple), mettez en surbrillance les fichiers à ajouter en rouge et les fichiers à ajouter ou modifier du code en bleu. Je suis.
schema.sql
/*Ajoutez le code suivant à la fin du fichier*/
CREATE TABLE IF NOT EXISTS access_authorization (
rolename VARCHAR(10) NOT NULL,
uri VARCHAR(255) NOT NULL,
PRIMARY KEY(rolename, uri)
);
data.sql
/*Ajoutez le code suivant à la fin du fichier*/
/*Toutes les autorisations de rôle*/
INSERT INTO access_authorization(rolename, uri) VALUES ('*', '/loginForm');
INSERT INTO access_authorization(rolename, uri) VALUES ('*', '/accessDeniedPage');
INSERT INTO access_authorization(rolename, uri) VALUES ('*', '/logout');
/*Autorisations de rôle ADMIN*/
INSERT INTO access_authorization(rolename, uri) VALUES ('ADMIN', '/home');
INSERT INTO access_authorization(rolename, uri) VALUES ('ADMIN', '/adminPage');
/*Autorisations du rôle USER*/
INSERT INTO access_authorization(rolename, uri) VALUES ('USER', '/home');
INSERT INTO access_authorization(rolename, uri) VALUES ('USER', '/userPage');
Un roleName de "*" est un caractère générique qui signifie que accorde l'accès à tous les rôles </ font>.
AdminPageController.java
/*réduction*/
@GetMapping("/adminPage")
// @PreAuthorize("hasRole('ROLE_ADMIN')")Commenter
public String adminPage() {
/*réduction*/
UserPageController.java
/*réduction*/
@GetMapping("/userPage")
// @PreAuthorize("hasRole('ROLE_USER')")Commenter
public String userPage() {
/*réduction*/
AccessAuthorization.java
package com.example.demo.entity;
import java.io.Serializable;
public class AccessAuthorization implements Serializable {
String roleName; //Accès dans H2DB_De la table d'autorisation"rolename"Champ à stocker
String uri; //Accès dans H2DB_De la table d'autorisation"uri"Champ à stocker
/**
* getter, setter
*/
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public String getUri() {
return uri;
}
public void setUri(String uri) {
this.uri = uri;
}
}
Accédez à la base de données à l'aide de Spring JDBC.
AccessAuthorizationDao.java
package com.example.demo.repository;
import com.example.demo.entity.AccessAuthorization;
public interface AccessAuthorizationDao {
AccessAuthorization find(String roleName, String uri);
}
AccessAuthorizationDaoImpl.java
package com.example.demo.repository;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import com.example.demo.entity.AccessAuthorization;
@Repository
public class AccessAuthorizationDaoImpl implements AccessAuthorizationDao {
private final JdbcTemplate jdbcTemplate;
@Autowired
public AccessAuthorizationDaoImpl(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
/**
*Exécutez l'instruction SELECT avec roleName et uri comme conditions de recherche pour rechercher s'ils sont enregistrés dans la base de données.
* @param roleName
* @param uri
* @return AccessAuthorization
*/
@Override
public AccessAuthorization find(String roleName, String uri) {
String sql = "SELECT rolename, uri FROM access_authorization WHERE rolename = ? AND uri = ?";
//Obtenez un utilisateur
Map<String, Object> result = jdbcTemplate.queryForMap(sql, roleName, uri);
//Classe d'entité(Type d'utilisateur)Conversion en
AccessAuthorization auth = convMapToAccessAuthorization(result);
return auth;
}
/**
*Résultat de l'exécution de l'instruction SQL SELECT(Map<String, Object>)Pour accéder au type d'autorisation
* @param Map<String, Object>
* @return AccessAuthorization
*/
private AccessAuthorization convMapToAccessAuthorization(Map<String, Object> map) {
AccessAuthorization auth = new AccessAuthorization();
auth.setRoleName((String) map.get("rolename"));
auth.setUri((String) map.get("uri"));
return auth;
}
}
AuthorizationService.java
package com.example.demo.service;
public interface AuthorizationService {
boolean isAuthorized(String roleName, String uri);
}
AuthorizationServiceImpl.java
package com.example.demo.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import com.example.demo.entity.AccessAuthorization;
import com.example.demo.repository.AccessAuthorizationDao;
@Service
public class AuthorizationServiceImpl implements AuthorizationService {
private final AccessAuthorizationDao authDao;
@Autowired
public AuthorizationServiceImpl(AccessAuthorizationDao authDao) {
this.authDao = authDao;
}
/**
*Détermine si la combinaison RoleName et URI passée dans l'argument est autorisée.
* @param roleName
* @param uri
* @return boolean
*/
@Override
public boolean isAuthorized(String roleName, String uri) { // ---(1)Méthode pour déterminer si l'accès est autorisé
if (StringUtils.isEmpty(roleName)) {
throw new IllegalArgumentException("RoleName est vide.");
}
if (StringUtils.isEmpty(uri)) {
throw new IllegalArgumentException("L'URI est vide.");
}
//Obtenir une exception AccessAuthorization se produit s'il n'y a pas d'AccessAuthorization
try {
AccessAuthorization auth = authDao.find(roleName, uri); // ---(2)Obtenir une instance AccessAuthorization
if (auth != null) { // ---(3)Autorisations
return true;
} else { // ---(4)Accès refusé
return false;
}
} catch (EmptyResultDataAccessException e) { // ---(5)Accès refusé
return false;
}
}
}
article | La description |
---|---|
(1) | Rôle de l'utilisateur Nom et chemin d'accès(URI)Est une méthode qui détermine si l'accès est autorisé. |
(2) | Passez le roleName et l'URI à Dao et devenez une entitéAccessAuthorization Obtenez une instance de. |
(3) | AccessAuthorization Si vous pouvez obtenir une instance de, déterminez que l'accès est autorisé ettrue rends le. |
(4) | Depuis DaoAccessAuthorization Impossible d'obtenir une instance denull Si est retourné, il est jugé que l'accès est refusé et l'accès est refusé.false rends le. |
MyVoter.java
package com.example.demo.voter;
import java.util.Collection;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.FilterInvocation;
import org.springframework.stereotype.Component;
import com.example.demo.service.AccountUserDetails;
import com.example.demo.service.AuthorizationService;
@Component
public class MyVoter implements AccessDecisionVoter<FilterInvocation> {
private final AuthorizationService authorizationService;
@Autowired
public MyVoter(AuthorizationService authorizationService) {
this.authorizationService = authorizationService;
}
@Override
public boolean supports(ConfigAttribute attribute) { // ---(1)Méthode pour déterminer si le vote est requis ou non
return true;
}
@Override
public boolean supports(Class<?> clazz) { // ---(1)Méthode pour déterminer si le vote est requis ou non
return true;
}
@Override
public int vote(Authentication authentication, FilterInvocation filterInvocation,
Collection<ConfigAttribute> attributes) { // ---(2)Une méthode pour voter pour accorder l'accès
HttpServletRequest request = filterInvocation.getHttpRequest(); // --- (3)Obtenir HttpServletRequest
String uri = request.getRequestURI(); // --- (4)Obtenir l'URI de la demande
if(authorizationService.isAuthorized("*", uri)) { // --- (5)Déterminer si tous les rôles sont autorisés à accéder
return ACCESS_GRANTED;
}
Object principal = authentication.getPrincipal(); // --- (6)Acquisition des informations d'identification de l'utilisateur
if (!principal.getClass().equals(AccountUserDetails.class)) { // ---(7)Déterminer si les informations d'identification acquises sont les détails de l'utilisateur du compte
return ACCESS_DENIED;
}
String roleName = ((AccountUserDetails) principal).getUser().getRoleName(); // ---(8)Obtenir le rôle de l'utilisateur
if(authorizationService.isAuthorized(roleName, uri)) { // ---(9)Déterminer si le rôle acquis est autorisé
return ACCESS_GRANTED;
}
return ACCESS_DENIED;
}
}
Numéro d'article | La description |
---|---|
(1) | Une méthode qui fait référence à la valeur de l'argument pour déterminer si le vote est nécessaire ou non |
(2) | Une méthode pour voter pour accorder l'accès(Le détail est,4-2-2.Vue d'ensemble de la fonction d'autorisation pour les activités réelles à mettre en œuvre) |
(3) | HttpServletRequest Obtenir.FilterInvocation EstHttpServletRequest OuHttpServletRequest Contient les objets associés aux filtres HTTP tels que. |
(4) | HttpServletRequest Obtenez l'URI à partir duquel vous essayez d'accéder. |
(5) | Déterminez si l'URI acquis est autorisé à accéder à tous les rôles. |
(6) | Acquérir les informations d'identification de l'utilisateur accédant |
(7) | Les informations d'identification acquises sontAccountUserDetails Déterminez s'il s'agit d'une classe.Si elles ne sont pas authentifiées, les informations d'identification sont acquises par la classe String, de sorte qu'une erreur se produira à moins que ce processus de détermination ne soit effectué. |
(8) | AccountUserDetails Obtenez le rôle de l'utilisateur à partir d'une instance de. |
(9) | Déterminez si le rôle de l'utilisateur et le chemin d'accès auquel vous essayez d'accéder sont autorisés. |
Définissez le "MyVoter" créé pour voter pour les droits d'accès.
WebSecurityConfig.java
/*réduction*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
AccountUserDetailsService userDetailsService;
@Autowired //Postscript
AccessDecisionVoter<FilterInvocation> myVoter; //Postscript
public AccessDecisionManager createAccessDecisionManager() { //Postscript
return new AffirmativeBased(Arrays.asList(myVoter)); //Postscript---(1)Affirmative Based est utilisé pour le traitement de l'approbation, et MyVoter est utilisé pour le traitement du vote.
} //Postscript
/*réduction*/
@Override
protected void configure(HttpSecurity http) throws Exception {
//Paramètres d'autorisation
http.exceptionHandling()
.accessDeniedPage("/accessDeniedPage")
.and()
.authorizeRequests()
.antMatchers("/**").authenticated() //Réparer
.accessDecisionManager(createAccessDecisionManager()); //Postscript---(2)Appliquer le processus d'autorisation pour tous les accès
/*réduction*/
Numéro d'article | La description |
---|---|
(1) | AccessDecisionManager En tant que classe d'implémentation deAffirmativeBased Postuler.AccessDecisionVoter En tant que classe d'implémentation deMyVoter Postuler.AffirmativeBased Au moment de l'instanciation deAccessDecisionVoter Plusieurs processus de vote peuvent être définis en passant des instances de. |
(2) | Traitement des autorisations pour tous les accès(MyVoter Processus de vote)Est configuré pour être exécuté. |
De nos jours, les questions de sécurité sont fréquemment couvertes par l'actualité. Par conséquent, assurez-vous de mettre en œuvre des mesures de sécurité dans votre application Web. Si vous développez une application Web à l'aide de Spring Framework, vous devez tirer parti de Spring Security pour les mesures de sécurité. Vous pouvez introduire des mesures de sécurité simples et robustes plutôt que des mesures de sécurité auto-conçues. D'un autre côté, d'un point de vue pratique, il est nécessaire d'implémenter une partie de Spring Security à étendre en raison d'exigences telles que vouloir faire référence à la DB dans le processus d'autorisation, mais je pense que cet article a montré un peu comment l'étendre. .. L'idée que «sur la base d'un produit standard, des fonctions supplémentaires nécessaires à l'entreprise et à l'exploitation sont mises en œuvre en plus» est importante dans les affaires réelles, et l'auteur lui-même peut apprendre à travers des auditions et la rédaction d'articles. J'ai fait. Dans cet article, Spring Security a implémenté la «fonction d'authentification» et la «fonction d'autorisation», qui sont les fonctions de base des mesures de sécurité. D'autres fonctionnalités de sécurité sont également fournies par Spring Security. J'ai encore beaucoup de choses que je ne sais pas, alors je veux apprendre.
Ceci est un article écrit par un nouveau venu (ma synchronisation) dans la section suivante. Si vous souhaitez introduire SSO (authentification unique), veuillez lire l'article suivant. Explique la procédure de liaison de Keycloak et d'Azure AD. Essayez la liaison d'identifiant externe à l'aide de Keycloak
Recommended Posts