乐闻世界logo
搜索文章和话题

How to implement security authentication in Spring Boot with Spring Security?

3月6日 21:59

Spring Boot + Spring Security Authentication

Spring Security Core Features

  • Authentication: Verify user identity
  • Authorization: Control user access permissions
  • Protection: CSRF, session fixation attack prevention
  • Encryption: Password encryption storage

Basic Integration

1. Add Dependencies

xml
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.12.3</version> </dependency> </dependencies>

2. Basic Security Configuration

java
@Configuration @EnableWebSecurity @EnableMethodSecurity(prePostEnabled = true) public class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .csrf(csrf -> csrf.disable()) .authorizeHttpRequests(auth -> auth .requestMatchers("/", "/login", "/register", "/public/**").permitAll() .requestMatchers("/admin/**").hasRole("ADMIN") .requestMatchers("/user/**").hasAnyRole("USER", "ADMIN") .anyRequest().authenticated() ) .formLogin(form -> form .loginPage("/login") .defaultSuccessUrl("/home", false) .failureUrl("/login?error=true") .permitAll() ) .logout(logout -> logout .logoutUrl("/logout") .logoutSuccessUrl("/login?logout=true") .invalidateHttpSession(true) .deleteCookies("JSESSIONID") .permitAll() ); return http.build(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }

In-Memory User Authentication

java
@Configuration public class InMemoryUserConfig { @Bean public UserDetailsService userDetailsService() { UserDetails user = User.builder() .username("user") .password(new BCryptPasswordEncoder().encode("password")) .roles("USER") .build(); UserDetails admin = User.builder() .username("admin") .password(new BCryptPasswordEncoder().encode("admin")) .roles("ADMIN", "USER") .build(); return new InMemoryUserDetailsManager(user, admin); } }

Database User Authentication

1. User Entity

java
@Entity @Table(name = "users") @Data public class User implements UserDetails { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(unique = true, nullable = false) private String username; @Column(nullable = false) private String password; private boolean enabled = true; @ManyToMany(fetch = FetchType.EAGER) @JoinTable( name = "user_roles", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id") ) private Set<Role> roles = new HashSet<>(); @Override public Collection<? extends GrantedAuthority> getAuthorities() { return roles.stream() .map(role -> new SimpleGrantedAuthority("ROLE_" + role.getName())) .collect(Collectors.toList()); } }

2. Custom UserDetailsService

java
@Service @RequiredArgsConstructor public class CustomUserDetailsService implements UserDetailsService { private final UserRepository userRepository; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = userRepository.findByUsername(username) .orElseThrow(() -> new UsernameNotFoundException("User not found: " + username)); return org.springframework.security.core.userdetails.User.builder() .username(user.getUsername()) .password(user.getPassword()) .roles(user.getRoles().stream() .map(Role::getName) .toArray(String[]::new)) .build(); } }

JWT Authentication Implementation

1. JWT Utility Class

java
@Component public class JwtTokenProvider { @Value("${jwt.secret}") private String jwtSecret; @Value("${jwt.expiration}") private long jwtExpiration; private final SecretKey key; public JwtTokenProvider() { this.key = Keys.secretKeyFor(SignatureAlgorithm.HS256); } public String generateToken(Authentication authentication) { UserDetails userDetails = (UserDetails) authentication.getPrincipal(); Date now = new Date(); Date expiryDate = new Date(now.getTime() + jwtExpiration); return Jwts.builder() .setSubject(userDetails.getUsername()) .claim("roles", userDetails.getAuthorities().stream() .map(GrantedAuthority::getAuthority) .collect(Collectors.toList())) .setIssuedAt(now) .setExpiration(expiryDate) .signWith(key) .compact(); } public String getUsernameFromToken(String token) { Claims claims = Jwts.parserBuilder() .setSigningKey(key) .build() .parseClaimsJws(token) .getBody(); return claims.getSubject(); } public boolean validateToken(String token) { try { Jwts.parserBuilder() .setSigningKey(key) .build() .parseClaimsJws(token); return true; } catch (JwtException | IllegalArgumentException e) { return false; } } }

2. JWT Authentication Filter

java
@Component @RequiredArgsConstructor public class JwtAuthenticationFilter extends OncePerRequestFilter { private final JwtTokenProvider tokenProvider; private final CustomUserDetailsService userDetailsService; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { try { String jwt = getJwtFromRequest(request); if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) { String username = tokenProvider.getUsernameFromToken(jwt); UserDetails userDetails = userDetailsService.loadUserByUsername(username); UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities() ); authentication.setDetails( new WebAuthenticationDetailsSource().buildDetails(request) ); SecurityContextHolder.getContext().setAuthentication(authentication); } } catch (Exception e) { logger.error("Cannot set user authentication: {}", e.getMessage()); } filterChain.doFilter(request, response); } private String getJwtFromRequest(HttpServletRequest request) { String bearerToken = request.getHeader("Authorization"); if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) { return bearerToken.substring(7); } return null; } }

3. JWT Security Configuration

java
@Configuration @EnableWebSecurity @RequiredArgsConstructor public class JwtSecurityConfig { private final JwtAuthenticationFilter jwtAuthenticationFilter; @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .csrf(csrf -> csrf.disable()) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS) ) .authorizeHttpRequests(auth -> auth .requestMatchers("/api/auth/**").permitAll() .requestMatchers("/api/admin/**").hasRole("ADMIN") .anyRequest().authenticated() ) .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }

Method-Level Security

java
@RestController @RequestMapping("/api/users") @RequiredArgsConstructor public class UserController { private final UserService userService; @GetMapping("/me") @PreAuthorize("isAuthenticated()") public ResponseEntity<User> getCurrentUser(@AuthenticationPrincipal UserDetails userDetails) { return ResponseEntity.ok(userService.findByUsername(userDetails.getUsername())); } @GetMapping("/{id}") @PreAuthorize("hasRole('ADMIN') or @userSecurity.hasUserId(authentication, #id)") public ResponseEntity<User> getUserById(@PathVariable Long id) { return ResponseEntity.ok(userService.findById(id)); } @DeleteMapping("/{id}") @PreAuthorize("hasRole('ADMIN')") public ResponseEntity<?> deleteUser(@PathVariable Long id) { userService.deleteUser(id); return ResponseEntity.ok().build(); } }

Summary

Authentication TypeUse CaseProsCons
SessionTraditional Web AppsSimple, matureNot suitable for distributed
JWTREST API, MobileStateless, scalableToken cannot be revoked
OAuth2Third-party loginStandardized, secureComplex implementation
LDAP/ADEnterpriseCentralized managementRequires LDAP server

Security Recommendations:

  1. Always use HTTPS
  2. Use BCrypt for password encryption
  3. Enable CSRF protection (Web apps)
  4. Set reasonable session timeout
  5. Implement password strength validation
  6. Add login failure lockout mechanism
标签:Spring Boot