I am developing and spring-boot application and to do some testing regarding the functionality of the jwt i created 2 endpoints one that returns all the information regarding the user and the second one that returns all the users in the db.
when i log in using postman i get the token but when i put it in the bearer and try to make use of the endpoints i always receive a forbidden only exception being when the token has expired for some reason it returns an 200 but does not return any of the information.
here is the code
ApplicationConfiguration
@Configuration
public class ApplicationConfiguration {
private final EmployeeRepository employeeRepository;
private final ClientRepository clientRepository;
public ApplicationConfiguration(EmployeeRepository employeeRepository, ClientRepository clientRepository) {
this.employeeRepository = employeeRepository;
this.clientRepository = clientRepository;
}
@Bean
public UserDetailsService employeeDetailsService() {
return username -> employeeRepository.findByEmail(username)
.orElseThrow(() -> new UsernameNotFoundException("Employee not found"));
}
@Bean
public UserDetailsService clientDetailsService() {
return username -> clientRepository.findByEmail(username)
.orElseThrow(() -> new UsernameNotFoundException("Client not found"));
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
private DaoAuthenticationProvider createDaoAuthenticationProvider(UserDetailsService userDetailsService) {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
@Bean
public AuthenticationProvider employeeAuthenticationProvider() {
return createDaoAuthenticationProvider(employeeDetailsService());
}
@Bean
public AuthenticationProvider clientAuthenticationProvider() {
return createDaoAuthenticationProvider(clientDetailsService());
}
}
JwtAuthenticationFilter
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final HandlerExceptionResolver handlerExceptionResolver;
private final JwtService jwtService;
private final UserDetailsService employeeDetailsService;
private final UserDetailsService clientDetailsService;
public JwtAuthenticationFilter(
JwtService jwtService,
@Qualifier("employeeDetailsService") UserDetailsService employeeDetailsService,
@Qualifier("clientDetailsService") UserDetailsService clientDetailsService,
HandlerExceptionResolver handlerExceptionResolver
) {
this.jwtService = jwtService;
this.employeeDetailsService = employeeDetailsService;
this.clientDetailsService = clientDetailsService;
this.handlerExceptionResolver = handlerExceptionResolver;
}
@Override
protected void doFilterInternal(
@NonNull HttpServletRequest request,
@NonNull HttpServletResponse response,
@NonNull FilterChain filterChain
) throws ServletException, IOException {
final String authHeader = request.getHeader("Authorization");
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
filterChain.doFilter(request, response);
return;
}
try {
final String jwt = authHeader.substring(7);
final String userEmail = jwtService.extractUsername(jwt);
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (userEmail != null && authentication == null) {
UserDetails userDetails = null;
// Determinar qué UserDetailsService utilizar basado en el tipo de usuario
if (userEmail.startsWith("EMPLOYEE_")) {
userDetails = this.employeeDetailsService.loadUserByUsername(userEmail);
} else if (userEmail.startsWith("CLIENT_")) {
userDetails = this.clientDetailsService.loadUserByUsername(userEmail);
}
if (userDetails != null && jwtService.isTokenValid(jwt, userDetails)) {
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
userDetails,
null,
userDetails.getAuthorities()
);
authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authToken);
}
}
filterChain.doFilter(request, response);
} catch (Exception exception) {
handlerExceptionResolver.resolveException(request, response, null, exception);
}
}
}
SecurityConfiguration
@Configuration
@EnableWebSecurity
public class SecurityConfiguration {
private final JwtAuthenticationFilter jwtAuthenticationFilter;
private final AuthenticationProvider employeeAuthenticationProvider;
private final AuthenticationProvider clientAuthenticationProvider;
public SecurityConfiguration(
JwtAuthenticationFilter jwtAuthenticationFilter,
AuthenticationProvider employeeAuthenticationProvider,
AuthenticationProvider clientAuthenticationProvider
) {
this.jwtAuthenticationFilter = jwtAuthenticationFilter;
this.employeeAuthenticationProvider = employeeAuthenticationProvider;
this.clientAuthenticationProvider = clientAuthenticationProvider;
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(authorizeRequests -> authorizeRequests
.requestMatchers("/auth/**").permitAll()
.anyRequest().authenticated()
)
.sessionManagement(sessionManagement -> sessionManagement
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
)
.authenticationProvider(employeeAuthenticationProvider)
.authenticationProvider(clientAuthenticationProvider)
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(List.of("http://localhost:8005"));
configuration.setAllowedMethods(List.of("GET","POST"));
configuration.setAllowedHeaders(List.of("Authorization","Content-Type"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**",configuration);
return source;
}
@Bean
public ProviderManager authenticationManager() {
return new ProviderManager(Arrays.asList(employeeAuthenticationProvider, clientAuthenticationProvider));
}
}
the service of the endpoints
@Service
public class UserService {
private final EmployeeRepository employeeRepository;
public UserService(EmployeeRepository employeeRepository) {
this.employeeRepository = employeeRepository;
}
public List<Employee> allUsers() {
return employeeRepository.findAll();
}
}
and the controller
@RequestMapping("/users")
@RestController
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping("/me")
public ResponseEntity<Employee> authenticatedUser() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Employee currentUser = (Employee) authentication.getPrincipal();
return ResponseEntity.ok(currentUser);
}
@GetMapping("/")
public ResponseEntity<List<Employee>> allUsers() {
try {
List <Employee> users = userService.allUsers();
return ResponseEntity.ok(users);
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
}
}
}
JwtService
@Service
public class JwtService {
private final UserDetailsService employeeDetailsService;
private final UserDetailsService clientDetailsService;
public JwtService(@Qualifier("employeeDetailsService") UserDetailsService employeeDetailsService,
@Qualifier("clientDetailsService") UserDetailsService clientDetailsService) {
this.employeeDetailsService = employeeDetailsService;
this.clientDetailsService = clientDetailsService;
}
@Value("${security.jwt.secret-key}")
private String secretKey;
@Value("${security.jwt.expiration-time}")
private long jwtExpiration;
public String extractUsername(String token) {
return extractClaim(token, Claims::getSubject);
}
public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
final Claims claims = extractAllClaims(token);
return claimsResolver.apply(claims);
}
public String generateToken(UserDetails userDetails) {
return generateToken(new HashMap<>(), userDetails);
}
public String generateToken(Map<String, Object> extraClaims, UserDetails userDetails) {
return buildToken(extraClaims, userDetails, jwtExpiration);
}
public long getExpirationTime() {
return jwtExpiration;
}
private String buildToken(
Map<String, Object> 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 String refreshJwt(String expiredToken) {
String username = extractUsername(expiredToken);
UserDetails userDetails;
if (username != null && username.startsWith("EMPLOYEE_")) {
userDetails = employeeDetailsService.loadUserByUsername(username);
} else if (username != null && username.startsWith("CLIENT_")) {
userDetails = clientDetailsService.loadUserByUsername(username);
} else {
throw new RuntimeException("Invalid token or user not found");
}
if (username != null && isTokenExpired(expiredToken)) {
Map<String, Object> extraClaims = new HashMap<>();
String newToken = generateToken(extraClaims, userDetails);
long newExpiration = jwtExpiration;
try {
Claims claims = extractAllClaims(newToken);
Date expirationDate = claims.getExpiration();
if (expirationDate != null) {
newExpiration = expirationDate.getTime() - System.currentTimeMillis();
}
} catch (Exception e) {
throw new RuntimeException("Error extracting expiration time from new token");
}
return newToken + "|" + newExpiration;
} else {
throw new RuntimeException("Invalid token or user not found");
}
}
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);
}
}
i set up the CORS and added code to see if the JWT was being build up from the pertinent data but frankly i dont know what else to do the service and controller are quite simple as i just wanted to see if the token was working properly and are not designed to be kept in the final code
End is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.