RU | EN | DE

1. Spring Security Basics

Spring Security = Filter rund um die Anwendung, die entscheiden:

  • wer sich anmelden darf (Authentication),
  • was sie tun dürfen (Authorization).

📌 Verbindung:

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

⚡ Nach dem Hinzufügen dieser Abhängigkeit:

  • Spring schützt automatisch alle Endpunkte.
  • Standardmäßig ist Basic Auth aktiviert → Login = user, Passwort wird beim Start (in den Logs) generiert.

2. Basic Auth

📄 Konfiguration:

@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();
    }
}

Jetzt:

  • GET /api/public/ → für alle zugänglich
  • andere → erfordern Basic Auth.

3. Form Login

http.formLogin(withDefaults());

Beim Versuch, sich anzumelden, sieht der Benutzer ein Standard-HTML-Formular.

4. UserDetailsService (Benutzerdefinierte Benutzer)

@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

Speichere Passwörter niemals im Klartext.

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

6. JWT Authentication

📄 Abhängigkeit:

<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-Generierung:

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 zur Token-Überprüfung:

@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);
    }
}

📄 Filterkonfiguration:

@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();
}

Jetzt akzeptiert die API ein Token:

Authorization: Bearer <jwt>

7. Role-based Access Control

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

📌 Überprüfung von Annotationen aktivieren:

@EnableMethodSecurity
public class SecurityConfig {}

8. OAuth2, OpenID Connect

📄 Abhängigkeit:

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

📄 Konfiguration (Beispiel für Google):

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

📌 Jetzt kann sich der Benutzer über Google anmelden.

9. API Key (Einfachster Schutz)

@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. Bewährte Vorgehensweisen

  • ✅ Passwörter niemals im Klartext speichern → nur BCrypt verwenden.
  • ✅ Für REST-APIs JWT, für Unternehmensportale → OAuth2/OpenID verwenden.
  • ✅ Die API schützen → standardmäßig alles geschlossen.
  • ✅ Autorisierungslogik (Rollen/Berechtigungen) in Annotationen @PreAuthorize auslagern.
  • CSRF für Webformulare nicht deaktivieren, aber für REST-APIs wird CSRF in der Regel deaktiviert.
  • Auth-Endpoints (/api/auth/) von der Geschäftslogik (/api/) trennen.