diff --git a/pom.xml b/pom.xml
index fc5bfeac..a479c3da 100644
--- a/pom.xml
+++ b/pom.xml
@@ -67,6 +67,23 @@
spring-security-test
test
+
+ io.jsonwebtoken
+ jjwt-api
+ 0.12.3
+
+
+ io.jsonwebtoken
+ jjwt-impl
+ 0.12.3
+ runtime
+
+
+ io.jsonwebtoken
+ jjwt-jackson
+ 0.12.3
+ runtime
+
diff --git a/src/main/java/com/example/bankapp/config/SecurityConfig.java b/src/main/java/com/example/bankapp/config/SecurityConfig.java
index 4dbd1572..2d8c586f 100644
--- a/src/main/java/com/example/bankapp/config/SecurityConfig.java
+++ b/src/main/java/com/example/bankapp/config/SecurityConfig.java
@@ -1,15 +1,21 @@
package com.example.bankapp.config;
+import com.example.bankapp.security.JwtAuthenticationFilter;
import com.example.bankapp.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import org.springframework.core.annotation.Order;
+import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
@Configuration
@@ -19,13 +25,38 @@ public class SecurityConfig {
@Autowired
AccountService accountService;
+ @Autowired
+ JwtAuthenticationFilter jwtAuthenticationFilter;
+
@Bean
public static PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
- public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
+ public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
+ return authenticationConfiguration.getAuthenticationManager();
+ }
+
+ @Bean
+ @Order(1)
+ public SecurityFilterChain apiSecurityFilterChain(HttpSecurity http) throws Exception {
+ http
+ .securityMatcher("/api/**")
+ .csrf(csrf -> csrf.disable())
+ .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
+ .authorizeHttpRequests(authz -> authz
+ .requestMatchers("/api/auth/**").permitAll()
+ .requestMatchers("/api/**").authenticated()
+ )
+ .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
+
+ return http.build();
+ }
+
+ @Bean
+ @Order(2)
+ public SecurityFilterChain webSecurityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(authz -> authz
@@ -55,6 +86,5 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(accountService).passwordEncoder(passwordEncoder());
-
}
-}
\ No newline at end of file
+}
diff --git a/src/main/java/com/example/bankapp/controller/AuthController.java b/src/main/java/com/example/bankapp/controller/AuthController.java
new file mode 100644
index 00000000..1d1d83c8
--- /dev/null
+++ b/src/main/java/com/example/bankapp/controller/AuthController.java
@@ -0,0 +1,73 @@
+package com.example.bankapp.controller;
+
+import com.example.bankapp.dto.AuthResponse;
+import com.example.bankapp.dto.LoginRequest;
+import com.example.bankapp.dto.RegisterRequest;
+import com.example.bankapp.model.Account;
+import com.example.bankapp.security.JwtUtil;
+import com.example.bankapp.service.AccountService;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@RestController
+@RequestMapping("/api/auth")
+public class AuthController {
+
+ private final AccountService accountService;
+ private final JwtUtil jwtUtil;
+ private final AuthenticationManager authenticationManager;
+
+ public AuthController(AccountService accountService, JwtUtil jwtUtil, AuthenticationManager authenticationManager) {
+ this.accountService = accountService;
+ this.jwtUtil = jwtUtil;
+ this.authenticationManager = authenticationManager;
+ }
+
+ @PostMapping("/login")
+ public ResponseEntity> login(@RequestBody LoginRequest request) {
+ try {
+ authenticationManager.authenticate(
+ new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword())
+ );
+
+ UserDetails userDetails = accountService.loadUserByUsername(request.getUsername());
+ String token = jwtUtil.generateToken(userDetails);
+
+ AuthResponse response = new AuthResponse(token, request.getUsername(), "Login successful");
+ return ResponseEntity.ok(response);
+ } catch (BadCredentialsException e) {
+ Map error = new HashMap<>();
+ error.put("error", "Invalid username or password");
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(error);
+ } catch (Exception e) {
+ Map error = new HashMap<>();
+ error.put("error", "Authentication failed: " + e.getMessage());
+ return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
+ }
+ }
+
+ @PostMapping("/register")
+ public ResponseEntity> register(@RequestBody RegisterRequest request) {
+ try {
+ Account account = accountService.registerAccount(request.getUsername(), request.getPassword());
+
+ UserDetails userDetails = accountService.loadUserByUsername(account.getUsername());
+ String token = jwtUtil.generateToken(userDetails);
+
+ AuthResponse response = new AuthResponse(token, account.getUsername(), "Registration successful");
+ return ResponseEntity.status(HttpStatus.CREATED).body(response);
+ } catch (RuntimeException e) {
+ Map error = new HashMap<>();
+ error.put("error", e.getMessage());
+ return ResponseEntity.badRequest().body(error);
+ }
+ }
+}
diff --git a/src/main/java/com/example/bankapp/controller/BankApiController.java b/src/main/java/com/example/bankapp/controller/BankApiController.java
new file mode 100644
index 00000000..54ee1b8b
--- /dev/null
+++ b/src/main/java/com/example/bankapp/controller/BankApiController.java
@@ -0,0 +1,110 @@
+package com.example.bankapp.controller;
+
+import com.example.bankapp.dto.*;
+import com.example.bankapp.model.Account;
+import com.example.bankapp.model.Transaction;
+import com.example.bankapp.service.AccountService;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@RestController
+@RequestMapping("/api")
+public class BankApiController {
+
+ private final AccountService accountService;
+
+ public BankApiController(AccountService accountService) {
+ this.accountService = accountService;
+ }
+
+ @GetMapping("/account")
+ public ResponseEntity getAccount() {
+ String username = SecurityContextHolder.getContext().getAuthentication().getName();
+ Account account = accountService.findAccountByUsername(username);
+ AccountDTO accountDTO = new AccountDTO(account.getId(), account.getUsername(), account.getBalance());
+ return ResponseEntity.ok(accountDTO);
+ }
+
+ @PostMapping("/deposit")
+ public ResponseEntity> deposit(@RequestBody DepositRequest request) {
+ try {
+ String username = SecurityContextHolder.getContext().getAuthentication().getName();
+ Account account = accountService.findAccountByUsername(username);
+ accountService.deposit(account, request.getAmount());
+
+ Account updatedAccount = accountService.findAccountByUsername(username);
+ AccountDTO accountDTO = new AccountDTO(updatedAccount.getId(), updatedAccount.getUsername(), updatedAccount.getBalance());
+
+ Map response = new HashMap<>();
+ response.put("message", "Deposit successful");
+ response.put("account", accountDTO);
+ return ResponseEntity.ok(response);
+ } catch (RuntimeException e) {
+ Map error = new HashMap<>();
+ error.put("error", e.getMessage());
+ return ResponseEntity.badRequest().body(error);
+ }
+ }
+
+ @PostMapping("/withdraw")
+ public ResponseEntity> withdraw(@RequestBody WithdrawRequest request) {
+ try {
+ String username = SecurityContextHolder.getContext().getAuthentication().getName();
+ Account account = accountService.findAccountByUsername(username);
+ accountService.withdraw(account, request.getAmount());
+
+ Account updatedAccount = accountService.findAccountByUsername(username);
+ AccountDTO accountDTO = new AccountDTO(updatedAccount.getId(), updatedAccount.getUsername(), updatedAccount.getBalance());
+
+ Map response = new HashMap<>();
+ response.put("message", "Withdrawal successful");
+ response.put("account", accountDTO);
+ return ResponseEntity.ok(response);
+ } catch (RuntimeException e) {
+ Map error = new HashMap<>();
+ error.put("error", e.getMessage());
+ return ResponseEntity.badRequest().body(error);
+ }
+ }
+
+ @PostMapping("/transfer")
+ public ResponseEntity> transfer(@RequestBody TransferRequest request) {
+ try {
+ String username = SecurityContextHolder.getContext().getAuthentication().getName();
+ Account fromAccount = accountService.findAccountByUsername(username);
+ accountService.transferAmount(fromAccount, request.getToUsername(), request.getAmount());
+
+ Account updatedAccount = accountService.findAccountByUsername(username);
+ AccountDTO accountDTO = new AccountDTO(updatedAccount.getId(), updatedAccount.getUsername(), updatedAccount.getBalance());
+
+ Map response = new HashMap<>();
+ response.put("message", "Transfer successful");
+ response.put("account", accountDTO);
+ return ResponseEntity.ok(response);
+ } catch (RuntimeException e) {
+ Map error = new HashMap<>();
+ error.put("error", e.getMessage());
+ return ResponseEntity.badRequest().body(error);
+ }
+ }
+
+ @GetMapping("/transactions")
+ public ResponseEntity> getTransactions() {
+ String username = SecurityContextHolder.getContext().getAuthentication().getName();
+ Account account = accountService.findAccountByUsername(username);
+ List transactions = accountService.getTransactionHistory(account);
+
+ List transactionDTOs = transactions.stream()
+ .map(t -> new TransactionDTO(t.getId(), t.getAmount(), t.getType(), t.getTimestamp()))
+ .collect(Collectors.toList());
+
+ return ResponseEntity.ok(transactionDTOs);
+ }
+}
diff --git a/src/main/java/com/example/bankapp/dto/AccountDTO.java b/src/main/java/com/example/bankapp/dto/AccountDTO.java
new file mode 100644
index 00000000..f948c625
--- /dev/null
+++ b/src/main/java/com/example/bankapp/dto/AccountDTO.java
@@ -0,0 +1,42 @@
+package com.example.bankapp.dto;
+
+import java.math.BigDecimal;
+
+public class AccountDTO {
+ private Long id;
+ private String username;
+ private BigDecimal balance;
+
+ public AccountDTO() {
+ }
+
+ public AccountDTO(Long id, String username, BigDecimal balance) {
+ this.id = id;
+ this.username = username;
+ this.balance = balance;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public BigDecimal getBalance() {
+ return balance;
+ }
+
+ public void setBalance(BigDecimal balance) {
+ this.balance = balance;
+ }
+}
diff --git a/src/main/java/com/example/bankapp/dto/AuthResponse.java b/src/main/java/com/example/bankapp/dto/AuthResponse.java
new file mode 100644
index 00000000..408648d9
--- /dev/null
+++ b/src/main/java/com/example/bankapp/dto/AuthResponse.java
@@ -0,0 +1,40 @@
+package com.example.bankapp.dto;
+
+public class AuthResponse {
+ private String token;
+ private String username;
+ private String message;
+
+ public AuthResponse() {
+ }
+
+ public AuthResponse(String token, String username, String message) {
+ this.token = token;
+ this.username = username;
+ this.message = message;
+ }
+
+ public String getToken() {
+ return token;
+ }
+
+ public void setToken(String token) {
+ this.token = token;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+}
diff --git a/src/main/java/com/example/bankapp/dto/DepositRequest.java b/src/main/java/com/example/bankapp/dto/DepositRequest.java
new file mode 100644
index 00000000..dee9f3db
--- /dev/null
+++ b/src/main/java/com/example/bankapp/dto/DepositRequest.java
@@ -0,0 +1,22 @@
+package com.example.bankapp.dto;
+
+import java.math.BigDecimal;
+
+public class DepositRequest {
+ private BigDecimal amount;
+
+ public DepositRequest() {
+ }
+
+ public DepositRequest(BigDecimal amount) {
+ this.amount = amount;
+ }
+
+ public BigDecimal getAmount() {
+ return amount;
+ }
+
+ public void setAmount(BigDecimal amount) {
+ this.amount = amount;
+ }
+}
diff --git a/src/main/java/com/example/bankapp/dto/LoginRequest.java b/src/main/java/com/example/bankapp/dto/LoginRequest.java
new file mode 100644
index 00000000..ccd7b43d
--- /dev/null
+++ b/src/main/java/com/example/bankapp/dto/LoginRequest.java
@@ -0,0 +1,30 @@
+package com.example.bankapp.dto;
+
+public class LoginRequest {
+ private String username;
+ private String password;
+
+ public LoginRequest() {
+ }
+
+ public LoginRequest(String username, String password) {
+ this.username = username;
+ this.password = password;
+ }
+
+ 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;
+ }
+}
diff --git a/src/main/java/com/example/bankapp/dto/RegisterRequest.java b/src/main/java/com/example/bankapp/dto/RegisterRequest.java
new file mode 100644
index 00000000..e69b84ae
--- /dev/null
+++ b/src/main/java/com/example/bankapp/dto/RegisterRequest.java
@@ -0,0 +1,30 @@
+package com.example.bankapp.dto;
+
+public class RegisterRequest {
+ private String username;
+ private String password;
+
+ public RegisterRequest() {
+ }
+
+ public RegisterRequest(String username, String password) {
+ this.username = username;
+ this.password = password;
+ }
+
+ 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;
+ }
+}
diff --git a/src/main/java/com/example/bankapp/dto/TransactionDTO.java b/src/main/java/com/example/bankapp/dto/TransactionDTO.java
new file mode 100644
index 00000000..02e5a643
--- /dev/null
+++ b/src/main/java/com/example/bankapp/dto/TransactionDTO.java
@@ -0,0 +1,53 @@
+package com.example.bankapp.dto;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+public class TransactionDTO {
+ private Long id;
+ private BigDecimal amount;
+ private String type;
+ private LocalDateTime timestamp;
+
+ public TransactionDTO() {
+ }
+
+ public TransactionDTO(Long id, BigDecimal amount, String type, LocalDateTime timestamp) {
+ this.id = id;
+ this.amount = amount;
+ this.type = type;
+ this.timestamp = timestamp;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public BigDecimal getAmount() {
+ return amount;
+ }
+
+ public void setAmount(BigDecimal amount) {
+ this.amount = amount;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public LocalDateTime getTimestamp() {
+ return timestamp;
+ }
+
+ public void setTimestamp(LocalDateTime timestamp) {
+ this.timestamp = timestamp;
+ }
+}
diff --git a/src/main/java/com/example/bankapp/dto/TransferRequest.java b/src/main/java/com/example/bankapp/dto/TransferRequest.java
new file mode 100644
index 00000000..d36504b4
--- /dev/null
+++ b/src/main/java/com/example/bankapp/dto/TransferRequest.java
@@ -0,0 +1,32 @@
+package com.example.bankapp.dto;
+
+import java.math.BigDecimal;
+
+public class TransferRequest {
+ private String toUsername;
+ private BigDecimal amount;
+
+ public TransferRequest() {
+ }
+
+ public TransferRequest(String toUsername, BigDecimal amount) {
+ this.toUsername = toUsername;
+ this.amount = amount;
+ }
+
+ public String getToUsername() {
+ return toUsername;
+ }
+
+ public void setToUsername(String toUsername) {
+ this.toUsername = toUsername;
+ }
+
+ public BigDecimal getAmount() {
+ return amount;
+ }
+
+ public void setAmount(BigDecimal amount) {
+ this.amount = amount;
+ }
+}
diff --git a/src/main/java/com/example/bankapp/dto/WithdrawRequest.java b/src/main/java/com/example/bankapp/dto/WithdrawRequest.java
new file mode 100644
index 00000000..55a6988d
--- /dev/null
+++ b/src/main/java/com/example/bankapp/dto/WithdrawRequest.java
@@ -0,0 +1,22 @@
+package com.example.bankapp.dto;
+
+import java.math.BigDecimal;
+
+public class WithdrawRequest {
+ private BigDecimal amount;
+
+ public WithdrawRequest() {
+ }
+
+ public WithdrawRequest(BigDecimal amount) {
+ this.amount = amount;
+ }
+
+ public BigDecimal getAmount() {
+ return amount;
+ }
+
+ public void setAmount(BigDecimal amount) {
+ this.amount = amount;
+ }
+}
diff --git a/src/main/java/com/example/bankapp/security/JwtAuthenticationFilter.java b/src/main/java/com/example/bankapp/security/JwtAuthenticationFilter.java
new file mode 100644
index 00000000..fd698b06
--- /dev/null
+++ b/src/main/java/com/example/bankapp/security/JwtAuthenticationFilter.java
@@ -0,0 +1,65 @@
+package com.example.bankapp.security;
+
+import com.example.bankapp.service.AccountService;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
+import org.springframework.stereotype.Component;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import java.io.IOException;
+
+@Component
+public class JwtAuthenticationFilter extends OncePerRequestFilter {
+
+ private final JwtUtil jwtUtil;
+ private final AccountService accountService;
+
+ public JwtAuthenticationFilter(JwtUtil jwtUtil, AccountService accountService) {
+ this.jwtUtil = jwtUtil;
+ this.accountService = accountService;
+ }
+
+ @Override
+ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
+ throws ServletException, IOException {
+
+ final String authorizationHeader = request.getHeader("Authorization");
+
+ String username = null;
+ String jwt = null;
+
+ if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
+ jwt = authorizationHeader.substring(7);
+ try {
+ username = jwtUtil.extractUsername(jwt);
+ } catch (Exception e) {
+ logger.error("Error extracting username from JWT: " + e.getMessage());
+ }
+ }
+
+ if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
+ UserDetails userDetails = accountService.loadUserByUsername(username);
+
+ if (jwtUtil.validateToken(jwt, userDetails)) {
+ UsernamePasswordAuthenticationToken authenticationToken =
+ new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
+ authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
+ SecurityContextHolder.getContext().setAuthentication(authenticationToken);
+ }
+ }
+
+ filterChain.doFilter(request, response);
+ }
+
+ @Override
+ protected boolean shouldNotFilter(HttpServletRequest request) {
+ String path = request.getServletPath();
+ return !path.startsWith("/api/") || path.startsWith("/api/auth/");
+ }
+}
diff --git a/src/main/java/com/example/bankapp/security/JwtUtil.java b/src/main/java/com/example/bankapp/security/JwtUtil.java
new file mode 100644
index 00000000..97c20cee
--- /dev/null
+++ b/src/main/java/com/example/bankapp/security/JwtUtil.java
@@ -0,0 +1,74 @@
+package com.example.bankapp.security;
+
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.security.Keys;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.stereotype.Component;
+
+import javax.crypto.SecretKey;
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Function;
+
+@Component
+public class JwtUtil {
+
+ @Value("${jwt.secret:defaultSecretKeyForJwtTokenGenerationThatIsAtLeast256BitsLong}")
+ private String secret;
+
+ @Value("${jwt.expiration:86400000}")
+ private Long expiration;
+
+ private SecretKey getSigningKey() {
+ return Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));
+ }
+
+ public String extractUsername(String token) {
+ return extractClaim(token, Claims::getSubject);
+ }
+
+ public Date extractExpiration(String token) {
+ return extractClaim(token, Claims::getExpiration);
+ }
+
+ public T extractClaim(String token, Function claimsResolver) {
+ final Claims claims = extractAllClaims(token);
+ return claimsResolver.apply(claims);
+ }
+
+ private Claims extractAllClaims(String token) {
+ return Jwts.parser()
+ .verifyWith(getSigningKey())
+ .build()
+ .parseSignedClaims(token)
+ .getPayload();
+ }
+
+ private Boolean isTokenExpired(String token) {
+ return extractExpiration(token).before(new Date());
+ }
+
+ public String generateToken(UserDetails userDetails) {
+ Map claims = new HashMap<>();
+ return createToken(claims, userDetails.getUsername());
+ }
+
+ private String createToken(Map claims, String subject) {
+ return Jwts.builder()
+ .claims(claims)
+ .subject(subject)
+ .issuedAt(new Date(System.currentTimeMillis()))
+ .expiration(new Date(System.currentTimeMillis() + expiration))
+ .signWith(getSigningKey())
+ .compact();
+ }
+
+ public Boolean validateToken(String token, UserDetails userDetails) {
+ final String username = extractUsername(token);
+ return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
+ }
+}