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/).