RU | EN | DE

1. Spring Security Basics

Spring Security = filters around the application that decide:

  • who can log in (Authentication),
  • what they can do (Authorization).

📌 Connection:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

⚡ After adding this dependency:

  • Spring automatically protects all endpoints.
  • By default, Basic Auth is enabled → login = user, password is generated at startup (in logs).

2. Basic Auth

📄 Configuration:

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain security(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/public/**").permitAll()
                .anyRequest().authenticated()
            )
            .httpBasic(); // Basic Auth
        return http.build();
    }
}

Now:

  • GET /api/public/** → accessible to everyone
  • others → require Basic Auth.

3. Form Login

http.formLogin(withDefaults());

When attempting to log in, the user will see a standard HTML form.

4. UserDetailsService (custom users)

@Service
public class MyUserDetailsService implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String username) {
        return User.withUsername(username)
            .password("{noop}password") // {noop} = no encryption
            .roles("USER")
            .build();
    }
}

5. Password Encoding

Never store passwords in plain text.

@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}
String encoded = passwordEncoder.encode("secret");
System.out.println(encoded);
// $2a$10$F5n2k...

6. JWT Authentication

📄 Dependency:

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.5</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>

📄 Token Generation:

String token = Jwts.builder()
    .setSubject("user1")
    .claim("role", "USER")
    .setIssuedAt(new Date())
    .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60))
    .signWith(SignatureAlgorithm.HS256, "secret-key")
    .compact();

📄 Filter for Token Verification:

@Component
public class JwtFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res,
                                    FilterChain chain) throws IOException, ServletException {
        String authHeader = req.getHeader("Authorization");
        if (authHeader != null && authHeader.startsWith("Bearer ")) {
            String jwt = authHeader.substring(7);
            Claims claims = Jwts.parser()
                .setSigningKey("secret-key")
                .parseClaimsJws(jwt)
                .getBody();
            String username = claims.getSubject();
            UsernamePasswordAuthenticationToken auth =
                new UsernamePasswordAuthenticationToken(username, null, List.of());
            SecurityContextHolder.getContext().setAuthentication(auth);
        }
        chain.doFilter(req, res);
    }
}

📄 Filter Configuration:

@Bean
public SecurityFilterChain security(HttpSecurity http, JwtFilter jwtFilter) throws Exception {
    return http
        .csrf(AbstractHttpConfigurer::disable)
        .authorizeHttpRequests(auth -> auth
            .requestMatchers("/api/auth/**").permitAll()
            .anyRequest().authenticated()
        )
        .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
        .build();
}

Now the API accepts a token:

Authorization: Bearer <jwt>

7. Role-based Access Control

@GetMapping("/admin")
@PreAuthorize("hasRole('ADMIN')")
public String admin() {
    return "Hello Admin!";
}

📌 Enable annotation checking:

@EnableMethodSecurity
public class SecurityConfig {}

8. OAuth2, OpenID Connect

📄 Dependency:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>

📄 Configuration (example for Google):

spring:
  security:
    oauth2:
      client:
        registration:
          google:
            client-id: <id>
            client-secret: <secret>
            scope: profile,email

📌 Now the user can log in through Google.

9. API Key (simplest protection)

@Component
public class ApiKeyFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res,
                                    FilterChain chain) throws IOException, ServletException {
        String apiKey = req.getHeader("X-API-KEY");
        if (!"secret-key".equals(apiKey)) {
            res.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return;
        }
        chain.doFilter(req, res);
    }
}

10. Best Practices

  • ✅ Never store passwords in plain text → only BCrypt.
  • ✅ For REST API use JWT, for corporate portals → OAuth2/OpenID.
  • ✅ Protect the API → by default all are closed.
  • ✅ Move authorization logic (roles/permissions) to annotations @PreAuthorize.
  • ✅ Do not disable CSRF for web forms, but for REST API usually CSRF is disabled.
  • ✅ Separate auth endpoints (/api/auth/) from business logic (/api/).