При создании токена доступа вы можете создать токен обновления и сохранить его в базе данных для этого пользователя. Вот изменения кода, которые вам нужно сделать -
Модель JwtRefreshToken
Создание модели домена JwtRefreshToken:
package com.example.polls.model;
import javax.persistence.*;
import java.time.Instant;
@Entity
@Table(name = "refresh_tokens")
public class JwtRefreshToken {
@Id
private String token;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private User user;
private Instant expirationDateTime;
public JwtRefreshToken() {
}
public JwtRefreshToken(String token) {
this.token = token;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Instant getExpirationDateTime() {
return expirationDateTime;
}
public void setExpirationDateTime(Instant expirationDateTime) {
this.expirationDateTime = expirationDateTime;
}
}
JwtRefreshToken репозиторий
Создать репозиторий для доступа к RefreshToken:
package com.example.polls.repository;
import com.example.polls.model.JwtRefreshToken;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface JwtRefreshTokenRepository extends JpaRepository<JwtRefreshToken, String> {
}
JwtAuthenticationResponse
Добавьте поля refreshToken и expiresInMsec в ответ аутентификации:
package com.example.polls.payload;
/**
* Created by rajeevkumarsingh on 19/08/17.
*/
public class JwtAuthenticationResponse {
private String accessToken;
private String refreshToken;
private String tokenType = "Bearer";
private Long expiresInMsec;
public JwtAuthenticationResponse(String accessToken, String refreshToken, Long expiresInMsec) {
this.accessToken = accessToken;
this.refreshToken = refreshToken;
this.expiresInMsec = expiresInMsec;
}
public String getRefreshToken() {
return refreshToken;
}
public void setRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
}
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public String getTokenType() {
return tokenType;
}
public void setTokenType(String tokenType) {
this.tokenType = tokenType;
}
public Long getExpiresInMsec() {
return expiresInMsec;
}
public void setExpiresInMsec(Long expiresInMsec) {
this.expiresInMsec = expiresInMsec;
}
}
JwtTokenProvider
Добавить метод для генерации токена обновления:
package com.example.polls.security;
import io.jsonwebtoken.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.UUID;
@Component
public class JwtTokenProvider {
private static final Logger logger = LoggerFactory.getLogger(JwtTokenProvider.class);
@Value("${app.jwtSecret}")
private String jwtSecret;
@Value("${app.jwtExpirationInMs}")
private long jwtExpirationInMs;
public String generateToken(UserPrincipal userPrincipal) {
Date now = new Date();
Date expiryDate = new Date(now.getTime() + jwtExpirationInMs);
return Jwts.builder()
.setSubject(Long.toString(userPrincipal.getId()))
.setIssuedAt(new Date())
.setExpiration(expiryDate)
.signWith(SignatureAlgorithm.HS512, jwtSecret)
.compact();
}
public String generateRefreshToken() {
// generate a random UUID as refresh token
return UUID.randomUUID().toString();
}
public Long getUserIdFromJWT(String token) {
Claims claims = Jwts.parser()
.setSigningKey(jwtSecret)
.parseClaimsJws(token)
.getBody();
return Long.parseLong(claims.getSubject());
}
public boolean validateToken(String authToken) {
try {
Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken);
return true;
} catch (SignatureException ex) {
logger.error("Invalid JWT signature");
} catch (MalformedJwtException ex) {
logger.error("Invalid JWT token");
} catch (ExpiredJwtException ex) {
logger.error("Expired JWT token");
} catch (UnsupportedJwtException ex) {
logger.error("Unsupported JWT token");
} catch (IllegalArgumentException ex) {
logger.error("JWT claims string is empty.");
}
return false;
}
}
AuthController
Сохраните токен обновления и напишите API для обновления токенов доступа:
package com.example.polls.controller;
import com.example.polls.exception.AppException;
import com.example.polls.exception.BadRequestException;
import com.example.polls.model.JwtRefreshToken;
import com.example.polls.model.Role;
import com.example.polls.model.RoleName;
import com.example.polls.model.User;
import com.example.polls.payload.*;
import com.example.polls.repository.JwtRefreshTokenRepository;
import com.example.polls.repository.RoleRepository;
import com.example.polls.repository.UserRepository;
import com.example.polls.security.JwtTokenProvider;
import com.example.polls.security.UserPrincipal;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import javax.validation.Valid;
import java.net.URI;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Collections;
/**
* Created by rajeevkumarsingh on 02/08/17.
*/
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@Autowired
AuthenticationManager authenticationManager;
@Autowired
UserRepository userRepository;
@Autowired
RoleRepository roleRepository;
@Autowired
PasswordEncoder passwordEncoder;
@Autowired
JwtTokenProvider tokenProvider;
@Autowired
JwtRefreshTokenRepository jwtRefreshTokenRepository;
@Value("${app.jwtExpirationInMs}")
private long jwtExpirationInMs;
@PostMapping("/signin")
public ResponseEntity<?> authenticateUser(@Valid @RequestBody LoginRequest loginRequest) {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
loginRequest.getUsernameOrEmail(),
loginRequest.getPassword()
)
);
SecurityContextHolder.getContext().setAuthentication(authentication);
UserPrincipal userPrincipal = (UserPrincipal) authentication.getPrincipal();
String accessToken = tokenProvider.generateToken(userPrincipal);
String refreshToken = tokenProvider.generateRefreshToken();
saveRefreshToken(userPrincipal, refreshToken);
return ResponseEntity.ok(new JwtAuthenticationResponse(accessToken, refreshToken, jwtExpirationInMs));
}
@PostMapping("/refreshToken")
public ResponseEntity<?> refreshAccessToken(@Valid @RequestBody RefreshTokenRequest refreshTokenRequest) {
return jwtRefreshTokenRepository.findById(refreshTokenRequest.getRefreshToken()).map(jwtRefreshToken -> {
User user = jwtRefreshToken.getUser();
String accessToken = tokenProvider.generateToken(UserPrincipal.create(user));
return ResponseEntity.ok(new JwtAuthenticationResponse(accessToken, jwtRefreshToken.getToken(), jwtExpirationInMs));
}).orElseThrow(() -> new BadRequestException("Invalid Refresh Token"));
}
private void saveRefreshToken(UserPrincipal userPrincipal, String refreshToken) {
// Persist Refresh Token
JwtRefreshToken jwtRefreshToken = new JwtRefreshToken(refreshToken);
jwtRefreshToken.setUser(userRepository.getOne(userPrincipal.getId()));
Instant expirationDateTime = Instant.now().plus(360, ChronoUnit.DAYS); // Todo Add this in application.properties
jwtRefreshToken.setExpirationDateTime(expirationDateTime);
jwtRefreshTokenRepository.save(jwtRefreshToken);
}
@PostMapping("/signup")
public ResponseEntity<?> registerUser(@Valid @RequestBody SignUpRequest signUpRequest) {
if(userRepository.existsByUsername(signUpRequest.getUsername())) {
return new ResponseEntity(new ApiResponse(false, "Username is already taken!"),
HttpStatus.BAD_REQUEST);
}
if(userRepository.existsByEmail(signUpRequest.getEmail())) {
return new ResponseEntity(new ApiResponse(false, "Email Address already in use!"),
HttpStatus.BAD_REQUEST);
}
// Creating user's account
User user = new User(signUpRequest.getName(), signUpRequest.getUsername(),
signUpRequest.getEmail(), signUpRequest.getPassword());
user.setPassword(passwordEncoder.encode(user.getPassword()));
Role userRole = roleRepository.findByName(RoleName.ROLE_USER)
.orElseThrow(() -> new AppException("User Role not set."));
user.setRoles(Collections.singleton(userRole));
User result = userRepository.save(user);
URI location = ServletUriComponentsBuilder
.fromCurrentContextPath().path("/users/{username}")
.buildAndExpand(result.getUsername()).toUri();
return ResponseEntity.created(location).body(new ApiResponse(true, "User registered successfully"));
}
}
RefreshTokenRequest
Используется API / api / auth / refreshToken.
package com.example.polls.payload;
import javax.validation.constraints.NotBlank;
public class RefreshTokenRequest {
@NotBlank
private String refreshToken;
public String getRefreshToken() {
return refreshToken;
}
public void setRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
}
}