Java Application Security Fundamentals
Learn Java Application Security fundamentals with Spring Boot, authentication, authorization, password hashing, secure API design, input validation, logging, secrets management, and production security best practices.
Introduction
Security is one of the most important parts of modern Java application development.
A Java application may have clean code, good performance, and scalable architecture, but if it is not secure, attackers can exploit it.
Common security issues include:
- Weak authentication
- Broken authorization
- Plain text passwords
- SQL injection
- Exposed secrets
- Insecure APIs
- Sensitive data in logs
- Missing HTTPS
- Poor error handling
- Missing input validation
This article explains Java application security fundamentals step by step with diagrams and Spring Boot implementation examples.
1. What is Application Security?
Application Security means protecting software from unauthorized access, data leaks, attacks, and misuse.
flowchart TB
User["User / Client"]
--> App["Java Application"]
App --> Auth["Authentication"]
App --> Authz["Authorization"]
App --> Validation["Input Validation"]
App --> Data["Secure Data Access"]
App --> Logs["Secure Logging"]
App --> Secrets["Secrets Management"]
Application security protects:
- Users
- APIs
- Business logic
- Databases
- Files
- Secrets
- Logs
- Infrastructure
2. Why Security Matters in Java Applications
Imagine a banking application.
Users can:
- Login
- View accounts
- Transfer money
- Download statements
- Update profile
If security is weak:
flowchart LR
Attacker["Attacker"]
--> WeakLogin["Weak Login"]
--> AccountData["Customer Account Data"]
--> Loss["Financial / Data Loss"]
A secure application must prevent:
- Unauthorized login
- Access to another user's data
- Password theft
- API abuse
- Data tampering
- Sensitive data exposure
3. Core Security Concepts
Every Java developer should understand these concepts:
mindmap
root((Java Security))
Authentication
Authorization
Password Hashing
Input Validation
Secure APIs
Data Protection
Secrets Management
Secure Logging
Error Handling
Monitoring
4. Authentication
Authentication answers this question:
Who are you?
Example:
Username: venu
Password: ********
If credentials are valid, the user is authenticated.
sequenceDiagram
participant User
participant App
participant Database
User->>App: Submit username/password
App->>Database: Find user by username
Database-->>App: User record
App->>App: Verify password
App-->>User: Login success/failure
5. Authorization
Authorization answers this question:
What are you allowed to do?
Example:
| Role | Permission |
|---|---|
| USER | View own profile |
| ADMIN | Manage users |
| AUDITOR | Read reports |
flowchart LR
User["Authenticated User"]
--> Role["Role"]
--> Permission["Permission"]
--> Resource["Protected API"]
Authentication happens first.
Authorization happens after authentication.
6. Authentication vs Authorization
| Concept | Meaning | Example |
|---|---|---|
| Authentication | Verify identity | Login with username/password |
| Authorization | Verify access | USER cannot access ADMIN API |
flowchart TB
A["Login Request"]
--> B["Authentication"]
B --> C["User Verified"]
C --> D["Authorization"]
D --> E["Allow or Deny Access"]
7. Security Flow in Java Web Application
sequenceDiagram
participant Client
participant SecurityFilter
participant Controller
participant Service
participant Database
Client->>SecurityFilter: HTTP Request
SecurityFilter->>SecurityFilter: Validate Authentication
SecurityFilter->>SecurityFilter: Check Authorization
SecurityFilter->>Controller: Forward Request
Controller->>Service: Call Business Logic
Service->>Database: Secure Data Access
Database-->>Service: Data
Service-->>Controller: Response
Controller-->>Client: JSON Response
In Spring Boot, this security flow is usually handled by Spring Security filters.
8. Common Security Layers
flowchart TB
Client["Client"]
--> HTTPS["HTTPS / TLS"]
--> Auth["Authentication"]
--> Authz["Authorization"]
--> Validation["Input Validation"]
--> Service["Business Logic"]
--> DB["Database Security"]
--> Logs["Secure Logging"]
A production Java application should secure every layer.
9. Spring Boot Project Setup
Maven Dependencies
<dependencies>
<!-- Spring Web for REST APIs -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Security for authentication and authorization -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Validation for request validation -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
</dependencies>
Dependency Explanation
| Dependency | Purpose |
|---|---|
| spring-boot-starter-web | Build REST APIs |
| spring-boot-starter-security | Add security filters |
| spring-boot-starter-validation | Validate request objects |
10. Project Structure
java-security-demo
└── src
└── main
└── java
└── com.codewithvenu.security
├── controller
│ └── UserController.java
├── dto
│ └── UserRequest.java
├── security
│ └── SecurityConfig.java
├── service
│ └── UserService.java
└── JavaSecurityDemoApplication.java
11. Basic Spring Security Configuration
package com.codewithvenu.security.security;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.requestMatchers("/api/user/**").hasAnyRole("USER", "ADMIN")
.anyRequest().authenticated()
)
.httpBasic(Customizer.withDefaults());
return http.build();
}
}
Code Explanation
@Configuration
Marks this class as a Spring configuration class.
@Bean
Registers SecurityFilterChain as a Spring bean.
csrf(csrf -> csrf.disable())
Disables CSRF protection for stateless REST API examples.
For browser-based apps, CSRF should usually be enabled.
requestMatchers("/api/public/**").permitAll()
Allows public APIs without login.
requestMatchers("/api/admin/**").hasRole("ADMIN")
Only users with ADMIN role can access admin APIs.
anyRequest().authenticated()
All other APIs require authentication.
httpBasic()
Enables Basic Authentication for learning purposes.
In production, JWT or OAuth2 is commonly used.
12. API Access Flow
flowchart TB
Request["HTTP Request"]
--> Filter["Spring Security Filter"]
Filter --> PublicCheck{"Public API?"}
PublicCheck -- Yes --> Allow["Allow Request"]
PublicCheck -- No --> AuthCheck{"Authenticated?"}
AuthCheck -- No --> Reject["401 Unauthorized"]
AuthCheck -- Yes --> RoleCheck{"Has Required Role?"}
RoleCheck -- No --> Forbidden["403 Forbidden"]
RoleCheck -- Yes --> Controller["Controller"]
13. Create Sample REST Controller
package com.codewithvenu.security.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@GetMapping("/api/public/health")
public String publicHealth() {
return "Application is running";
}
@GetMapping("/api/user/profile")
public String userProfile() {
return "User profile data";
}
@GetMapping("/api/admin/dashboard")
public String adminDashboard() {
return "Admin dashboard data";
}
}
API Behavior
| API | Access |
|---|---|
/api/public/health |
No login required |
/api/user/profile |
USER or ADMIN |
/api/admin/dashboard |
ADMIN only |
14. Configure In-Memory Users
package com.codewithvenu.security.security;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
@Configuration
public class UserConfig {
@Bean
public UserDetailsService userDetailsService() {
var user = User.withUsername("venu")
.password("{noop}password123")
.roles("USER")
.build();
var admin = User.withUsername("admin")
.password("{noop}admin123")
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
}
Important Note
{noop} means no password encoding.
This is only for learning.
Never use {noop} in production.
15. Test APIs
Public API
curl http://localhost:8080/api/public/health
Expected response:
Application is running
User API
curl -u venu:password123 http://localhost:8080/api/user/profile
Expected response:
User profile data
Admin API
curl -u admin:admin123 http://localhost:8080/api/admin/dashboard
Expected response:
Admin dashboard data
Forbidden Example
curl -u venu:password123 http://localhost:8080/api/admin/dashboard
Expected response:
403 Forbidden
16. Secure Password Hashing
Passwords should never be stored as plain text.
Bad:
password123
Good:
$2a$10$Xk3...
Use BCrypt.
flowchart LR
Password["Plain Password"]
--> BCrypt["BCrypt Hashing"]
--> Hash["Stored Hash"]
17. BCrypt Password Encoder
package com.codewithvenu.security.security;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class PasswordConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Explanation
PasswordEncoder is an interface used by Spring Security.
BCryptPasswordEncoder hashes passwords using BCrypt.
BCrypt automatically adds salt and makes password cracking harder.
18. Update User Configuration with BCrypt
@Bean
public UserDetailsService userDetailsService(PasswordEncoder passwordEncoder) {
var user = User.withUsername("venu")
.password(passwordEncoder.encode("password123"))
.roles("USER")
.build();
var admin = User.withUsername("admin")
.password(passwordEncoder.encode("admin123"))
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
Now passwords are stored securely in memory.
19. Input Validation
Never trust user input.
Attackers may send:
- Empty values
- Long strings
- Malicious scripts
- SQL injection payloads
- Invalid email formats
flowchart LR
ClientInput["Client Input"]
--> Validation["Validation Layer"]
--> Service["Service Layer"]
--> Database["Database"]
20. Request DTO Validation
package com.codewithvenu.security.dto;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
public record UserRequest(
@NotBlank(message = "Name is required")
@Size(min = 2, max = 50, message = "Name must be between 2 and 50 characters")
String name,
@NotBlank(message = "Email is required")
@Email(message = "Invalid email format")
String email,
@NotBlank(message = "Password is required")
@Size(min = 8, max = 100, message = "Password must be at least 8 characters")
String password
) {
}
21. Controller with Validation
package com.codewithvenu.security.controller;
import com.codewithvenu.security.dto.UserRequest;
import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/public/users")
public class RegistrationController {
@PostMapping("/register")
public String registerUser(@Valid @RequestBody UserRequest request) {
return "User registered successfully: " + request.email();
}
}
Explanation
@Valid
Triggers validation rules defined in UserRequest.
@RequestBody
Converts JSON request body to Java object.
22. Global Exception Handling
package com.codewithvenu.security.controller;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
public Map<String, String> handleValidationErrors(MethodArgumentNotValidException exception) {
Map<String, String> errors = new HashMap<>();
exception.getBindingResult()
.getFieldErrors()
.forEach(error -> errors.put(error.getField(), error.getDefaultMessage()));
return errors;
}
}
This returns clean validation errors instead of exposing internal stack traces.
23. Secure Error Response Flow
flowchart TB
InvalidRequest["Invalid Request"]
--> Validation["Validation Fails"]
--> ExceptionHandler["Global Exception Handler"]
--> CleanResponse["Clean Error Response"]
24. Secrets Management
Never store secrets in source code.
Bad:
String password = "dbPassword123";
String apiKey = "my-secret-api-key";
Good:
spring:
datasource:
password: ${DB_PASSWORD}
Use:
- Environment variables
- AWS Secrets Manager
- HashiCorp Vault
- Kubernetes Secrets
flowchart LR
SpringBoot["Spring Boot App"]
--> Env["Environment Variables"]
--> Secrets["Secrets Manager / Vault"]
25. Secure Logging
Do not log sensitive information.
Avoid logging:
- Passwords
- Access tokens
- Refresh tokens
- Credit card numbers
- SSN
- API keys
- Authorization headers
Bad:
log.info("User login request: {}", request);
Good:
log.info("Login attempt for email: {}", request.email());
flowchart LR
Request["Request"]
--> Logger["Logger"]
--> Masking["Mask Sensitive Data"]
--> Logs["Application Logs"]
26. HTTPS / TLS
Always use HTTPS in production.
HTTP:
flowchart LR
Client --> PlainText["Plain Text Data"] --> Server
HTTPS:
flowchart LR
Client --> Encrypted["Encrypted Data"] --> Server
HTTPS protects:
- Login credentials
- Tokens
- Personal data
- API requests
- Session data
27. SQL Injection Prevention
Bad approach:
String query = "select * from users where email = '" + email + "'";
This is vulnerable to SQL injection.
Good approach:
Use JPA repositories or prepared statements.
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
}
flowchart LR
UserInput["User Input"]
--> Repository["JPA Repository"]
--> PreparedQuery["Prepared Query"]
--> Database["Database"]
28. Secure API Design Checklist
Every API should answer:
- Is this API public or private?
- Who can access this API?
- What role is required?
- Is input validated?
- Are errors safe?
- Is data filtered by logged-in user?
- Are logs safe?
- Is rate limiting required?
mindmap
root((Secure API))
Authentication
Authorization
Validation
Error Handling
Logging
Rate Limiting
Data Filtering
29. Real-World Banking Example
Requirement
A banking customer should only view their own account.
Bad design:
GET /api/accounts/1001
If user changes 1001 to 1002, they may access another account.
Secure design:
GET /api/accounts/me
Backend finds accounts using logged-in user identity.
sequenceDiagram
participant Customer
participant API
participant SecurityContext
participant Database
Customer->>API: GET /api/accounts/me
API->>SecurityContext: Get logged-in user
SecurityContext-->>API: userId = 501
API->>Database: Find accounts by userId
Database-->>API: User's own accounts
API-->>Customer: Account response
30. Production Security Checklist
Before deploying a Java application:
- Use HTTPS
- Enable Spring Security
- Use BCrypt for passwords
- Validate all input
- Protect admin APIs
- Use roles and permissions
- Do not expose stack traces
- Do not hardcode secrets
- Do not log sensitive data
- Use secure headers
- Use rate limiting
- Use dependency scanning
- Use least privilege access
- Monitor logs and alerts
31. Common Mistakes
| Mistake | Risk |
|---|---|
| Plain text passwords | Password theft |
| Hardcoded secrets | Credential leakage |
| Missing validation | Injection attacks |
| Admin APIs without protection | Privilege abuse |
| Exposing stack traces | Internal details leak |
| Logging tokens | Account takeover |
| No HTTPS | Data interception |
32. Interview Questions
Q1. What is application security?
Application security protects software from unauthorized access, attacks, data leaks, and misuse.
Q2. What is authentication?
Authentication verifies who the user is.
Q3. What is authorization?
Authorization verifies what the user is allowed to access.
Q4. Why should passwords be hashed?
Because stored passwords should not be readable even if the database is compromised.
Q5. Why is BCrypt preferred?
BCrypt is slow, salted, and designed to resist brute-force attacks.
Q6. What is input validation?
Input validation checks whether user input is safe and valid before processing it.
Q7. Why should secrets not be stored in code?
Because source code may be exposed through GitHub, logs, build artifacts, or developer machines.
Q8. What is SQL injection?
SQL injection happens when attackers manipulate SQL queries using malicious input.
Q9. Why is HTTPS important?
HTTPS encrypts communication between client and server.
Q10. What is secure logging?
Secure logging means recording useful events without exposing sensitive data.
33. Key Takeaways
- Security must be designed from the beginning.
- Authentication verifies identity.
- Authorization controls access.
- Passwords must be hashed using BCrypt.
- Input validation prevents bad data and attacks.
- Never hardcode secrets.
- Never log sensitive information.
- HTTPS is mandatory in production.
- Secure APIs must check user identity and permissions.
- Spring Security provides strong security foundations for Java applications.
Next Article
➡️ Authentication vs Authorization in Java