IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Spring Java Discussion :

Problème avec le jeton d'actualisation après une longue inactivité dans Angular


Sujet :

Spring Java

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé
    Homme Profil pro
    growth hacker
    Inscrit en
    Novembre 2018
    Messages
    187
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Tunisie

    Informations professionnelles :
    Activité : growth hacker
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Novembre 2018
    Messages : 187
    Par défaut Problème avec le jeton d'actualisation après une longue inactivité dans Angular
    Je travaille sur une application Angular qui utilise l'authentification avec accessToken et refreshToken. Mon objectif est de garder l'utilisateur connecté même après une longue période d'inactivité. Voici le comportement attendu par rapport au comportement réel*:

    Comportement attendu*: lorsque l'utilisateur revient après une longue période d'inactivité, le refreshToken doit être utilisé pour obtenir un nouveau accessToken sans nécessiter de reconnexion manuelle.
    Comportement réel*: le refreshToken ne fonctionne que la première fois. Après une longue période d'inactivité (par exemple, plusieurs heures ou jours), il cesse de s'actualiser automatiquement, forçant l'utilisateur à se déconnecter manuellement et à se reconnecter.
    Je soupçonne qu'il y a un problème avec la logique d'actualisation automatique, qui cesse de fonctionner correctement après une certaine durée. Voici le code correspondant*:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
     
     
     
              interface CustomJwtPayload {
                exp: number; // Timestamp de l'expiration
                userId?: number; // ID utilisateur, optionnel
              }
     
              export class AuthService {
     
                private baseUrl = 'http://localhost:8080/api';
                private readonly ACCESS_TOKEN_EXPIRATION = 1000 * 60 * 15; // 15 minutes
                private readonly REFRESH_TOKEN_EXPIRATION = 1000 * 60 * 60 * 24 * 7; // 7 jours
     
                constructor(
                  private http: HttpClient,
                  @Inject(PLATFORM_ID) private platformId: Object,
                  private ngZone: NgZone
                ) {
                  this.setupAutoRefresh();
                }
     
                private getToken(): string | null {
                  if (isPlatformBrowser(this.platformId)) {
                    return localStorage.getItem('accessToken'); // Utilisez localStorage
                  }
                  return null;
                }
     
                private getRefreshToken(): string | null {
                  return localStorage.getItem('refreshToken'); // Utilisez localStorage
                }
     
                private storeNewAccessToken(token: string) {
                  if (isPlatformBrowser(this.platformId)) {
                    localStorage.setItem('accessToken', token); // Utilisez localStorage
                  }
                }
     
                public refreshToken(refreshToken: string): Observable<any> {
                  return this.http.post(`${this.baseUrl}/refresh-token`, { refreshToken }, {
                    headers: this.getHeaders()
                  }).pipe(
                    catchError(error => {
                      console.error('Token refresh failed', error);
                      return EMPTY; // Retourne un Observable vide pour éviter tout blocage
                    })
                  );
                }
     
                private setupAutoRefresh() {
                  if (isPlatformBrowser(this.platformId)) {
                    this.ngZone.runOutsideAngular(() => {
                      setInterval(() => {
                        if (this.isAccessTokenCloseToExpiration()) {
                          const refreshToken = this.getRefreshToken(); // Récupère le token de rafraîchissement
                          if (refreshToken) {
                            this.refreshToken(refreshToken).subscribe(newAccessToken => {
                              this.storeNewAccessToken(newAccessToken.token); // Stocke le nouveau token d'accès
                            });
                          }
                        }
                      }, 13 * 60 * 1000); // Appelle toutes les 13 minutes
                    });
                  }
                }
     
                private isAccessTokenCloseToExpiration(): boolean {
                  const decodedToken = this.decodeToken();
                  if (!decodedToken) return false;
                  const expirationTime = decodedToken.exp * 1000;
                  const currentTime = new Date().getTime();
                  return expirationTime - currentTime < 2 * 60 * 1000; // Moins de 2 minutes restantes
                }
     
                private decodeToken(): CustomJwtPayload | null {
                  const token = this.getToken();
                  if (!token) return null;
                  try {
                    return jwtDecode<CustomJwtPayload>(token);
                  } catch (error) {
                    console.error('Token decoding failed', error);
                    return null;
                  }
                }
     
     
              SPRing boot
     
     
                private final long ACCESS_TOKEN_EXPIRATION = 1000 * 60 * 15;
                private final long REFRESH_TOKEN_EXPIRATION = 1000 * 60 * 60 * 24 * 7;
     
                public String extractUsername(String token) {
                  return extractClaim(token, Claims:: getSubject);
                }
     
                public Date extractExpiration(String token) {
                  return extractClaim(token, Claims:: getExpiration);
                }
     
                public<T> T extractClaim(String token, Function <Claims, T > claimsResolver) {
                          final Claims claims = extractAllClaims(token);
                return claimsResolver.apply(claims);
              }
     
                      private Claims extractAllClaims(String token) {
                return Jwts.parserBuilder()
                  .setSigningKey(SECRET_KEY)
                  .build()
                  .parseClaimsJws(token)
                  .getBody();
              }
     
                      private Boolean isTokenExpired(String token) {
                return extractExpiration(token).before(new Date());
              }
     
                      // Génération d'un Token d'Accès
                      public String generateAccessToken(String username, Long userId) {
                Map < String, Object > claims = new HashMap<>();
                claims.put("userId", userId); // Ajoutez l'ID utilisateur aux revendications
                return createToken(claims, username, ACCESS_TOKEN_EXPIRATION, SECRET_KEY);
              }
     
                      // Génération d'un Refresh Token
                      public String generateRefreshToken(String username, Long userId) {
                Map < String, Object > claims = new HashMap<>();
                claims.put("userId", userId); // Ajoutez l'ID utilisateur aux revendications
                return createToken(claims, username, REFRESH_TOKEN_EXPIRATION, REFRESH_SECRET_KEY);
              }
     
                      // Extraction de l'ID utilisateur à partir du token
                      public Long extractUserId(String token) {
                          final Claims claims = extractAllClaims(token);
                return (Long) claims.get("userId");
              }
     
                      // Création d'un JWT avec expiration personnalisée et clé donnée
                      private String createToken(Map < String, Object > claims, String subject, long expirationTime, Key key) {
                return Jwts.builder()
                  .setClaims(claims)
                  .setSubject(subject)
                  .setIssuedAt(new Date(System.currentTimeMillis()))
                  .setExpiration(new Date(System.currentTimeMillis() + expirationTime))
                  .signWith(key) // Utilisez la clé appropriée
                  .compact();
              }
     
                      // Validation du Token d'Accès
                      public Boolean validateAccessToken(String token, String username) {
                          final String extractedUsername = extractUsername(token);
                return (extractedUsername.equals(username) && !isTokenExpired(token));
              }
     
                      // Validation du Refresh Token
                      public Boolean validateRefreshToken(String token) {
                try {
                  Jwts.parserBuilder()
                    .setSigningKey(REFRESH_SECRET_KEY)
                    .build()
                    .parseClaimsJws(token);
                  return true;
                } catch (Exception e) {
                  return false;
                }
              }
     
                      // Vérification si le Token d'Accès est expiré
                      public Boolean isAccessTokenExpired(String token) {
                return isTokenExpired(token);
              }
     
     
              @Override
              protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
                      throws ServletException, IOException {
     
                  final String authorizationHeader = request.getHeader("Authorization");
                  final String refreshTokenHeader = request.getHeader("Refresh-Token");
     
                  String jwt = null;
                  String username = null;
     
                if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
                  jwt = authorizationHeader.substring(7);
                  try {
                    username = jwtUtil.extractUsername(jwt);
                  } catch (ExpiredJwtException e) {
                    // Si le token d'accès est expiré, essayons de générer un nouveau token à partir du refresh token
                    if (refreshTokenHeader != null && jwtUtil.validateRefreshToken(refreshTokenHeader)) {
                              // Extraire le nom d'utilisateur à partir du refresh token
                              String refreshTokenUsername = jwtUtil.extractUsername(refreshTokenHeader);
                              String newAccessToken = jwtTokenRefreshService.refreshAccessToken(refreshTokenHeader, refreshTokenUsername);
                      response.setHeader("New-Access-Token", newAccessToken); // Retourne le nouveau token d'accès dans les en-têtes de réponse
                    } else {
                      response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                      response.getWriter().write("Refresh token is invalid or not provided.");
                      return;
                    }
                  } catch (Exception e) {
                    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                    response.getWriter().write("JWT token parsing failed.");
                    return;
                  }
     
                  if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                    try {
                              UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
                      if (jwtUtil.validateAccessToken(jwt, username)) {
                                  UsernamePasswordAuthenticationToken authenticationToken =
                          new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                        authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
                      } else {
                        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                        response.getWriter().write("Invalid JWT token.");
                        return;
                      }
                    } catch (Exception e) {
                      response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                      response.getWriter().write("Exception in setting authentication: " + e.getMessage());
                      return;
                    }
                  }
                }
     
                chain.doFilter(request, response);
              }

  2. #2
    Membre très actif

    Profil pro
    Inscrit en
    Janvier 2009
    Messages
    486
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2009
    Messages : 486
    Billets dans le blog
    5
    Par défaut
    La base pour l'ensemble:
    https://www.developpez.net/forums/d2.../#post11875369

    Pour le côté angular, le mieux est d'utiliser un HttpInterceptor (https://v17.angular.io/api/common/http/HttpInterceptor)

    Tu interceptes chaque requête avant de l'envoyer sur le serveur, et tu ajoute les options d'authentification (comme le JWT par exemple).

    Le HttpInterceptor permet aussi d'intercepter la requête de réponse, comme c'est le cas des erreurs:
    https://bitbucket.org/philippegibaul...eptor.error.ts

Discussions similaires

  1. [WM23] Relancer l'application après une longue inactivité
    Par Invité dans le forum Windev Mobile
    Réponses: 11
    Dernier message: 02/05/2018, 10h47
  2. Réponses: 5
    Dernier message: 26/12/2007, 10h51
  3. Actualisation d'une image seule dans ma page
    Par jack_1981 dans le forum Général JavaScript
    Réponses: 1
    Dernier message: 04/08/2006, 14h58

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo