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.