Spring Security
Security-Aspekte
- Authentifizierung
-
- Anmeldung
- Registrierung
- Autorisierung
- Rechte
Setup
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

Default sign in
Using generated security password: abde9925-920b-4278-8ff5-bc9e533f8abc
This generated password is for development use only.
username: user
password: abde9925-920b-4278-8ff5-bc9e533f8abc
Settings
@EnableWebSecurity
@Configuration
public class SecurityConfig {
@Bean
public AuthenticationProvider authenticationManager(
PasswordEncoder passwordEncoder,
UserDetailsService userDetailsService) {
var authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setPasswordEncoder(passwordEncoder);
authenticationProvider.setUserDetailsService(userDetailsService);
return authenticationProvider;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(10, new SecureRandom());
}
UserDetailsService
public interface UserDetailsService {
UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException;
}
@Service
public record UserDetailsServiceImpl(UserRepository userRepository)
implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
var user = userRepository
.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException());
return new UserDetailsImpl(user);
}
}
UserDetails
public interface UserDetails extends Serializable {
Collection<? extends GrantedAuthority> getAuthorities();
String getPassword();
String getUsername();
boolean isAccountNonExpired();
boolean isAccountNonLocked();
boolean isCredentialsNonExpired();
boolean isEnabled();
}public class UserDetailsImpl implements UserDetails {
private final UserEntity user;
public UserDetailsImpl(UserEntity user) {
this.user = user;
}
// Spring braucht den ROLE_-Präfix
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Set.of(new SimpleGrantedAuthority("ROLE_" + user.getRole()));
}
@Override
public String getPassword() {
return user.getPassword();
}
// ... Properties vom user holen
}
User
@Entity
@...
public class UserEntity {
@Id
private String username;
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
@NotBlank
private String password;
@NotNull
private String role;
// ... weitere Felder
}
User-Klasse nach Belieben implementierbar
Autorisierung
public class SecurityConfig {
...
@Bean
public SecurityFilterChain filterChain(HttpSecurity http)
throws Exception {
return http
// alle Routen nur für Authentifizierte
.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
// Anmeldung über ein html-form
.formLogin(withDefaults())
.build();
}
}
default
Route authorization
http.authorizeHttpRequests(auth ->
auth
.requestMatchers(GET, "/", "/home").permitAll()
.requestMatchers("/admin/**").hasRole("admin")
.anyRequest().authenticated())
first match handles request
Customization
http
.authorizeHttpRequests( ... )
.formLogin(form -> form
.loginPage("/login")
.permitAll()
.defaultSuccessUrl("/greeting", true))
.rememberMe(withDefaults());
rememberMe setzt langlebigen Cookie
csrf

<img src="http://bank.com/transfer?accountNo=5678&amount=1000"/>
<form action="http://bank.com/transfer" method="POST">
<input type="hidden" name="accountNo" value="5678"/>
<input type="hidden" name="amount" value="1000"/>
<input type="submit" value="Apply for free"/>
</form>
<body onload="document.forms[0].submit()">
POST, PATCH, PUT, DELETE ist forbidden(403)
Countermeasures

Website liefert html
- keine Aktion erforderlich
- Spring added automatisch bei jedem template csrf als hidden field
Service
return http
.csrf(csrf -> csrf.csrfTokenRepository(withHttpOnlyFalse()))
.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
.httpBasic(withDefaults());
return http.build();
oder Stateless 👍
JWT

Token

- payload per default nur base64-encrypted
- mäßig für Sessions geeignet
- logout implementieren
- Token-Blacklist?
OAuth2
