Wie kann ich Token in Spring Security aktualisieren
Diese Linie:
Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
Löst einen Fehler wie diesen aus, wenn mein jwt-Token abläuft:
JWT abgelaufen am 2020-05-13T07:50:39Z. Aktuelle Zeit: 2020-05-16T21:29:41Z.
Genauer gesagt ist es diese Funktion, die die Ausnahme "ExpiredJwtException" auslöst:

Wie gehe ich mit diesen Ausnahmen um? Soll ich sie abfangen und eine Fehlermeldung an den Client zurücksenden und sie zwingen, sich erneut anzumelden?
Wie kann ich eine Aktualisierungstoken-Funktion implementieren? Ich verwende Spring und MySQL im Backend und vuejs im Frontend.
Ich generiere das anfängliche Token wie folgt:
@Override
public JSONObject login(AuthenticationRequest authreq) {
JSONObject json = new JSONObject();
try {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(authreq.getUsername(), authreq.getPassword()));
UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();
List<String> roles = userDetails.getAuthorities().stream().map(item -> item.getAuthority())
.collect(Collectors.toList());
if (userDetails != null) {
final String jwt = jwtTokenUtil.generateToken(userDetails);
JwtResponse jwtres = new JwtResponse(jwt, userDetails.getId(), userDetails.getUsername(),
userDetails.getEmail(), roles, jwtTokenUtil.extractExpiration(jwt).toString());
return json.put("jwtresponse", jwtres);
}
} catch (BadCredentialsException ex) {
json.put("status", "badcredentials");
} catch (LockedException ex) {
json.put("status", "LockedException");
} catch (DisabledException ex) {
json.put("status", "DisabledException");
}
return json;
}
Und dann in der Klasse JwtUtil:
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
return createToken(claims, userDetails.getUsername());
}
private String createToken(Map<String, Object> claims, String subject) {
return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + EXPIRESIN))
.signWith(SignatureAlgorithm.HS256, SECRET_KEY).compact();
}
Für weitere Informationen, hier ist meine Funktion doFilterInternal, die jede Anfrage filtert:
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException, ExpiredJwtException, MalformedJwtException {
try {
final String authorizationHeader = request.getHeader("Authorization");
String username = null;
String jwt = null;
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
jwt = authorizationHeader.substring(7);
username = jwtUtil.extractUsername(jwt);
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userService.loadUserByUsername(username);
boolean correct = jwtUtil.validateToken(jwt, userDetails);
if (correct) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
chain.doFilter(request, response);
} catch (ExpiredJwtException ex) {
resolver.resolveException(request, response, null, ex);
}
}
Antworten
Es gibt 2 Hauptansätze, um mit solchen Situationen umzugehen:
Zugriffs- und Aktualisierungstoken verwalten
In diesem Fall ist der Ablauf folgender:
Benutzeranmeldungen bei der Anwendung (einschließlich
username
undpassword
)Ihre Back-End-Anwendung gibt alle erforderlichen Anmeldeinformationen zurück und:
2.1 Greifen Sie auf JWT-Token mit einer abgelaufenen Zeit zu, die normalerweise "niedrig" ist (15, 30 Minuten usw.).
2.2 Aktualisieren Sie das JWT-Token mit einer abgelaufenen Zeit, die größer als die Zugriffszeit ist.
Von nun an verwendet Ihre Frontend-Anwendung
access token
imAuthorization
Header für jede Anfrage.
Wenn das Backend zurückgibt 401
, versucht die Frontend-Anwendung, refresh token
mithilfe eines bestimmten Endpunkts neue Anmeldeinformationen abzurufen, ohne den Benutzer zu einer erneuten Anmeldung zu zwingen.
Refresh-Token-Fluss (Dies ist nur ein Beispiel, normalerweise wird nur das Refresh-Token gesendet)
Wenn es kein Problem gibt, kann der Benutzer die Anwendung weiter verwenden. Wenn das Backend ein neues 401
=> zurückgibt, sollte das Frontend zur Anmeldeseite umleiten.
Verwalten Sie nur ein JWT-Token
In diesem Fall ähnelt der Ablauf dem vorherigen, und Sie können Ihren eigenen Endpunkt erstellen, um mit solchen Situationen umzugehen: /auth/token/extend
(z. B.) einschließlich des abgelaufenen Jwt als Parameter der Anforderung.
Jetzt liegt es an Ihnen zu verwalten:
- Wie lange ist ein abgelaufener Jwt-Token „gültig“, um ihn zu verlängern?
Der neue Endpunkt wird ein ähnliches Verhalten von Refresh One im vorherigen Abschnitt haben, ich meine, er wird ein neues Jwt-Token oder 401
so zurückgeben, aus Sicht des Frontends wird der Fluss derselbe sein.
Eine wichtige Sache , unabhängig von dem Ansatz, den Sie verfolgen möchten, der "neue Endpunkt" sollte von den erforderlichen Spring-authentifizierten Endpunkten ausgeschlossen werden, da Sie die Sicherheit selbst verwalten:
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
..
@Override
protected void configure(HttpSecurity http) throws Exception {
http.
..
.authorizeRequests()
// List of services do not require authentication
.antMatchers(Rest Operator, "MyEndpointToRefreshOrExtendToken").permitAll()
// Any other request must be authenticated
.anyRequest().authenticated()
..
}
}
Sie können die API zum Abrufen des Aktualisierungstokens wie folgt aufrufen
POST https://yourdomain.com/oauth/token
Header
"Authorization": "Bearer [base64encode(clientId:clientSecret)]"
Parameters
"grant_type": "refresh_token"
"refresh_token": "[yourRefreshToken]"
Bitte beachten Sie, dass die
- base64encode ist die Methode zur Verschlüsselung der Client-Autorisierung. Sie können online unter verwendenhttps://www.base64encode.org/
- das refresh_token ist der String-Wert des grant_type
- yourRefreshToken ist das mit dem JWT-Zugriffstoken empfangene Aktualisierungstoken
Das Ergebnis kann sich sehen lassen
{
"token_type":"bearer",
"access_token":"eyJ0eXAiOiJK.iLCJpYXQiO.Dww7TC9xu_2s",
"expires_in":20,
"refresh_token":"7fd15938c823cf58e78019bea2af142f9449696a"
}
Viel Glück.