Maison >Java >javaDidacticiel >Implémentation de l'authentification basée sur les jetons dans Spring Boot à l'aide du modèle Spring Security, JWT et JDBC
Lien Github pour la référence du code
Dans les applications Web modernes, l'authentification sécurisée des utilisateurs est cruciale. Traditionnellement, l'authentification basée sur la session a été largement utilisée, mais à mesure que les applications deviennent de plus en plus distribuées et évolutives, l'authentification basée sur les jetons offre plusieurs avantages.
L'authentification basée sur les jetons permet aux applications d'être sans état, ce qui signifie que le serveur n'a pas besoin de stocker de données de session, ce qui le rend bien adapté aux API RESTful évolutives.
Ce tutoriel vous guidera dans la mise en œuvre de l'authentification JWT (JSON Web Token) dans une application Spring Boot à l'aide de Spring Security avec le modèle JDBC.
JWT (JSON Web Token) est un moyen compact et sécurisé pour les URL de représenter les réclamations transférées entre deux parties. Il est couramment utilisé pour l'authentification sans état où chaque demande est authentifiée à l'aide d'un jeton signé.
Authentification apatride
Les jetons JWT sont autonomes et transportent les informations d'authentification de l'utilisateur directement dans la charge utile du jeton, ce qui réduit l'utilisation de la mémoire du serveur et augmente l'évolutivité.
Support multiplateforme
Les jetons sont faciles à utiliser dans les applications mobiles et Web car ils peuvent être stockés en toute sécurité dans le client (par exemple, dans un stockage local ou dans des cookies).
Sécurité
Chaque jeton est signé numériquement, garantissant son intégrité et permettant au serveur de le vérifier sans interroger une base de données à chaque demande.
Dans ce tutoriel, vous apprendrez à :
À la fin de ce didacticiel, vous disposerez d'un système d'authentification sécurisé et sans état qui exploite Spring Boot et JWT pour fournir un contrôle d'accès transparent et évolutif pour vos applications.
Flux API :
Exigences technologiques :
Utilisez l'outil Web Spring ou votre outil de développement (STS, Intellij ou tout autre IDE) pour créer un projet Spring Boot.
ouvrez pom.xml et ajoutez des dépendances pour Spring Security, JWT et le modèle JDBC :
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jdbc</artifactId> </dependency> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> </dependency>
Sous le dossier src/main/resources, ouvrez application.properties et ajoutez les configurations ci-dessous. J'utiliserai la base de données Postgres pour ce tutoriel.
spring.application.name= authmanager server.port= 1001 servlet.context-path= /authmanager database.username= postgres database.password= admin@123 database.driverClassName= org.postgresql.Driver database.jdbcUrl= jdbc:postgresql://localhost:5432/postgres database.maxActive= 5 database.minIdle= 5 database.poolName= Authmanager Postgres Datasource app.jwtSecret= ###############ib-Spring############### app.jwtExpirationMs= 3600000 app.jwtRefreshExpirationMs= 86400000
Définissez une structure de tableau simple pour les informations utilisateur, les rôles, le mappage des rôles utilisateur et les jetons d'actualisation :
CREATE SCHEMA IB; ------------------------------------------------------------------------- create sequence users_uniqueid_seq START 1; create table ib.users( uniqueid bigint not null default nextval('users_uniqueid_seq') PRIMARY KEY, email varchar(75), password varchar(200), username varchar(20) ); insert into ib.users(email,password,username) values ('admin@ib.com','a$VcdzH8Q.o4KEo6df.XesdOmXdXQwT5ugNQvu1Pl0390rmfOeA1bhS','admin'); #(password = 12345678) ------------------------------------------------------------------------- create sequence roles_id_seq START 1; create table ib.roles( id int not null default nextval('roles_id_seq') PRIMARY KEY, name varchar(20) ); INSERT INTO ib.roles(name) VALUES('ROLE_USER'); INSERT INTO ib.roles(name) VALUES('ROLE_MODERATOR'); INSERT INTO ib.roles(name) VALUES('ROLE_ADMIN'); ------------------------------------------------------------------------- create table ib.user_roles( user_uniqueid bigint not null, role_id int not null, primary key(user_uniqueid,role_id) ); insert into ib.user_roles (user_uniqueid,role_id) values (1,3); ------------------------------------------------------------------------- create sequence refresh_tokens_id_seq START 1; create table ib.refresh_tokens( id bigint not null default nextval('refresh_tokens_id_seq') PRIMARY KEY, uniqueid bigint, token varchar(500) not null, expiryDate TIMESTAMP WITH TIME ZONE not null ); -------------------------------------------------------------------------
Définissons les modèles suivants.
Dans le package models, créez ci-dessous 4 fichiers :
model/ERole.java
package com.security.authmanager.model; public enum ERole { ROLE_USER, ROLE_MODERATOR, ROLE_ADMIN }
modèle/Rôle.java
package com.security.authmanager.model; public class Role { private Integer id; private ERole name; public Role() { } public Role(ERole name) { this.name = name; } //generate getters and setters }
modèle/Utilisateur.java
package com.security.authmanager.model; import java.util.HashSet; import java.util.Set; public class User { private Long id; private String username; private String email; private String password; private Set<Role> roles = new HashSet<>(); public User() { } public User(String username, String email, String password) { this.username = username; this.email = email; this.password = password; } //generate getters and setters }
modèle/RefreshToken.java
package com.security.authmanager.model; import java.util.HashSet; import java.util.Set; public class User { private Long id; private String username; private String email; private String password; private Set<Role> roles = new HashSet<>(); public User() { } public User(String username, String email, String password) { this.username = username; this.email = email; this.password = password; } //generate getters and setters }
La classe CustomUserDetailsRepository est un Spring @Repository qui gère les opérations de base de données personnalisées liées aux entités User et Role. Il utilise JdbcTemplate pour exécuter des requêtes SQL pour des tâches telles que récupérer des utilisateurs, vérifier si un utilisateur existe par nom d'utilisateur ou e-mail, créer de nouveaux utilisateurs et récupérer des rôles.
package com.security.authmanager.repository; import com.security.authmanager.common.QueryConstants; import com.security.authmanager.model.ERole; import com.security.authmanager.model.Role; import com.security.authmanager.model.User; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.Statement; import java.util.HashSet; import java.util.Objects; import java.util.Set; @Repository public class CustomUserDetailsRepository { private static final Logger logger = LoggerFactory.getLogger(CustomUserDetailsRepository.class); @Autowired private JdbcTemplate jdbcTemplate; public User fetchUserByUserName(String userName){ try{ return jdbcTemplate.query((conn) ->{ final PreparedStatement ps = conn.prepareStatement(QueryConstants.FETCH_USER); ps.setString(1, userName.toUpperCase()); return ps; },rs->{ User user = null; Set<Role> roles = new HashSet<>(); while (rs.next()) { if (user == null) { user = new User(); user.setEmail(rs.getString("email")); user.setId(rs.getLong("uniqueid")); user.setPassword(rs.getString("password")); user.setUsername(rs.getString("username")); } Role role = new Role(); role.setId(rs.getInt("id")); role.setName(ERole.valueOf(rs.getString("name"))); roles.add(role); } if (user != null) { user.setRoles(roles); } return user; }); }catch(Exception e){ logger.error("Exception in fetchUserByUserName()",e); throw new RuntimeException(e); } } public boolean existsByUsername(String userName) { try{ return jdbcTemplate.query((conn) -> { final PreparedStatement ps = conn.prepareStatement(QueryConstants.CHECK_USER_BY_USERNAME); ps.setString(1, userName.toUpperCase()); return ps; }, (rs,rownum) -> rs.getInt("count")).get(0)>0; }catch(Exception e){ logger.error("Exception in existsByUsername()",e); throw new RuntimeException(e); } } public boolean existsByEmail(String email) { try{ return jdbcTemplate.query((conn) -> { final PreparedStatement ps = conn.prepareStatement(QueryConstants.CHECK_USER_BY_EMAIL); ps.setString(1, email.toUpperCase()); return ps; }, (rs,rownum) -> rs.getInt("count")).get(0)>0; }catch(Exception e){ logger.error("Exception in existsByEmail()",e); throw new RuntimeException(e); } } public Role findRoleByName(ERole eRole) { try{ return jdbcTemplate.query((conn) -> { final PreparedStatement ps = conn.prepareStatement(QueryConstants.FETCH_ROLE_BY_NAME); ps.setString(1, String.valueOf(eRole)); return ps; }, rs -> { Role role=null; while(rs.next()){ role = new Role(); role.setName(ERole.valueOf(rs.getString("name"))); role.setId(rs.getInt("id")); } return role; }); }catch(Exception e){ logger.error("Exception in findRoleByName()",e); throw new RuntimeException(e); } } public void createUser(User user) { try(Connection conn = Objects.requireNonNull(jdbcTemplate.getDataSource()).getConnection()){ try (PreparedStatement userStatement = conn.prepareStatement(QueryConstants.INSERT_TO_USERS,Statement.RETURN_GENERATED_KEYS)) { userStatement.setString(1, user.getEmail().toUpperCase()); userStatement.setString(2, user.getPassword()); userStatement.setString(3, user.getUsername().toUpperCase()); userStatement.executeUpdate(); // Retrieve generated userId try (ResultSet generatedKeys = userStatement.getGeneratedKeys()) { if (generatedKeys.next()) { Long userId = generatedKeys.getLong(1); // Assuming userId is of type VARCHAR logger.info("gen userid {}",userId.toString()); user.setId(userId); } } } if (user.getRoles() != null && !user.getRoles().isEmpty()) { try (PreparedStatement userRoleStatement = conn.prepareStatement(QueryConstants.INSERT_TO_USER_ROLES)) { for (Role role : user.getRoles()) { userRoleStatement.setLong(1, user.getId()); userRoleStatement.setLong(2, role.getId()); userRoleStatement.executeUpdate(); } } } }catch(Exception e){ logger.error("Exception in existsByEmail()",e); throw new RuntimeException(e); } } }
Ce référentiel effectue des opérations CRUD personnalisées basées sur SQL pour gérer les données d'utilisateur et de rôle dans la base de données.
Fonctions clés :
La classe RefreshTokenRepository est un Spring @Repository qui gère les opérations de base de données liées à l'entité RefreshToken. Il utilise le JdbcTemplate de Spring pour interagir avec la base de données via des requêtes SQL brutes, encapsulant la logique d'enregistrement, de suppression et de récupération des jetons d'actualisation.
package com.security.authmanager.repository; import com.security.authmanager.common.QueryConstants; import com.security.authmanager.model.ERole; import com.security.authmanager.model.RefreshToken; import com.security.authmanager.model.Role; import com.security.authmanager.model.User; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import java.sql.PreparedStatement; import java.sql.Timestamp; import java.util.Optional; @Repository public class RefreshTokenRepository { private static final Logger logger = LoggerFactory.getLogger(RefreshTokenRepository.class); @Autowired private JdbcTemplate jdbcTemplate; public void deleteRefreshToken(RefreshToken refreshToken) { try{ jdbcTemplate.update(QueryConstants.DELETE_REFRESH_TOKEN,(final PreparedStatement ps) ->{ ps.setString(1,refreshToken.getToken()); }); }catch (Exception e){ logger.error("Exception in deleteRefreshToken()",e); throw new RuntimeException(e); } } public int deleteRefreshTokenByUser(User user) { return 0; } public RefreshToken saveRefreshToken(RefreshToken refreshToken) { try{ jdbcTemplate.update(QueryConstants.SAVE_REFRESH_TOKEN,(final PreparedStatement ps) ->{ ps.setLong(1,refreshToken.getUser().getId()); ps.setString(2,refreshToken.getToken()); ps.setTimestamp(3, Timestamp.from(refreshToken.getExpiryDate())); }); }catch (Exception e){ logger.error("Exception in saveRefreshToken()",e); throw new RuntimeException(e); } return refreshToken; } public Optional<RefreshToken> findByToken(String token) { RefreshToken refreshToken = new RefreshToken(); try{ return Optional.ofNullable(jdbcTemplate.query((conn) -> { final PreparedStatement ps = conn.prepareStatement(QueryConstants.FIND_BY_TOKEN); ps.setString(1, token); return ps; }, rs -> { User user = new User(); while (rs.next()) { refreshToken.setId(rs.getLong("id")); refreshToken.setToken(rs.getString("token")); refreshToken.setExpiryDate(rs.getTimestamp("expiryDate").toInstant()); user.setId(rs.getLong("uniqueid")); user.setEmail(rs.getString("email")); user.setUsername(rs.getString("username")); refreshToken.setUser(user); } return refreshToken; })); }catch(Exception e){ logger.error("Exception in findByToken()",e); throw new RuntimeException(e); } } }
Ce référentiel interagit directement avec la base de données pour les opérations CRUD sur les entités RefreshToken.
Fonctions clés :
WebSecurityConfig configure Spring Security pour utiliser l'authentification par jeton basée sur JWT.
Il définit des beans pour divers composants nécessaires à l'authentification comme AuthTokenFilter (pour gérer les jetons JWT), DaoAuthenticationProvider (pour récupérer les détails de l'utilisateur et valider les mots de passe) et BCryptPasswordEncoder (pour hacher et comparer les mots de passe).
SecurityFilterChain configure la façon dont les requêtes HTTP entrantes sont sécurisées :
- Permet d'accéder à certaines routes publiques (/auth/**, /test/**).
- Protège toutes les autres routes, nécessitant une authentification.
- Désactive la gestion des sessions (rendant le système apatride).
- Configure un filtre pour intercepter et traiter les jetons JWT pour
authentification de l'utilisateur.
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jdbc</artifactId> </dependency> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> </dependency>
Cette classe est principalement utilisée dans Spring Security pour représenter l'utilisateur actuellement authentifié.
Lorsqu'un utilisateur tente de se connecter :
UserDetailsImpl est un pont entre votre entité utilisateur et les mécanismes internes d'authentification et d'autorisation de Spring Security.
spring.application.name= authmanager server.port= 1001 servlet.context-path= /authmanager database.username= postgres database.password= admin@123 database.driverClassName= org.postgresql.Driver database.jdbcUrl= jdbc:postgresql://localhost:5432/postgres database.maxActive= 5 database.minIdle= 5 database.poolName= Authmanager Postgres Datasource app.jwtSecret= ###############ib-Spring############### app.jwtExpirationMs= 3600000 app.jwtRefreshExpirationMs= 86400000
Ci-dessous les requêtes : (QueryConstants.java)
CREATE SCHEMA IB; ------------------------------------------------------------------------- create sequence users_uniqueid_seq START 1; create table ib.users( uniqueid bigint not null default nextval('users_uniqueid_seq') PRIMARY KEY, email varchar(75), password varchar(200), username varchar(20) ); insert into ib.users(email,password,username) values ('admin@ib.com','a$VcdzH8Q.o4KEo6df.XesdOmXdXQwT5ugNQvu1Pl0390rmfOeA1bhS','admin'); #(password = 12345678) ------------------------------------------------------------------------- create sequence roles_id_seq START 1; create table ib.roles( id int not null default nextval('roles_id_seq') PRIMARY KEY, name varchar(20) ); INSERT INTO ib.roles(name) VALUES('ROLE_USER'); INSERT INTO ib.roles(name) VALUES('ROLE_MODERATOR'); INSERT INTO ib.roles(name) VALUES('ROLE_ADMIN'); ------------------------------------------------------------------------- create table ib.user_roles( user_uniqueid bigint not null, role_id int not null, primary key(user_uniqueid,role_id) ); insert into ib.user_roles (user_uniqueid,role_id) values (1,3); ------------------------------------------------------------------------- create sequence refresh_tokens_id_seq START 1; create table ib.refresh_tokens( id bigint not null default nextval('refresh_tokens_id_seq') PRIMARY KEY, uniqueid bigint, token varchar(500) not null, expiryDate TIMESTAMP WITH TIME ZONE not null ); -------------------------------------------------------------------------
La classe UserDetailsServiceImpl agit comme un pont entre la base de données de votre application et le processus d'authentification de Spring Security. Il récupère les détails de l'utilisateur de la base de données à l'aide de CustomUserDetailsRepository, convertit l'objet User en UserDetailsImpl (un format compatible avec Spring Security) et gère les cas où un utilisateur n'est pas trouvé en lançant une exception. Ce service permet à Spring Security d'authentifier les utilisateurs et de gérer les autorisations en fonction des rôles et des autorisations de l'utilisateur.
package com.security.authmanager.model; public enum ERole { ROLE_USER, ROLE_MODERATOR, ROLE_ADMIN }
La classe AuthTokenFilter étend OncePerRequestFilter de Spring, ce qui en fait un filtre qui traite chaque requête HTTP une fois dans une chaîne de requêtes. Son rôle principal est d'extraire et de valider un JWT (JSON Web Token) de la requête et de définir l'authentification de l'utilisateur dans SecurityContext de Spring Security si le jeton est valide.
package com.security.authmanager.model; public class Role { private Integer id; private ERole name; public Role() { } public Role(ERole name) { this.name = name; } //generate getters and setters }
Chaque fois qu'une demande est faite :
Ce filtre garantit que toutes les requêtes contenant des jetons JWT valides sont authentifiées automatiquement, sans que l'utilisateur ait besoin de fournir des informations d'identification (comme un nom d'utilisateur/un mot de passe) dans les requêtes ultérieures après la connexion.
La classe RefreshTokenService fournit des services liés à la gestion des jetons d'actualisation dans un système d'authentification basé sur des jetons. Les jetons d'actualisation sont utilisés pour obtenir de nouveaux jetons JWT après l'expiration du JWT initial sans que l'utilisateur ne se ré-authentifie.
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jdbc</artifactId> </dependency> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> </dependency>
RefreshTokenService gère la création, la validation et la suppression des jetons d'actualisation. Il utilise des référentiels pour enregistrer et récupérer les jetons et les utilisateurs de la base de données.
Ce service est un élément essentiel d'un système d'authentification dans lequel des jetons d'actualisation sont utilisés pour maintenir les utilisateurs connectés sans leur demander de fournir à nouveau leurs informations d'identification après l'expiration du JWT.
La classe JwtUtils est une classe utilitaire qui gère la création, l'analyse et la validation de JWT (JSON Web Token) à des fins d'authentification dans une application Spring Boot. Il utilise la bibliothèque jjwt pour travailler avec les JWT.
spring.application.name= authmanager server.port= 1001 servlet.context-path= /authmanager database.username= postgres database.password= admin@123 database.driverClassName= org.postgresql.Driver database.jdbcUrl= jdbc:postgresql://localhost:5432/postgres database.maxActive= 5 database.minIdle= 5 database.poolName= Authmanager Postgres Datasource app.jwtSecret= ###############ib-Spring############### app.jwtExpirationMs= 3600000 app.jwtRefreshExpirationMs= 86400000
La classe JwtUtils est responsable de la génération, de l'analyse et de la validation des jetons JWT. Il signe en toute sécurité les jetons à l'aide d'une clé secrète (HMAC-SHA256) et garantit que les jetons ne peuvent être lus ou vérifiés que par les parties possédant la clé secrète correcte.
La classe extrait également le nom d'utilisateur du jeton et vérifie si le jeton est valide avant d'accorder l'accès à l'utilisateur. Cet utilitaire est essentiel pour maintenir une authentification sécurisée basée sur des jetons dans votre application.
La classe AuthEntryPointJwt implémente l'interface AuthenticationEntryPoint de Spring Security. Il gère ce qui se passe lorsqu'une demande non autorisée est effectuée, généralement lorsqu'un utilisateur tente d'accéder à une ressource protégée sans authentification valide (par exemple, pas de JWT ou un JWT non valide).
CREATE SCHEMA IB; ------------------------------------------------------------------------- create sequence users_uniqueid_seq START 1; create table ib.users( uniqueid bigint not null default nextval('users_uniqueid_seq') PRIMARY KEY, email varchar(75), password varchar(200), username varchar(20) ); insert into ib.users(email,password,username) values ('admin@ib.com','a$VcdzH8Q.o4KEo6df.XesdOmXdXQwT5ugNQvu1Pl0390rmfOeA1bhS','admin'); #(password = 12345678) ------------------------------------------------------------------------- create sequence roles_id_seq START 1; create table ib.roles( id int not null default nextval('roles_id_seq') PRIMARY KEY, name varchar(20) ); INSERT INTO ib.roles(name) VALUES('ROLE_USER'); INSERT INTO ib.roles(name) VALUES('ROLE_MODERATOR'); INSERT INTO ib.roles(name) VALUES('ROLE_ADMIN'); ------------------------------------------------------------------------- create table ib.user_roles( user_uniqueid bigint not null, role_id int not null, primary key(user_uniqueid,role_id) ); insert into ib.user_roles (user_uniqueid,role_id) values (1,3); ------------------------------------------------------------------------- create sequence refresh_tokens_id_seq START 1; create table ib.refresh_tokens( id bigint not null default nextval('refresh_tokens_id_seq') PRIMARY KEY, uniqueid bigint, token varchar(500) not null, expiryDate TIMESTAMP WITH TIME ZONE not null ); -------------------------------------------------------------------------
La classe AuthEntryPointJwt est un point d'entrée personnalisé qui intercepte les tentatives d'accès non autorisées et renvoie une réponse JSON structurée avec un code d'erreur 401, un message d'erreur et des détails sur la demande.
Il enregistre l'erreur et fournit une réponse claire et conviviale aux clients lorsque l'authentification échoue.
Vous trouverez ci-dessous les charges utiles de nos RestAPI :
1. Demandes :
- LoginRequest.java :
package com.security.authmanager.model; public enum ERole { ROLE_USER, ROLE_MODERATOR, ROLE_ADMIN }
- SignupRequest.java :
package com.security.authmanager.model; public class Role { private Integer id; private ERole name; public Role() { } public Role(ERole name) { this.name = name; } //generate getters and setters }
- TokenRefreshRequest.java :
package com.security.authmanager.model; import java.util.HashSet; import java.util.Set; public class User { private Long id; private String username; private String email; private String password; private Set<Role> roles = new HashSet<>(); public User() { } public User(String username, String email, String password) { this.username = username; this.email = email; this.password = password; } //generate getters and setters }
2. Réponses :
- JwtResponse.java
package com.security.authmanager.model; import java.util.HashSet; import java.util.Set; public class User { private Long id; private String username; private String email; private String password; private Set<Role> roles = new HashSet<>(); public User() { } public User(String username, String email, String password) { this.username = username; this.email = email; this.password = password; } //generate getters and setters }
- MessageResponse.java
package com.security.authmanager.repository; import com.security.authmanager.common.QueryConstants; import com.security.authmanager.model.ERole; import com.security.authmanager.model.Role; import com.security.authmanager.model.User; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.Statement; import java.util.HashSet; import java.util.Objects; import java.util.Set; @Repository public class CustomUserDetailsRepository { private static final Logger logger = LoggerFactory.getLogger(CustomUserDetailsRepository.class); @Autowired private JdbcTemplate jdbcTemplate; public User fetchUserByUserName(String userName){ try{ return jdbcTemplate.query((conn) ->{ final PreparedStatement ps = conn.prepareStatement(QueryConstants.FETCH_USER); ps.setString(1, userName.toUpperCase()); return ps; },rs->{ User user = null; Set<Role> roles = new HashSet<>(); while (rs.next()) { if (user == null) { user = new User(); user.setEmail(rs.getString("email")); user.setId(rs.getLong("uniqueid")); user.setPassword(rs.getString("password")); user.setUsername(rs.getString("username")); } Role role = new Role(); role.setId(rs.getInt("id")); role.setName(ERole.valueOf(rs.getString("name"))); roles.add(role); } if (user != null) { user.setRoles(roles); } return user; }); }catch(Exception e){ logger.error("Exception in fetchUserByUserName()",e); throw new RuntimeException(e); } } public boolean existsByUsername(String userName) { try{ return jdbcTemplate.query((conn) -> { final PreparedStatement ps = conn.prepareStatement(QueryConstants.CHECK_USER_BY_USERNAME); ps.setString(1, userName.toUpperCase()); return ps; }, (rs,rownum) -> rs.getInt("count")).get(0)>0; }catch(Exception e){ logger.error("Exception in existsByUsername()",e); throw new RuntimeException(e); } } public boolean existsByEmail(String email) { try{ return jdbcTemplate.query((conn) -> { final PreparedStatement ps = conn.prepareStatement(QueryConstants.CHECK_USER_BY_EMAIL); ps.setString(1, email.toUpperCase()); return ps; }, (rs,rownum) -> rs.getInt("count")).get(0)>0; }catch(Exception e){ logger.error("Exception in existsByEmail()",e); throw new RuntimeException(e); } } public Role findRoleByName(ERole eRole) { try{ return jdbcTemplate.query((conn) -> { final PreparedStatement ps = conn.prepareStatement(QueryConstants.FETCH_ROLE_BY_NAME); ps.setString(1, String.valueOf(eRole)); return ps; }, rs -> { Role role=null; while(rs.next()){ role = new Role(); role.setName(ERole.valueOf(rs.getString("name"))); role.setId(rs.getInt("id")); } return role; }); }catch(Exception e){ logger.error("Exception in findRoleByName()",e); throw new RuntimeException(e); } } public void createUser(User user) { try(Connection conn = Objects.requireNonNull(jdbcTemplate.getDataSource()).getConnection()){ try (PreparedStatement userStatement = conn.prepareStatement(QueryConstants.INSERT_TO_USERS,Statement.RETURN_GENERATED_KEYS)) { userStatement.setString(1, user.getEmail().toUpperCase()); userStatement.setString(2, user.getPassword()); userStatement.setString(3, user.getUsername().toUpperCase()); userStatement.executeUpdate(); // Retrieve generated userId try (ResultSet generatedKeys = userStatement.getGeneratedKeys()) { if (generatedKeys.next()) { Long userId = generatedKeys.getLong(1); // Assuming userId is of type VARCHAR logger.info("gen userid {}",userId.toString()); user.setId(userId); } } } if (user.getRoles() != null && !user.getRoles().isEmpty()) { try (PreparedStatement userRoleStatement = conn.prepareStatement(QueryConstants.INSERT_TO_USER_ROLES)) { for (Role role : user.getRoles()) { userRoleStatement.setLong(1, user.getId()); userRoleStatement.setLong(2, role.getId()); userRoleStatement.executeUpdate(); } } } }catch(Exception e){ logger.error("Exception in existsByEmail()",e); throw new RuntimeException(e); } } }
- TokenRefreshResponse.java
package com.security.authmanager.repository; import com.security.authmanager.common.QueryConstants; import com.security.authmanager.model.ERole; import com.security.authmanager.model.RefreshToken; import com.security.authmanager.model.Role; import com.security.authmanager.model.User; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import java.sql.PreparedStatement; import java.sql.Timestamp; import java.util.Optional; @Repository public class RefreshTokenRepository { private static final Logger logger = LoggerFactory.getLogger(RefreshTokenRepository.class); @Autowired private JdbcTemplate jdbcTemplate; public void deleteRefreshToken(RefreshToken refreshToken) { try{ jdbcTemplate.update(QueryConstants.DELETE_REFRESH_TOKEN,(final PreparedStatement ps) ->{ ps.setString(1,refreshToken.getToken()); }); }catch (Exception e){ logger.error("Exception in deleteRefreshToken()",e); throw new RuntimeException(e); } } public int deleteRefreshTokenByUser(User user) { return 0; } public RefreshToken saveRefreshToken(RefreshToken refreshToken) { try{ jdbcTemplate.update(QueryConstants.SAVE_REFRESH_TOKEN,(final PreparedStatement ps) ->{ ps.setLong(1,refreshToken.getUser().getId()); ps.setString(2,refreshToken.getToken()); ps.setTimestamp(3, Timestamp.from(refreshToken.getExpiryDate())); }); }catch (Exception e){ logger.error("Exception in saveRefreshToken()",e); throw new RuntimeException(e); } return refreshToken; } public Optional<RefreshToken> findByToken(String token) { RefreshToken refreshToken = new RefreshToken(); try{ return Optional.ofNullable(jdbcTemplate.query((conn) -> { final PreparedStatement ps = conn.prepareStatement(QueryConstants.FIND_BY_TOKEN); ps.setString(1, token); return ps; }, rs -> { User user = new User(); while (rs.next()) { refreshToken.setId(rs.getLong("id")); refreshToken.setToken(rs.getString("token")); refreshToken.setExpiryDate(rs.getTimestamp("expiryDate").toInstant()); user.setId(rs.getLong("uniqueid")); user.setEmail(rs.getString("email")); user.setUsername(rs.getString("username")); refreshToken.setUser(user); } return refreshToken; })); }catch(Exception e){ logger.error("Exception in findByToken()",e); throw new RuntimeException(e); } } }
- AuthController.java
La classe AuthController est un Spring @RestController responsable de la gestion des points de terminaison liés à l'authentification dans l'application. Il fournit des points de terminaison pour les opérations de connexion, d'enregistrement et d'actualisation des jetons des utilisateurs.
Fonctions clés :
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jdbc</artifactId> </dependency> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> </dependency>
- TestController.java
La classe TestController est un Spring @RestController qui fournit plusieurs points de terminaison pour tester le contrôle d'accès en fonction des rôles d'utilisateur. Il montre comment utiliser l'autorisation basée sur les rôles de Spring Security pour restreindre l'accès à certaines parties de l'application.
Fonctions clés :
spring.application.name= authmanager server.port= 1001 servlet.context-path= /authmanager database.username= postgres database.password= admin@123 database.driverClassName= org.postgresql.Driver database.jdbcUrl= jdbc:postgresql://localhost:5432/postgres database.maxActive= 5 database.minIdle= 5 database.poolName= Authmanager Postgres Datasource app.jwtSecret= ###############ib-Spring############### app.jwtExpirationMs= 3600000 app.jwtRefreshExpirationMs= 86400000
1. Inscrivez-vous en tant que mod et utilisateur. (Connexion)
2. Connectez-vous pour obtenir un jeton d'accès.
3. Obtenez l'API de jeton d'actualisation.
4. Tester l'accès de l'utilisateur en transmettant le jeton d'accès.
5. Tester l'accès au mod en passant le jeton d'accès.
6. Tester l'accès administrateur en passant le même jeton d'accès.
Non autorisé car cet utilisateur n'a pas d'accès administrateur (l'utilisateur n'a que des rôles de mod et d'utilisateur)
Bon apprentissage ! On se reverra.?
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!