Complete Guide: Spring Boot Security with JWT Authentication (Latest Version) - Nandu-Chopade/Spring-security-complete-guide GitHub Wiki
com.example.securityjwt
├── config
│ └── SecurityConfig.java
├── controller
│ └── AuthController.java
├── jwt
│ ├── JwtAuthFilter.java
│ └── JwtService.java
├── model
│ └── UserInfo.java
├── repository
│ └── UserInfoRepository.java
├── service
│ └── UserInfoUserDetailsService.java
└── SecurityJwtApplication.java
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
@Entity
public class UserInfo {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
private String email;
private String password;
private String roles; // e.g., ROLE_ADMIN, ROLE_USER
// Getters and setters
}
public interface UserInfoRepository extends JpaRepository<UserInfo, Integer> {
Optional<UserInfo> findByEmail(String email);
}
@Service
public class UserInfoUserDetailsService implements UserDetailsService {
@Autowired
private UserInfoRepository repository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserInfo user = repository.findByEmail(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
return User.withUsername(user.getEmail())
.password(user.getPassword())
.roles(user.getRoles())
.build();
}
}
@Component
public class JwtService {
private String secret = "secretkey"; // use env variable in prod
public String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10))
.signWith(SignatureAlgorithm.HS256, secret)
.compact();
}
public String extractUsername(String token) {
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody().getSubject();
}
public boolean validateToken(String token, UserDetails userDetails) {
String username = extractUsername(token);
return username.equals(userDetails.getUsername());
}
}
@Component
public class JwtAuthFilter extends OncePerRequestFilter {
@Autowired
private JwtService jwtService;
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String authHeader = request.getHeader("Authorization");
String token = null;
String username = null;
if (authHeader != null && authHeader.startsWith("Bearer ")) {
token = authHeader.substring(7);
username = jwtService.extractUsername(token);
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (jwtService.validateToken(token, userDetails)) {
UsernamePasswordAuthenticationToken authToken =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authToken);
}
}
filterChain.doFilter(request, response);
}
}
@Configuration @EnableWebSecurity public class SecurityConfig {
@Autowired
private JwtAuthFilter authFilter;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public UserDetailsService userDetailsService() {
return new UserInfoUserDetailsService();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
@Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService());
provider.setPasswordEncoder(passwordEncoder());
return provider;
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/auth/**", "/swagger-ui/**", "/v3/api-docs/**").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN")
.requestMatchers("/manager/**").hasRole("MANAGER")
.requestMatchers("/employee/**").hasRole("EMPLOYEE")
.anyRequest().authenticated())
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authenticationProvider(authenticationProvider())
.addFilterBefore(authFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(List.of("http://localhost:3000", "https://yourdomain.com"));
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
config.setAllowedHeaders(List.of("*").stream().toList());
config.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return source;
}
}
@RestController
@RequestMapping("/auth")
public class AuthController {
@Autowired
private AuthenticationManager authManager;
@Autowired
private JwtService jwtService;
@PostMapping("/login")
public ResponseEntity<?> authenticate(@RequestBody AuthRequest authRequest) {
Authentication authentication = authManager.authenticate(
new UsernamePasswordAuthenticationToken(authRequest.getEmail(), authRequest.getPassword())
);
if (authentication.isAuthenticated()) {
String token = jwtService.generateToken(authRequest.getEmail());
return ResponseEntity.ok(Collections.singletonMap("token", token));
} else {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid credentials");
}
}
}
public class AuthRequest {
private String email;
private String password;
// Getters and setters
}
-
Login using
POST /auth/login
- Response will return a token like:
{ "token": "eyJhbGciOiJIUzI1NiJ9..." }
- Use this in headers to access secure endpoints:
Authorization: Bearer <your_token>
- You can extend this setup with role-based authorization.
- Use environment variables for JWT secret.
- Use exception handlers for invalid token responses.
Need a full GitHub repo of this project with React frontend? Let me know!