diff --git a/README.md b/README.md
index e3e7f79..a65578f 100644
--- a/README.md
+++ b/README.md
@@ -32,3 +32,4 @@ To build and run the project, follow these steps:
* Run the project: mvn spring-boot:run
-> The application will be available at http://localhost:8080.
+END.
diff --git a/pom.xml b/pom.xml
index 9c14b26..4f45c65 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.springframework.boot
spring-boot-starter-parent
- 3.1.4
+ 3.3.2
com.alibou
diff --git a/src/main/java/com/alibou/security/SecurityApplication.java b/src/main/java/com/alibou/security/SecurityApplication.java
index 1448cff..282185d 100644
--- a/src/main/java/com/alibou/security/SecurityApplication.java
+++ b/src/main/java/com/alibou/security/SecurityApplication.java
@@ -2,7 +2,6 @@
import com.alibou.security.auth.AuthenticationService;
import com.alibou.security.auth.RegisterRequest;
-import com.alibou.security.user.Role;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@@ -16,33 +15,30 @@
@EnableJpaAuditing(auditorAwareRef = "auditorAware")
public class SecurityApplication {
- public static void main(String[] args) {
- SpringApplication.run(SecurityApplication.class, args);
- }
+ public static void main(String[] args) {
+ SpringApplication.run(SecurityApplication.class, args);
+ }
- @Bean
- public CommandLineRunner commandLineRunner(
- AuthenticationService service
- ) {
- return args -> {
- var admin = RegisterRequest.builder()
- .firstname("Admin")
- .lastname("Admin")
- .email("admin@mail.com")
- .password("password")
- .role(ADMIN)
- .build();
- System.out.println("Admin token: " + service.register(admin).getAccessToken());
+ @Bean
+ public CommandLineRunner commandLineRunner(AuthenticationService service) {
+ return args -> {
+ var admin = RegisterRequest.builder()
+ .firstname("Admin")
+ .lastname("Admin")
+ .email("admin@mail.com")
+ .password("password")
+ .role(ADMIN)
+ .build();
+ System.out.println("Admin token: " + service.register(admin).getAccessToken());
- var manager = RegisterRequest.builder()
- .firstname("Admin")
- .lastname("Admin")
- .email("manager@mail.com")
- .password("password")
- .role(MANAGER)
- .build();
- System.out.println("Manager token: " + service.register(manager).getAccessToken());
-
- };
- }
+ var manager = RegisterRequest.builder()
+ .firstname("Manager")
+ .lastname("Manager")
+ .email("manager@mail.com")
+ .password("password")
+ .role(MANAGER)
+ .build();
+ System.out.println("Manager token: " + service.register(manager).getAccessToken());
+ };
+ }
}
diff --git a/src/main/java/com/alibou/security/auditing/ApplicationAuditAware.java b/src/main/java/com/alibou/security/auditing/ApplicationAuditAware.java
index 3f8172f..5a5a179 100644
--- a/src/main/java/com/alibou/security/auditing/ApplicationAuditAware.java
+++ b/src/main/java/com/alibou/security/auditing/ApplicationAuditAware.java
@@ -9,6 +9,7 @@
import java.util.Optional;
public class ApplicationAuditAware implements AuditorAware {
+
@Override
public Optional getCurrentAuditor() {
Authentication authentication =
@@ -16,7 +17,7 @@ public Optional getCurrentAuditor() {
.getContext()
.getAuthentication();
if (authentication == null ||
- !authentication.isAuthenticated() ||
+ !authentication.isAuthenticated() ||
authentication instanceof AnonymousAuthenticationToken
) {
return Optional.empty();
diff --git a/src/main/java/com/alibou/security/auth/AuthenticationController.java b/src/main/java/com/alibou/security/auth/AuthenticationController.java
index e1d5107..9594bf7 100644
--- a/src/main/java/com/alibou/security/auth/AuthenticationController.java
+++ b/src/main/java/com/alibou/security/auth/AuthenticationController.java
@@ -16,28 +16,20 @@
@RequiredArgsConstructor
public class AuthenticationController {
- private final AuthenticationService service;
-
- @PostMapping("/register")
- public ResponseEntity register(
- @RequestBody RegisterRequest request
- ) {
- return ResponseEntity.ok(service.register(request));
- }
- @PostMapping("/authenticate")
- public ResponseEntity authenticate(
- @RequestBody AuthenticationRequest request
- ) {
- return ResponseEntity.ok(service.authenticate(request));
- }
-
- @PostMapping("/refresh-token")
- public void refreshToken(
- HttpServletRequest request,
- HttpServletResponse response
- ) throws IOException {
- service.refreshToken(request, response);
- }
-
-
+ private final AuthenticationService service;
+
+ @PostMapping("/register")
+ public ResponseEntity register(@RequestBody RegisterRequest request) {
+ return ResponseEntity.ok(service.register(request));
+ }
+
+ @PostMapping("/authenticate")
+ public ResponseEntity authenticate(@RequestBody AuthenticationRequest request) {
+ return ResponseEntity.ok(service.authenticate(request));
+ }
+
+ @PostMapping("/refresh-token")
+ public void refreshToken(HttpServletRequest request, HttpServletResponse response) throws IOException {
+ service.refreshToken(request, response);
+ }
}
diff --git a/src/main/java/com/alibou/security/auth/AuthenticationRequest.java b/src/main/java/com/alibou/security/auth/AuthenticationRequest.java
index 6d72722..8297f1e 100644
--- a/src/main/java/com/alibou/security/auth/AuthenticationRequest.java
+++ b/src/main/java/com/alibou/security/auth/AuthenticationRequest.java
@@ -11,6 +11,6 @@
@NoArgsConstructor
public class AuthenticationRequest {
- private String email;
- String password;
+ private String email;
+ String password;
}
diff --git a/src/main/java/com/alibou/security/auth/AuthenticationResponse.java b/src/main/java/com/alibou/security/auth/AuthenticationResponse.java
index c10bbb6..a0964bc 100644
--- a/src/main/java/com/alibou/security/auth/AuthenticationResponse.java
+++ b/src/main/java/com/alibou/security/auth/AuthenticationResponse.java
@@ -12,8 +12,8 @@
@NoArgsConstructor
public class AuthenticationResponse {
- @JsonProperty("access_token")
- private String accessToken;
- @JsonProperty("refresh_token")
- private String refreshToken;
+ @JsonProperty("access_token")
+ private String accessToken;
+ @JsonProperty("refresh_token")
+ private String refreshToken;
}
diff --git a/src/main/java/com/alibou/security/auth/AuthenticationService.java b/src/main/java/com/alibou/security/auth/AuthenticationService.java
index 53193a7..a70e83c 100644
--- a/src/main/java/com/alibou/security/auth/AuthenticationService.java
+++ b/src/main/java/com/alibou/security/auth/AuthenticationService.java
@@ -25,96 +25,94 @@
@Service
@RequiredArgsConstructor
public class AuthenticationService {
- private final UserRepository repository;
- private final TokenRepository tokenRepository;
- private final PasswordEncoder passwordEncoder;
- private final JwtService jwtService;
- private final AuthenticationManager authenticationManager;
- public AuthenticationResponse register(RegisterRequest request) {
- var user = User.builder()
- .firstname(request.getFirstname())
- .lastname(request.getLastname())
- .email(request.getEmail())
- .password(passwordEncoder.encode(request.getPassword()))
- .role(request.getRole())
- .build();
- var savedUser = repository.save(user);
- var jwtToken = jwtService.generateToken(user);
- var refreshToken = jwtService.generateRefreshToken(user);
- saveUserToken(savedUser, jwtToken);
- return AuthenticationResponse.builder()
- .accessToken(jwtToken)
- .refreshToken(refreshToken)
- .build();
- }
+ private final UserRepository repository;
+ private final TokenRepository tokenRepository;
+ private final PasswordEncoder passwordEncoder;
+ private final JwtService jwtService;
+ private final AuthenticationManager authenticationManager;
- public AuthenticationResponse authenticate(AuthenticationRequest request) {
- authenticationManager.authenticate(
- new UsernamePasswordAuthenticationToken(
- request.getEmail(),
- request.getPassword()
- )
- );
- var user = repository.findByEmail(request.getEmail())
- .orElseThrow();
- var jwtToken = jwtService.generateToken(user);
- var refreshToken = jwtService.generateRefreshToken(user);
- revokeAllUserTokens(user);
- saveUserToken(user, jwtToken);
- return AuthenticationResponse.builder()
- .accessToken(jwtToken)
- .refreshToken(refreshToken)
- .build();
- }
-
- private void saveUserToken(User user, String jwtToken) {
- var token = Token.builder()
- .user(user)
- .token(jwtToken)
- .tokenType(TokenType.BEARER)
- .expired(false)
- .revoked(false)
- .build();
- tokenRepository.save(token);
- }
-
- private void revokeAllUserTokens(User user) {
- var validUserTokens = tokenRepository.findAllValidTokenByUser(user.getId());
- if (validUserTokens.isEmpty())
- return;
- validUserTokens.forEach(token -> {
- token.setExpired(true);
- token.setRevoked(true);
- });
- tokenRepository.saveAll(validUserTokens);
- }
-
- public void refreshToken(
- HttpServletRequest request,
- HttpServletResponse response
- ) throws IOException {
- final String authHeader = request.getHeader(HttpHeaders.AUTHORIZATION);
- final String refreshToken;
- final String userEmail;
- if (authHeader == null ||!authHeader.startsWith("Bearer ")) {
- return;
+ public AuthenticationResponse register(RegisterRequest request) {
+ var user = User.builder()
+ .firstname(request.getFirstname())
+ .lastname(request.getLastname())
+ .email(request.getEmail())
+ .password(passwordEncoder.encode(request.getPassword()))
+ .role(request.getRole())
+ .build();
+ var savedUser = repository.save(user);
+ var jwtToken = jwtService.generateToken(user);
+ var refreshToken = jwtService.generateRefreshToken(user);
+ saveUserToken(savedUser, jwtToken);
+ return AuthenticationResponse.builder()
+ .accessToken(jwtToken)
+ .refreshToken(refreshToken)
+ .build();
}
- refreshToken = authHeader.substring(7);
- userEmail = jwtService.extractUsername(refreshToken);
- if (userEmail != null) {
- var user = this.repository.findByEmail(userEmail)
- .orElseThrow();
- if (jwtService.isTokenValid(refreshToken, user)) {
- var accessToken = jwtService.generateToken(user);
+
+ public AuthenticationResponse authenticate(AuthenticationRequest request) {
+ authenticationManager.authenticate(
+ new UsernamePasswordAuthenticationToken(
+ request.getEmail(),
+ request.getPassword()
+ )
+ );
+ var user = repository.findByEmail(request.getEmail())
+ .orElseThrow();
+ var jwtToken = jwtService.generateToken(user);
+ var refreshToken = jwtService.generateRefreshToken(user);
revokeAllUserTokens(user);
- saveUserToken(user, accessToken);
- var authResponse = AuthenticationResponse.builder()
- .accessToken(accessToken)
+ saveUserToken(user, jwtToken);
+ return AuthenticationResponse.builder()
+ .accessToken(jwtToken)
.refreshToken(refreshToken)
.build();
- new ObjectMapper().writeValue(response.getOutputStream(), authResponse);
- }
}
- }
+
+ private void saveUserToken(User user, String jwtToken) {
+ var token = Token.builder()
+ .user(user)
+ .token(jwtToken)
+ .tokenType(TokenType.BEARER)
+ .expired(false)
+ .revoked(false)
+ .build();
+ tokenRepository.save(token);
+ }
+
+ private void revokeAllUserTokens(User user) {
+ var validUserTokens = tokenRepository.findAllValidTokenByUser(user.getId());
+ if (validUserTokens.isEmpty())
+ return;
+ validUserTokens.forEach(token -> {
+ token.setExpired(true);
+ token.setRevoked(true);
+ });
+ tokenRepository.saveAll(validUserTokens);
+ }
+
+ public void refreshToken(HttpServletRequest request, HttpServletResponse response) throws IOException {
+ final String authHeader = request.getHeader(HttpHeaders.AUTHORIZATION);
+ final String refreshToken;
+ final String userEmail;
+ if (authHeader == null || !authHeader.startsWith("Bearer ")) {
+ return;
+ }
+ refreshToken = authHeader.substring(7);
+ userEmail = jwtService.extractUsername(refreshToken);
+ if (userEmail != null) {
+ var user = this.repository.findByEmail(userEmail)
+ .orElseThrow();
+ if (jwtService.isTokenValid(refreshToken, user)) {
+ var accessToken = jwtService.generateToken(user);
+ revokeAllUserTokens(user);
+ saveUserToken(user, accessToken);
+ var authResponse = AuthenticationResponse.builder()
+ .accessToken(accessToken)
+ .refreshToken(refreshToken)
+ .build();
+ new ObjectMapper().writeValue(response.getOutputStream(), authResponse);
+ }
+ }
+ }
}
diff --git a/src/main/java/com/alibou/security/auth/RegisterRequest.java b/src/main/java/com/alibou/security/auth/RegisterRequest.java
index 4f51665..c8d0e97 100644
--- a/src/main/java/com/alibou/security/auth/RegisterRequest.java
+++ b/src/main/java/com/alibou/security/auth/RegisterRequest.java
@@ -12,9 +12,9 @@
@NoArgsConstructor
public class RegisterRequest {
- private String firstname;
- private String lastname;
- private String email;
- private String password;
- private Role role;
+ private String firstname;
+ private String lastname;
+ private String email;
+ private String password;
+ private Role role;
}
diff --git a/src/main/java/com/alibou/security/book/Book.java b/src/main/java/com/alibou/security/book/Book.java
index 3f041af..809e72e 100644
--- a/src/main/java/com/alibou/security/book/Book.java
+++ b/src/main/java/com/alibou/security/book/Book.java
@@ -32,22 +32,15 @@ public class Book {
private String isbn;
@CreatedDate
- @Column(
- nullable = false,
- updatable = false
- )
+ @Column(nullable = false, updatable = false)
private LocalDateTime createDate;
@LastModifiedDate
@Column(insertable = false)
private LocalDateTime lastModified;
-
@CreatedBy
- @Column(
- nullable = false,
- updatable = false
- )
+ @Column(nullable = false, updatable = false)
private Integer createdBy;
@LastModifiedBy
diff --git a/src/main/java/com/alibou/security/book/BookController.java b/src/main/java/com/alibou/security/book/BookController.java
index 4c45728..c878424 100644
--- a/src/main/java/com/alibou/security/book/BookController.java
+++ b/src/main/java/com/alibou/security/book/BookController.java
@@ -18,9 +18,7 @@ public class BookController {
private final BookService service;
@PostMapping
- public ResponseEntity> save(
- @RequestBody BookRequest request
- ) {
+ public ResponseEntity> save(@RequestBody BookRequest request) {
service.save(request);
return ResponseEntity.accepted().build();
}
diff --git a/src/main/java/com/alibou/security/config/ApplicationConfig.java b/src/main/java/com/alibou/security/config/ApplicationConfig.java
index ae71abf..423e1ad 100644
--- a/src/main/java/com/alibou/security/config/ApplicationConfig.java
+++ b/src/main/java/com/alibou/security/config/ApplicationConfig.java
@@ -20,35 +20,34 @@
@RequiredArgsConstructor
public class ApplicationConfig {
- private final UserRepository repository;
-
- @Bean
- public UserDetailsService userDetailsService() {
- return username -> repository.findByEmail(username)
- .orElseThrow(() -> new UsernameNotFoundException("User not found"));
- }
-
- @Bean
- public AuthenticationProvider authenticationProvider() {
- DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
- authProvider.setUserDetailsService(userDetailsService());
- authProvider.setPasswordEncoder(passwordEncoder());
- return authProvider;
- }
-
- @Bean
- public AuditorAware auditorAware() {
- return new ApplicationAuditAware();
- }
-
- @Bean
- public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
- return config.getAuthenticationManager();
- }
-
- @Bean
- public PasswordEncoder passwordEncoder() {
- return new BCryptPasswordEncoder();
- }
-
+ private final UserRepository repository;
+
+ @Bean
+ public UserDetailsService userDetailsService() {
+ return username -> repository.findByEmail(username)
+ .orElseThrow(() -> new UsernameNotFoundException("User not found"));
+ }
+
+// @Bean
+ public AuthenticationProvider authenticationProvider() {
+ DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
+ authProvider.setUserDetailsService(userDetailsService());
+ authProvider.setPasswordEncoder(passwordEncoder());
+ return authProvider;
+ }
+
+ @Bean
+ public AuditorAware auditorAware() {
+ return new ApplicationAuditAware();
+ }
+
+ @Bean
+ public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
+ return config.getAuthenticationManager();
+ }
+
+ @Bean
+ public PasswordEncoder passwordEncoder() {
+ return new BCryptPasswordEncoder();
+ }
}
diff --git a/src/main/java/com/alibou/security/config/JwtAuthenticationFilter.java b/src/main/java/com/alibou/security/config/JwtAuthenticationFilter.java
index d6e55d1..ac1800c 100644
--- a/src/main/java/com/alibou/security/config/JwtAuthenticationFilter.java
+++ b/src/main/java/com/alibou/security/config/JwtAuthenticationFilter.java
@@ -26,46 +26,44 @@
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {
- private final JwtService jwtService;
- private final UserDetailsService userDetailsService;
- private final TokenRepository tokenRepository;
+ private final JwtService jwtService;
+ private final UserDetailsService userDetailsService;
+ private final TokenRepository tokenRepository;
- @Override
- protected void doFilterInternal(
- @NonNull HttpServletRequest request,
- @NonNull HttpServletResponse response,
- @NonNull FilterChain filterChain
- ) throws ServletException, IOException {
- if (request.getServletPath().contains("/api/v1/auth")) {
- filterChain.doFilter(request, response);
- return;
+ @Override
+ protected void doFilterInternal(
+ @NonNull HttpServletRequest request,
+ @NonNull HttpServletResponse response,
+ @NonNull FilterChain filterChain
+ ) throws ServletException, IOException {
+ if (request.getServletPath().contains("/api/v1/auth")) {
+ filterChain.doFilter(request, response);
+ return;
+ }
+ final String authHeader = request.getHeader("Authorization");
+ final String jwt;
+ final String userEmail;
+ if (authHeader == null || !authHeader.startsWith("Bearer ")) {
+ filterChain.doFilter(request, response);
+ return;
+ }
+ jwt = authHeader.substring(7);
+ userEmail = jwtService.extractUsername(jwt);
+ if (userEmail != null && SecurityContextHolder.getContext().getAuthentication() == null) {
+ UserDetails userDetails = this.userDetailsService.loadUserByUsername(userEmail);
+ var isTokenValid = tokenRepository.findByToken(jwt)
+ .map(t -> !t.isExpired() && !t.isRevoked())
+ .orElse(false);
+ if (jwtService.isTokenValid(jwt, userDetails) && isTokenValid) {
+ UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
+ userDetails,
+ null,
+ userDetails.getAuthorities()
+ );
+ authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
+ SecurityContextHolder.getContext().setAuthentication(authToken);
+ }
+ }
+ filterChain.doFilter(request, response);
}
- final String authHeader = request.getHeader("Authorization");
- final String jwt;
- final String userEmail;
- if (authHeader == null ||!authHeader.startsWith("Bearer ")) {
- filterChain.doFilter(request, response);
- return;
- }
- jwt = authHeader.substring(7);
- userEmail = jwtService.extractUsername(jwt);
- if (userEmail != null && SecurityContextHolder.getContext().getAuthentication() == null) {
- UserDetails userDetails = this.userDetailsService.loadUserByUsername(userEmail);
- var isTokenValid = tokenRepository.findByToken(jwt)
- .map(t -> !t.isExpired() && !t.isRevoked())
- .orElse(false);
- if (jwtService.isTokenValid(jwt, userDetails) && isTokenValid) {
- UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
- userDetails,
- null,
- userDetails.getAuthorities()
- );
- authToken.setDetails(
- new WebAuthenticationDetailsSource().buildDetails(request)
- );
- SecurityContextHolder.getContext().setAuthentication(authToken);
- }
- }
- filterChain.doFilter(request, response);
- }
}
diff --git a/src/main/java/com/alibou/security/config/JwtService.java b/src/main/java/com/alibou/security/config/JwtService.java
index 9c1ed46..457a184 100644
--- a/src/main/java/com/alibou/security/config/JwtService.java
+++ b/src/main/java/com/alibou/security/config/JwtService.java
@@ -5,6 +5,7 @@
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
+
import java.security.Key;
import java.util.Date;
import java.util.HashMap;
@@ -18,78 +19,71 @@
@Service
public class JwtService {
- @Value("${application.security.jwt.secret-key}")
- private String secretKey;
- @Value("${application.security.jwt.expiration}")
- private long jwtExpiration;
- @Value("${application.security.jwt.refresh-token.expiration}")
- private long refreshExpiration;
-
- public String extractUsername(String token) {
- return extractClaim(token, Claims::getSubject);
- }
-
- public T extractClaim(String token, Function claimsResolver) {
- final Claims claims = extractAllClaims(token);
- return claimsResolver.apply(claims);
- }
-
- public String generateToken(UserDetails userDetails) {
- return generateToken(new HashMap<>(), userDetails);
- }
-
- public String generateToken(
- Map extraClaims,
- UserDetails userDetails
- ) {
- return buildToken(extraClaims, userDetails, jwtExpiration);
- }
-
- public String generateRefreshToken(
- UserDetails userDetails
- ) {
- return buildToken(new HashMap<>(), userDetails, refreshExpiration);
- }
-
- private String buildToken(
- Map extraClaims,
- UserDetails userDetails,
- long expiration
- ) {
- return Jwts
- .builder()
- .setClaims(extraClaims)
- .setSubject(userDetails.getUsername())
- .setIssuedAt(new Date(System.currentTimeMillis()))
- .setExpiration(new Date(System.currentTimeMillis() + expiration))
- .signWith(getSignInKey(), SignatureAlgorithm.HS256)
- .compact();
- }
-
- public boolean isTokenValid(String token, UserDetails userDetails) {
- final String username = extractUsername(token);
- return (username.equals(userDetails.getUsername())) && !isTokenExpired(token);
- }
-
- private boolean isTokenExpired(String token) {
- return extractExpiration(token).before(new Date());
- }
-
- private Date extractExpiration(String token) {
- return extractClaim(token, Claims::getExpiration);
- }
-
- private Claims extractAllClaims(String token) {
- return Jwts
- .parserBuilder()
- .setSigningKey(getSignInKey())
- .build()
- .parseClaimsJws(token)
- .getBody();
- }
-
- private Key getSignInKey() {
- byte[] keyBytes = Decoders.BASE64.decode(secretKey);
- return Keys.hmacShaKeyFor(keyBytes);
- }
+ @Value("${application.security.jwt.secret-key}")
+ private String secretKey;
+
+ @Value("${application.security.jwt.expiration}")
+ private long jwtExpiration;
+
+ @Value("${application.security.jwt.refresh-token.expiration}")
+ private long refreshExpiration;
+
+ public String extractUsername(String token) {
+ return extractClaim(token, Claims::getSubject);
+ }
+
+ public T extractClaim(String token, Function claimsResolver) {
+ final Claims claims = extractAllClaims(token);
+ return claimsResolver.apply(claims);
+ }
+
+ public String generateToken(UserDetails userDetails) {
+ return generateToken(new HashMap<>(), userDetails);
+ }
+
+ public String generateToken(Map extraClaims, UserDetails userDetails) {
+ return buildToken(extraClaims, userDetails, jwtExpiration);
+ }
+
+ public String generateRefreshToken(UserDetails userDetails) {
+ return buildToken(new HashMap<>(), userDetails, refreshExpiration);
+ }
+
+ private String buildToken(Map extraClaims, UserDetails userDetails, long expiration) {
+ return Jwts
+ .builder()
+ .setClaims(extraClaims)
+ .setSubject(userDetails.getUsername())
+ .setIssuedAt(new Date(System.currentTimeMillis()))
+ .setExpiration(new Date(System.currentTimeMillis() + expiration))
+ .signWith(getSigningKey(), SignatureAlgorithm.HS256)
+ .compact();
+ }
+
+ public boolean isTokenValid(String token, UserDetails userDetails) {
+ final String username = extractUsername(token);
+ return (username.equals(userDetails.getUsername())) && !isTokenExpired(token);
+ }
+
+ private boolean isTokenExpired(String token) {
+ return extractExpiration(token).before(new Date());
+ }
+
+ private Date extractExpiration(String token) {
+ return extractClaim(token, Claims::getExpiration);
+ }
+
+ private Claims extractAllClaims(String token) {
+ return Jwts
+ .parserBuilder()
+ .setSigningKey(getSigningKey())
+ .build()
+ .parseClaimsJws(token)
+ .getBody();
+ }
+
+ private Key getSigningKey() {
+ byte[] keyBytes = Decoders.BASE64.decode(secretKey);
+ return Keys.hmacShaKeyFor(keyBytes);
+ }
}
diff --git a/src/main/java/com/alibou/security/config/LogoutService.java b/src/main/java/com/alibou/security/config/LogoutService.java
index 0784565..ca7462c 100644
--- a/src/main/java/com/alibou/security/config/LogoutService.java
+++ b/src/main/java/com/alibou/security/config/LogoutService.java
@@ -13,27 +13,23 @@
@RequiredArgsConstructor
public class LogoutService implements LogoutHandler {
- private final TokenRepository tokenRepository;
+ private final TokenRepository tokenRepository;
- @Override
- public void logout(
- HttpServletRequest request,
- HttpServletResponse response,
- Authentication authentication
- ) {
- final String authHeader = request.getHeader("Authorization");
- final String jwt;
- if (authHeader == null ||!authHeader.startsWith("Bearer ")) {
- return;
+ @Override
+ public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
+ final String authHeader = request.getHeader("Authorization");
+ final String jwt;
+ if (authHeader == null || !authHeader.startsWith("Bearer ")) {
+ return;
+ }
+ jwt = authHeader.substring(7);
+ var storedToken = tokenRepository.findByToken(jwt)
+ .orElse(null);
+ if (storedToken != null) {
+ storedToken.setExpired(true);
+ storedToken.setRevoked(true);
+ tokenRepository.save(storedToken);
+ SecurityContextHolder.clearContext();
+ }
}
- jwt = authHeader.substring(7);
- var storedToken = tokenRepository.findByToken(jwt)
- .orElse(null);
- if (storedToken != null) {
- storedToken.setExpired(true);
- storedToken.setRevoked(true);
- tokenRepository.save(storedToken);
- SecurityContextHolder.clearContext();
- }
- }
}
diff --git a/src/main/java/com/alibou/security/config/SecurityConfiguration.java b/src/main/java/com/alibou/security/config/SecurityConfiguration.java
index e4aefe6..f0a1268 100644
--- a/src/main/java/com/alibou/security/config/SecurityConfiguration.java
+++ b/src/main/java/com/alibou/security/config/SecurityConfiguration.java
@@ -35,46 +35,33 @@
@EnableMethodSecurity
public class SecurityConfiguration {
- private static final String[] WHITE_LIST_URL = {"/api/v1/auth/**",
- "/v2/api-docs",
- "/v3/api-docs",
- "/v3/api-docs/**",
- "/swagger-resources",
- "/swagger-resources/**",
- "/configuration/ui",
- "/configuration/security",
- "/swagger-ui/**",
- "/webjars/**",
- "/swagger-ui.html"};
+ private static final String[] WHITE_LIST_URL = {
+ "/api/v1/auth/**",
+ };
+
private final JwtAuthenticationFilter jwtAuthFilter;
- private final AuthenticationProvider authenticationProvider;
private final LogoutHandler logoutHandler;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
- http
+ return http
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(req ->
- req.requestMatchers(WHITE_LIST_URL)
- .permitAll()
+ req.requestMatchers(WHITE_LIST_URL).permitAll()
.requestMatchers("/api/v1/management/**").hasAnyRole(ADMIN.name(), MANAGER.name())
.requestMatchers(GET, "/api/v1/management/**").hasAnyAuthority(ADMIN_READ.name(), MANAGER_READ.name())
.requestMatchers(POST, "/api/v1/management/**").hasAnyAuthority(ADMIN_CREATE.name(), MANAGER_CREATE.name())
.requestMatchers(PUT, "/api/v1/management/**").hasAnyAuthority(ADMIN_UPDATE.name(), MANAGER_UPDATE.name())
.requestMatchers(DELETE, "/api/v1/management/**").hasAnyAuthority(ADMIN_DELETE.name(), MANAGER_DELETE.name())
- .anyRequest()
- .authenticated()
+ .anyRequest().authenticated()
)
.sessionManagement(session -> session.sessionCreationPolicy(STATELESS))
- .authenticationProvider(authenticationProvider)
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)
.logout(logout ->
logout.logoutUrl("/api/v1/auth/logout")
.addLogoutHandler(logoutHandler)
.logoutSuccessHandler((request, response, authentication) -> SecurityContextHolder.clearContext())
)
- ;
-
- return http.build();
+ .build();
}
}
diff --git a/src/main/java/com/alibou/security/demo/AdminController.java b/src/main/java/com/alibou/security/demo/AdminController.java
index 18ede65..f6aa3bf 100644
--- a/src/main/java/com/alibou/security/demo/AdminController.java
+++ b/src/main/java/com/alibou/security/demo/AdminController.java
@@ -19,18 +19,21 @@ public class AdminController {
public String get() {
return "GET:: admin controller";
}
+
@PostMapping
@PreAuthorize("hasAuthority('admin:create')")
@Hidden
public String post() {
return "POST:: admin controller";
}
+
@PutMapping
@PreAuthorize("hasAuthority('admin:update')")
@Hidden
public String put() {
return "PUT:: admin controller";
}
+
@DeleteMapping
@PreAuthorize("hasAuthority('admin:delete')")
@Hidden
diff --git a/src/main/java/com/alibou/security/demo/DemoController.java b/src/main/java/com/alibou/security/demo/DemoController.java
index ee2c380..84ac9d4 100644
--- a/src/main/java/com/alibou/security/demo/DemoController.java
+++ b/src/main/java/com/alibou/security/demo/DemoController.java
@@ -11,9 +11,8 @@
@Hidden
public class DemoController {
- @GetMapping
- public ResponseEntity sayHello() {
- return ResponseEntity.ok("Hello from secured endpoint");
- }
-
+ @GetMapping
+ public ResponseEntity sayHello() {
+ return ResponseEntity.ok("Hello from secured endpoint");
+ }
}
diff --git a/src/main/java/com/alibou/security/demo/ManagementController.java b/src/main/java/com/alibou/security/demo/ManagementController.java
index a214a9b..de9b613 100644
--- a/src/main/java/com/alibou/security/demo/ManagementController.java
+++ b/src/main/java/com/alibou/security/demo/ManagementController.java
@@ -29,20 +29,22 @@ public class ManagementController {
responseCode = "403"
)
}
-
)
@GetMapping
public String get() {
return "GET:: management controller";
}
+
@PostMapping
public String post() {
return "POST:: management controller";
}
+
@PutMapping
public String put() {
return "PUT:: management controller";
}
+
@DeleteMapping
public String delete() {
return "DELETE:: management controller";
diff --git a/src/main/java/com/alibou/security/token/Token.java b/src/main/java/com/alibou/security/token/Token.java
index 71f3571..31896df 100644
--- a/src/main/java/com/alibou/security/token/Token.java
+++ b/src/main/java/com/alibou/security/token/Token.java
@@ -22,21 +22,21 @@
@Entity
public class Token {
- @Id
- @GeneratedValue
- public Integer id;
+ @Id
+ @GeneratedValue
+ public Integer id;
- @Column(unique = true)
- public String token;
+ @Column(unique = true)
+ public String token;
- @Enumerated(EnumType.STRING)
- public TokenType tokenType = TokenType.BEARER;
+ @Enumerated(EnumType.STRING)
+ public TokenType tokenType = TokenType.BEARER;
- public boolean revoked;
+ public boolean revoked;
- public boolean expired;
+ public boolean expired;
- @ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "user_id")
- public User user;
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "user_id")
+ public User user;
}
diff --git a/src/main/java/com/alibou/security/token/TokenRepository.java b/src/main/java/com/alibou/security/token/TokenRepository.java
index 48235d8..b5e91ec 100644
--- a/src/main/java/com/alibou/security/token/TokenRepository.java
+++ b/src/main/java/com/alibou/security/token/TokenRepository.java
@@ -2,17 +2,18 @@
import java.util.List;
import java.util.Optional;
+
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
public interface TokenRepository extends JpaRepository {
- @Query(value = """
- select t from Token t inner join User u\s
- on t.user.id = u.id\s
- where u.id = :id and (t.expired = false or t.revoked = false)\s
- """)
- List findAllValidTokenByUser(Integer id);
+ @Query(value = """
+ select t from Token t inner join User u\s
+ on t.user.id = u.id\s
+ where u.id = :id and (t.expired = false or t.revoked = false)
+ """)
+ List findAllValidTokenByUser(Integer id);
- Optional findByToken(String token);
+ Optional findByToken(String token);
}
diff --git a/src/main/java/com/alibou/security/token/TokenType.java b/src/main/java/com/alibou/security/token/TokenType.java
index 82a8cff..3513703 100644
--- a/src/main/java/com/alibou/security/token/TokenType.java
+++ b/src/main/java/com/alibou/security/token/TokenType.java
@@ -1,5 +1,5 @@
package com.alibou.security.token;
public enum TokenType {
- BEARER
+ BEARER
}
diff --git a/src/main/java/com/alibou/security/user/Permission.java b/src/main/java/com/alibou/security/user/Permission.java
index 16ae8b4..230a6c1 100644
--- a/src/main/java/com/alibou/security/user/Permission.java
+++ b/src/main/java/com/alibou/security/user/Permission.java
@@ -3,6 +3,7 @@
import lombok.Getter;
import lombok.RequiredArgsConstructor;
+@Getter
@RequiredArgsConstructor
public enum Permission {
@@ -13,10 +14,7 @@ public enum Permission {
MANAGER_READ("management:read"),
MANAGER_UPDATE("management:update"),
MANAGER_CREATE("management:create"),
- MANAGER_DELETE("management:delete")
+ MANAGER_DELETE("management:delete");
- ;
-
- @Getter
private final String permission;
}
diff --git a/src/main/java/com/alibou/security/user/Role.java b/src/main/java/com/alibou/security/user/Role.java
index 0ff9bd1..4c6ae21 100644
--- a/src/main/java/com/alibou/security/user/Role.java
+++ b/src/main/java/com/alibou/security/user/Role.java
@@ -18,42 +18,40 @@
import static com.alibou.security.user.Permission.MANAGER_READ;
import static com.alibou.security.user.Permission.MANAGER_UPDATE;
+@Getter
@RequiredArgsConstructor
public enum Role {
- USER(Collections.emptySet()),
- ADMIN(
- Set.of(
- ADMIN_READ,
- ADMIN_UPDATE,
- ADMIN_DELETE,
- ADMIN_CREATE,
- MANAGER_READ,
- MANAGER_UPDATE,
- MANAGER_DELETE,
- MANAGER_CREATE
- )
- ),
- MANAGER(
- Set.of(
- MANAGER_READ,
- MANAGER_UPDATE,
- MANAGER_DELETE,
- MANAGER_CREATE
- )
- )
-
- ;
-
- @Getter
- private final Set permissions;
-
- public List getAuthorities() {
- var authorities = getPermissions()
- .stream()
- .map(permission -> new SimpleGrantedAuthority(permission.getPermission()))
- .collect(Collectors.toList());
- authorities.add(new SimpleGrantedAuthority("ROLE_" + this.name()));
- return authorities;
- }
+ USER(Collections.emptySet()),
+ ADMIN(
+ Set.of(
+ ADMIN_READ,
+ ADMIN_UPDATE,
+ ADMIN_DELETE,
+ ADMIN_CREATE,
+ MANAGER_READ,
+ MANAGER_UPDATE,
+ MANAGER_DELETE,
+ MANAGER_CREATE
+ )
+ ),
+ MANAGER(
+ Set.of(
+ MANAGER_READ,
+ MANAGER_UPDATE,
+ MANAGER_DELETE,
+ MANAGER_CREATE
+ )
+ );
+
+ private final Set permissions;
+
+ public List getAuthorities() {
+ var authorities = getPermissions()
+ .stream()
+ .map(permission -> new SimpleGrantedAuthority(permission.getPermission()))
+ .collect(Collectors.toList());
+ authorities.add(new SimpleGrantedAuthority("ROLE_" + this.name()));
+ return authorities;
+ }
}
diff --git a/src/main/java/com/alibou/security/user/User.java b/src/main/java/com/alibou/security/user/User.java
index bc4e086..2dc068b 100644
--- a/src/main/java/com/alibou/security/user/User.java
+++ b/src/main/java/com/alibou/security/user/User.java
@@ -8,8 +8,10 @@
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
+
import java.util.Collection;
import java.util.List;
+
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
@@ -26,52 +28,32 @@
@Table(name = "_user")
public class User implements UserDetails {
- @Id
- @GeneratedValue
- private Integer id;
- private String firstname;
- private String lastname;
- private String email;
- private String password;
-
- @Enumerated(EnumType.STRING)
- private Role role;
-
- @OneToMany(mappedBy = "user")
- private List tokens;
-
- @Override
- public Collection extends GrantedAuthority> getAuthorities() {
- return role.getAuthorities();
- }
-
- @Override
- public String getPassword() {
- return password;
- }
-
- @Override
- public String getUsername() {
- return email;
- }
-
- @Override
- public boolean isAccountNonExpired() {
- return true;
- }
-
- @Override
- public boolean isAccountNonLocked() {
- return true;
- }
-
- @Override
- public boolean isCredentialsNonExpired() {
- return true;
- }
-
- @Override
- public boolean isEnabled() {
- return true;
- }
+ @Id
+ @GeneratedValue
+ private Integer id;
+ private String firstname;
+ private String lastname;
+ private String email;
+ private String password;
+
+ @Enumerated(EnumType.STRING)
+ private Role role;
+
+ @OneToMany(mappedBy = "user")
+ private List tokens;
+
+ @Override
+ public Collection extends GrantedAuthority> getAuthorities() {
+ return role.getAuthorities();
+ }
+
+ @Override
+ public String getPassword() {
+ return password;
+ }
+
+ @Override
+ public String getUsername() {
+ return email;
+ }
}
diff --git a/src/main/java/com/alibou/security/user/UserController.java b/src/main/java/com/alibou/security/user/UserController.java
index 415be48..0484154 100644
--- a/src/main/java/com/alibou/security/user/UserController.java
+++ b/src/main/java/com/alibou/security/user/UserController.java
@@ -17,10 +17,7 @@ public class UserController {
private final UserService service;
@PatchMapping
- public ResponseEntity> changePassword(
- @RequestBody ChangePasswordRequest request,
- Principal connectedUser
- ) {
+ public ResponseEntity> changePassword(@RequestBody ChangePasswordRequest request, Principal connectedUser) {
service.changePassword(request, connectedUser);
return ResponseEntity.ok().build();
}
diff --git a/src/main/java/com/alibou/security/user/UserRepository.java b/src/main/java/com/alibou/security/user/UserRepository.java
index a979ad6..7868c30 100644
--- a/src/main/java/com/alibou/security/user/UserRepository.java
+++ b/src/main/java/com/alibou/security/user/UserRepository.java
@@ -1,10 +1,10 @@
package com.alibou.security.user;
import java.util.Optional;
+
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository {
- Optional findByEmail(String email);
-
+ Optional findByEmail(String email);
}
diff --git a/src/main/java/com/alibou/security/user/UserService.java b/src/main/java/com/alibou/security/user/UserService.java
index a17181d..84d4876 100644
--- a/src/main/java/com/alibou/security/user/UserService.java
+++ b/src/main/java/com/alibou/security/user/UserService.java
@@ -13,6 +13,7 @@ public class UserService {
private final PasswordEncoder passwordEncoder;
private final UserRepository repository;
+
public void changePassword(ChangePasswordRequest request, Principal connectedUser) {
var user = (User) ((UsernamePasswordAuthenticationToken) connectedUser).getPrincipal();
@@ -21,6 +22,7 @@ public void changePassword(ChangePasswordRequest request, Principal connectedUse
if (!passwordEncoder.matches(request.getCurrentPassword(), user.getPassword())) {
throw new IllegalStateException("Wrong password");
}
+
// check if the two new passwords are the same
if (!request.getNewPassword().equals(request.getConfirmationPassword())) {
throw new IllegalStateException("Password are not the same");
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 71b71d1..d65e97b 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -1,8 +1,8 @@
spring:
datasource:
url: jdbc:postgresql://localhost:5432/jwt_security
- username: username
- password: password
+ username: postgres
+ password: postgres
driver-class-name: org.postgresql.Driver
jpa:
hibernate:
diff --git a/src/test/java/com/alibou/security/SecurityApplicationTests.java b/src/test/java/com/alibou/security/SecurityApplicationTests.java
index 6e2729f..570c8bc 100644
--- a/src/test/java/com/alibou/security/SecurityApplicationTests.java
+++ b/src/test/java/com/alibou/security/SecurityApplicationTests.java
@@ -6,8 +6,8 @@
@SpringBootTest
class SecurityApplicationTests {
- @Test
- void contextLoads() {
- }
+ @Test
+ void contextLoads() {
+ }
}