Module 3: Build Sprint 2
Understanding Your Second Ticket
Learn how to approach your second ticket in the Labs project and understand the development workflow.
Second Ticket Details
View your second ticket details and requirements on GitHub:
Second Ticket DocumentationApproaching Your Second Feature
Learn how to implement JWT authentication and RESTful endpoints for the Bloom Coder Assignment App.
Implementation Checklist
- Add JWT dependency and implement JWT utilities
- Create JWT filter for request authentication
- Update Security Configuration for JWT
- Implement User Repository
- Update UserDetailsService implementation
- Configure application properties
- Implement RESTful endpoints
JWT Implementation Concepts
// Example of JWT token structure and claims @Component public class JwtUtil { // Token expiration time in seconds private static final long EXPIRATION_TIME = 86400000; // 24 hours // Secret key for signing tokens @Value("${jwt.secret}") private String secret; // Example of how to structure token claims public String generateToken(UserDetails userDetails) { // Claims are key-value pairs stored in the token Mapclaims = new HashMap<>(); // Example of adding user roles to claims claims.put("roles", userDetails.getAuthorities() .stream() .map(auth -> auth.getAuthority()) .collect(Collectors.toList())); // Token generation with claims return Jwts.builder() .setClaims(claims) .setSubject(userDetails.getUsername()) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)) .signWith(SignatureAlgorithm.HS256, secret) .compact(); } }
Security Configuration Concepts
// Example of configuring security for JWT authentication @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; @Autowired private UserDetailsService jwtUserDetailsService; @Autowired private JwtRequestFilter jwtRequestFilter; @Override protected void configure(HttpSecurity httpSecurity) throws Exception { httpSecurity // Disable CSRF for API endpoints .csrf().disable() // Allow specific endpoints without authentication .authorizeRequests() .antMatchers("/api/auth/**").permitAll() .anyRequest().authenticated() .and() // Add exception handling .exceptionHandling() .authenticationEntryPoint(jwtAuthenticationEntryPoint) .and() // Set session management to stateless .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS); // Add JWT filter httpSecurity.addFilterBefore( jwtRequestFilter, UsernamePasswordAuthenticationFilter.class ); } }
RESTful API Best Practices
Learn about RESTful API design principles and best practices for your Spring Boot application.
API Design Principles
- Resource-based URLs - Organize around resources, not actions
- HTTP Methods - Use proper HTTP methods (GET, POST, PUT, DELETE)
- Status Codes - Return appropriate HTTP status codes
- Versioning - Consider API versioning strategy
- Error Handling - Provide clear error messages and codes
- Documentation - Document your API with Swagger or similar tools
Example REST Controller
@RestController @RequestMapping("/api/assignments") public class AssignmentController { @Autowired private AssignmentService assignmentService; @GetMapping public ResponseEntity> getAllAssignments() { List
assignments = assignmentService.findAll(); return ResponseEntity.ok(assignments); } @GetMapping("/{id}") public ResponseEntity getAssignmentById(@PathVariable Long id) { try { AssignmentDTO assignment = assignmentService.findById(id); return ResponseEntity.ok(assignment); } catch (ResourceNotFoundException e) { return ResponseEntity.notFound().build(); } } @PostMapping public ResponseEntity createAssignment( @Valid @RequestBody AssignmentDTO assignmentDTO) { AssignmentDTO created = assignmentService.create(assignmentDTO); URI location = ServletUriComponentsBuilder .fromCurrentRequest() .path("/{id}") .buildAndExpand(created.getId()) .toUri(); return ResponseEntity.created(location).body(created); } @PutMapping("/{id}") public ResponseEntity updateAssignment( @PathVariable Long id, @Valid @RequestBody AssignmentDTO assignmentDTO) { try { AssignmentDTO updated = assignmentService.update(id, assignmentDTO); return ResponseEntity.ok(updated); } catch (ResourceNotFoundException e) { return ResponseEntity.notFound().build(); } } @DeleteMapping("/{id}") public ResponseEntity deleteAssignment(@PathVariable Long id) { try { assignmentService.delete(id); return ResponseEntity.noContent().build(); } catch (ResourceNotFoundException e) { return ResponseEntity.notFound().build(); } } }
Error Handling in Spring Boot
Learn about implementing global exception handling and error responses in your Spring Boot application.
Exception Handling Best Practices
- Global Exception Handler - Use @ControllerAdvice for centralized exception handling
- Custom Exceptions - Define domain-specific exceptions
- Error Response Structure - Return consistent error response format
- Logging - Log exceptions for debugging and monitoring
- Validation Errors - Handle validation errors consistently
Example Global Exception Handler
@ControllerAdvice public class GlobalExceptionHandler { private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class); @ExceptionHandler(ResourceNotFoundException.class) public ResponseEntityhandleResourceNotFoundException( ResourceNotFoundException ex, WebRequest request) { ErrorResponse errorResponse = new ErrorResponse( HttpStatus.NOT_FOUND.value(), ex.getMessage(), request.getDescription(false), new Date() ); return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND); } @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity handleValidationExceptions( MethodArgumentNotValidException ex, WebRequest request) { Map errors = new HashMap<>(); ex.getBindingResult().getAllErrors().forEach((error) -> { String fieldName = ((FieldError) error).getField(); String errorMessage = error.getDefaultMessage(); errors.put(fieldName, errorMessage); }); ErrorResponse errorResponse = new ErrorResponse( HttpStatus.BAD_REQUEST.value(), "Validation Error", errors.toString(), new Date() ); return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST); } @ExceptionHandler(Exception.class) public ResponseEntity handleGlobalException( Exception ex, WebRequest request) { logger.error("Unhandled exception", ex); ErrorResponse errorResponse = new ErrorResponse( HttpStatus.INTERNAL_SERVER_ERROR.value(), "An unexpected error occurred", request.getDescription(false), new Date() ); return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR); } }