Week 6 Day 3 — Authorization Deep Dive
Goal
Today I want to understand authorization in Spring Security.
Main questions:
- What is authorization?
- What is the difference between roles and authorities?
- What does
hasRolecheck? - What does
hasAuthoritycheck? - How do URL authorization rules work?
- Why does rule order matter?
- What is method security?
- What is
@EnableMethodSecurity? - What is
@PreAuthorize? - How do I use method parameters in security expressions?
- What are data-level permissions?
- Why are URL rules not enough?
- What are common exam traps?
1. Quick Review from Week 6 Day 2
In Day 2, I learned:
UserDetailsis Spring Security’s user representation.UserDetailsServiceloads users by username.- Username can be an email.
PasswordEncoderhashes and verifies passwords.- Store password hashes, not raw passwords.
AuthenticationManagerauthenticates credentials.DaoAuthenticationProviderusesUserDetailsServiceandPasswordEncoder.Authenticationrepresents the login attempt or authenticated user.roles("ADMIN")createsROLE_ADMIN.hasRole("ADMIN")checksROLE_ADMIN.
Memory sentence:
Authentication = who are you?
Authorization = what can you do?
Today I go deeper into authorization.
2. What Is Authorization?
Authorization answers:
What is this user allowed to do?
Examples:
Can this user access /api/admin/users?
Can this user delete a task?
Can this user approve an invoice?
Can this user access client 10?
Can this user see another tenant's data?
Can this user export payroll documents?
Authorization happens after authentication.
First:
Who is the user?
Then:
What may this user do?
Memory sentence:
Authorization checks permissions after authentication.
3. Authentication vs Authorization
| Concept | Question | Example |
|---|---|---|
| Authentication | Who are you? | Login with email and password |
| Authorization | What can you do? | Only ADMIN can delete users |
Example:
User logs in successfully.
Authentication succeeded.
User tries to call /api/admin/users.
Authorization checks if user has ADMIN role.
Memory sentence:
Authentication proves identity.
Authorization checks access.
4. 401 vs 403 Review
401 Unauthorized
Means:
The user is not authenticated.
Examples:
missing token
invalid token
wrong password
not logged in
403 Forbidden
Means:
The user is authenticated but not allowed.
Examples:
USER tries to access ADMIN endpoint
employee tries to access another tenant
normal user tries to delete another user's task
Memory sentence:
401 = who are you?
403 = I know who you are, but you are not allowed.
5. What Are Authorities?
An authority is a permission granted to a user.
Examples:
ROLE_USER
ROLE_ADMIN
TASK_READ
TASK_WRITE
TASK_DELETE
INVOICE_APPROVE
CLIENT_EXPORT
Spring Security stores authorities inside:
Authentication
Example:
principal = user@example.com
authorities = ROLE_USER, TASK_READ, TASK_WRITE
Memory sentence:
Authorities are permissions known by Spring Security.
6. What Are Roles?
A role is usually a broad group of permissions.
Examples:
USER
ADMIN
MANAGER
ACCOUNTANT
CONSULTANT
In Spring Security, roles are usually represented as authorities with prefix:
ROLE_
So role:
ADMIN
becomes authority:
ROLE_ADMIN
Memory sentence:
A role is usually an authority with
ROLE_prefix.
7. Role vs Authority
| Topic | Role | Authority |
|---|---|---|
| Meaning | broad group | specific permission |
| Example | ADMIN | TASK_DELETE |
| Stored form | ROLE_ADMIN | TASK_DELETE |
| Common check | hasRole("ADMIN") | hasAuthority("TASK_DELETE") |
| Use case | simple apps, broad access | fine-grained permissions |
Memory:
Role = group.
Authority = permission.
8. hasRole
Example:
.requestMatchers("/api/admin/**").hasRole("ADMIN")
This checks whether the user has authority:
ROLE_ADMIN
Important:
hasRole("ADMIN")
does not check for authority:
ADMIN
It checks for:
ROLE_ADMIN
Memory sentence:
hasRole("ADMIN")checks forROLE_ADMIN.
9. hasAuthority
Example:
.requestMatchers("/api/tasks/**").hasAuthority("TASK_READ")
This checks exactly:
TASK_READ
Another example:
.requestMatchers("/api/admin/**").hasAuthority("ROLE_ADMIN")
This checks exactly:
ROLE_ADMIN
Memory sentence:
hasAuthoritychecks the exact authority string.
10. hasRole vs hasAuthority
| Code | Checks for |
|---|---|
hasRole("ADMIN") | ROLE_ADMIN |
hasAuthority("ROLE_ADMIN") | ROLE_ADMIN |
hasAuthority("TASK_READ") | TASK_READ |
hasRole("ROLE_ADMIN") | usually wrong |
Common mistake:
hasRole("ROLE_ADMIN")
Why wrong?
hasRole adds ROLE_ automatically.
This can become ROLE_ROLE_ADMIN.
Correct:
hasRole("ADMIN")
or:
hasAuthority("ROLE_ADMIN")
Memory sentence:
Use
hasRole("ADMIN")orhasAuthority("ROLE_ADMIN"), nothasRole("ROLE_ADMIN").
11. Where Do Authorities Come From?
Authorities usually come from:
database roles
database permissions
JWT claims
OAuth2 scopes
LDAP groups
hardcoded in-memory users
custom AuthenticationProvider
Example with database user:
return User.builder()
.username(user.getEmail())
.password(user.getPasswordHash())
.roles(user.getRole())
.build();
If user.getRole() returns:
ADMIN
then Spring creates:
ROLE_ADMIN
Memory sentence:
Authorization depends on authorities loaded during authentication.
12. Simple URL Authorization
Security config:
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.httpBasic(Customizer.withDefaults())
.build();
}
Meaning:
/api/public/** is open
/api/admin/** needs ADMIN role
everything else needs login
Memory sentence:
URL authorization protects endpoints by request path and method.
13. HTTP Method Authorization
Example:
.authorizeHttpRequests(auth -> auth
.requestMatchers(HttpMethod.GET, "/api/tasks/**").hasAuthority("TASK_READ")
.requestMatchers(HttpMethod.POST, "/api/tasks/**").hasAuthority("TASK_WRITE")
.requestMatchers(HttpMethod.DELETE, "/api/tasks/**").hasAuthority("TASK_DELETE")
.anyRequest().authenticated()
)
Meaning:
GET task endpoints need TASK_READ
POST task endpoints need TASK_WRITE
DELETE task endpoints need TASK_DELETE
This is more precise than path-only rules.
Memory sentence:
URL rules can match both path and HTTP method.
14. Rule Order Matters
Good:
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
Bad:
.authorizeHttpRequests(auth -> auth
.anyRequest().authenticated()
.requestMatchers("/api/public/**").permitAll()
)
Why bad?
anyRequest matches everything.
Rules after anyRequest are unreachable or invalid.
Memory sentence:
Specific rules first. General rules last.
15. permitAll, authenticated, denyAll
Common rules:
permitAll()
authenticated()
denyAll()
Meaning:
| Rule | Meaning |
|---|---|
permitAll() | anyone can access |
authenticated() | user must be logged in |
denyAll() | nobody can access |
Examples:
.requestMatchers("/api/auth/**").permitAll()
.requestMatchers("/api/internal/**").denyAll()
.anyRequest().authenticated()
16. hasAnyRole and hasAnyAuthority
Example:
.requestMatchers("/api/reports/**").hasAnyRole("ADMIN", "MANAGER")
Checks for:
ROLE_ADMIN or ROLE_MANAGER
Example:
.requestMatchers("/api/tasks/**").hasAnyAuthority("TASK_READ", "TASK_WRITE")
Checks for:
TASK_READ or TASK_WRITE
Memory sentence:
hasAnyRole/hasAnyAuthoritymeans at least one of them is enough.
17. URL Authorization Is Coarse-Grained
URL rules are good for broad access.
Examples:
/api/admin/** needs ADMIN
/api/auth/** is public
GET /api/tasks/** needs TASK_READ
POST /api/tasks/** needs TASK_WRITE
But URL rules often cannot answer:
Can this user access client 10?
Does this task belong to the user's tenant?
Can this accountant approve this invoice?
Is this user the owner of this record?
These are data-level permissions.
Memory sentence:
URL authorization protects endpoints, but not always specific data.
18. Data-Level Permission
Data-level permission means:
Access depends on the specific data being accessed.
Example:
GET /api/clients/10/tasks
URL rule can check:
user is authenticated
user has role USER
But service must check:
Does this user belong to client 10?
Does this user belong to the same tenant?
Is this user allowed to see these tasks?
Memory sentence:
Data-level permission checks access to a specific resource, not just a URL.
19. Tenant Permission Example
Imagine a multi-tenant app.
User:
userId = 5
tenantId = 100
role = USER
Task:
taskId = 20
tenantId = 200
Even if the user is authenticated and has role USER:
user should not access task from tenant 200
Why?
different tenant
Memory sentence:
In multi-tenant apps, authorization must check tenant ownership.
20. Service-Level Permission Check
Example:
@Service
public class TaskService {
private final TaskRepository taskRepository;
private final PermissionService permissionService;
public TaskService(
TaskRepository taskRepository,
PermissionService permissionService
) {
this.taskRepository = taskRepository;
this.permissionService = permissionService;
}
@Transactional(readOnly = true)
public TaskDto findById(Long taskId, AppUserPrincipal currentUser) {
TaskEntity task = taskRepository.findById(taskId)
.orElseThrow(() -> new ResourceNotFoundException("Task", taskId));
if (!permissionService.canAccessTask(currentUser, task)) {
throw new AccessDeniedException("No access to this task");
}
return toDto(task);
}
}
This checks real business permission.
Memory sentence:
Service methods should protect business data access.
21. AccessDeniedException
If authenticated user is not allowed, throw:
AccessDeniedException
Example:
throw new AccessDeniedException("No access to this task");
Spring Security commonly maps this to:
403 Forbidden
Memory sentence:
AccessDeniedExceptionmeans authenticated but forbidden.
22. Method Security
Method security means:
Authorization rules are placed on methods, usually service methods.
Example:
@PreAuthorize("hasRole('ADMIN')")
public void deleteUser(Long id) {
userRepository.deleteById(id);
}
This method can run only if the user has role ADMIN.
Memory sentence:
Method security protects method calls, not just URLs.
23. Enabling Method Security
To use annotations like @PreAuthorize, enable method security:
@Configuration
@EnableMethodSecurity
public class MethodSecurityConfig {
}
Or put it on existing security config:
@Configuration
@EnableMethodSecurity
public class SecurityConfig {
}
Important:
Adding spring-boot-starter-security does not automatically enable method security.
Memory sentence:
Use
@EnableMethodSecurityto activate method security annotations.
24. @PreAuthorize
@PreAuthorize checks authorization before the method runs.
Example:
@PreAuthorize("hasRole('ADMIN')")
public void deleteUser(Long id) {
userRepository.deleteById(id);
}
If user has ADMIN:
method runs
If user does not have ADMIN:
method is blocked
AccessDeniedException
Memory sentence:
@PreAuthorizechecks before method execution.
25. @PreAuthorize with Authority
Example:
@PreAuthorize("hasAuthority('TASK_DELETE')")
public void deleteTask(Long taskId) {
taskRepository.deleteById(taskId);
}
This checks exact authority:
TASK_DELETE
If the user only has:
TASK_READ
then the method is blocked.
26. @PreAuthorize with Multiple Roles
Example:
@PreAuthorize("hasAnyRole('ADMIN', 'MANAGER')")
public List<ReportDto> getReports() {
return reportRepository.findAllReports();
}
Meaning:
ADMIN or MANAGER can call this method
Another example:
@PreAuthorize("hasRole('ADMIN') or hasAuthority('REPORT_READ')")
public List<ReportDto> getReports() {
return reportRepository.findAllReports();
}
Memory sentence:
@PreAuthorizesupports security expressions.
27. Method Parameters in @PreAuthorize
@PreAuthorize can use method parameters.
Example:
@PreAuthorize("#userId == authentication.principal.id")
public UserDto getOwnProfile(Long userId) {
return userService.findById(userId);
}
Meaning:
user can access only their own profile
Another example:
@PreAuthorize("hasRole('ADMIN') or #userId == authentication.principal.id")
public UserDto getProfile(Long userId) {
return userService.findById(userId);
}
Meaning:
ADMIN can access any profile
normal user can access only own profile
Memory sentence:
#parameterNamerefers to a method argument in security expressions.
28. Custom Permission Method in @PreAuthorize
For complex checks, call a Spring bean.
Permission service:
@Component("permissionService")
public class PermissionService {
public boolean canAccessTask(Long taskId, Authentication authentication) {
String username = authentication.getName();
// load user/task and check tenant, ownership, role, etc.
return true;
}
}
Service method:
@PreAuthorize("@permissionService.canAccessTask(#taskId, authentication)")
public TaskDto findTask(Long taskId) {
return taskRepository.findById(taskId)
.map(this::toDto)
.orElseThrow(() -> new ResourceNotFoundException("Task", taskId));
}
Meaning:
Before method runs, Spring calls permissionService.canAccessTask(...)
Memory sentence:
For real business permissions, call a permission bean from
@PreAuthorize.
29. authentication in Expressions
Inside @PreAuthorize, I can use:
authentication
Example:
@PreAuthorize("authentication.name == #email")
public UserDto findByEmail(String email) {
return userService.findByEmail(email);
}
authentication.name usually returns:
current username
If username is email, then:
authentication.name = current user's email
Memory sentence:
authenticationgives access to the current authenticated user in expressions.
30. Custom Principal in Expressions
If principal is custom:
public class AppUserPrincipal implements UserDetails {
private final Long id;
private final Long tenantId;
private final String email;
public Long getId() {
return id;
}
public Long getTenantId() {
return tenantId;
}
@Override
public String getUsername() {
return email;
}
}
Then expression:
@PreAuthorize("#tenantId == authentication.principal.tenantId")
public List<TaskDto> findTenantTasks(Long tenantId) {
return taskRepository.findByTenantId(tenantId)
.stream()
.map(this::toDto)
.toList();
}
Meaning:
User can only access their own tenant.
Memory sentence:
Custom principal fields can be used in method security expressions.
31. @PostAuthorize
@PostAuthorize checks after the method returns.
Example:
@PostAuthorize("returnObject.ownerEmail == authentication.name")
public DocumentDto findDocument(Long id) {
return documentRepository.findById(id)
.map(this::toDto)
.orElseThrow(() -> new ResourceNotFoundException("Document", id));
}
Meaning:
method runs first
then Spring checks returned object
only owner can receive it
Use carefully.
Why?
the data is loaded before authorization decision
may be less efficient
may have side effects if used on write methods
Memory sentence:
@PostAuthorizechecks after method execution.
32. Prefer @PreAuthorize for Most Cases
Usually prefer:
@PreAuthorize(...)
because:
it blocks method before execution
avoids unnecessary database work
avoids side effects
clearer for write operations
Use @PostAuthorize when the decision depends on the returned object.
Memory sentence:
Prefer pre-checks unless the decision needs the return value.
33. @Secured
Another method security annotation:
@Secured("ROLE_ADMIN")
public void deleteUser(Long id) {
}
It is simpler but less expressive than @PreAuthorize.
@PreAuthorize can use:
roles
authorities
method parameters
authentication
custom beans
SpEL expressions
Memory sentence:
@PreAuthorizeis more expressive than@Secured.
34. JSR-250 Annotations
Spring Security can also support annotations like:
@RolesAllowed("ADMIN")
@PermitAll
@DenyAll
But these may require enabling JSR-250 support:
@EnableMethodSecurity(jsr250Enabled = true)
For Spring Security learning, focus first on:
@PreAuthorize
Memory sentence:
@PreAuthorizeis the most flexible method security annotation to learn first.
35. Method Security Works on Spring Beans
Method security applies to Spring-managed beans.
Good:
@Service
public class TaskService {
@PreAuthorize("hasAuthority('TASK_READ')")
public TaskDto findTask(Long id) {
...
}
}
Not useful on objects that are not Spring beans.
Example:
new TaskService()
This bypasses Spring and its proxies.
Memory sentence:
Method security works through Spring-managed beans.
36. Self-Invocation Trap Again
Similar to @Transactional, method security is often proxy-based.
Problem:
@Service
public class TaskService {
public TaskDto outer(Long id) {
return inner(id);
}
@PreAuthorize("hasAuthority('TASK_READ')")
public TaskDto inner(Long id) {
return findTask(id);
}
}
If outer() calls inner() inside the same class, the call may bypass the security proxy.
Better:
put annotation on outer public method
move secured method to another Spring bean
avoid relying on internal calls for security
Memory sentence:
Method security can be bypassed by self-invocation.
37. URL Security vs Method Security
| Topic | URL Security | Method Security |
|---|---|---|
| Where | SecurityFilterChain | service/controller methods |
| Style | DSL config | annotations |
| Best for | endpoint-level rules | business/service-level rules |
| Example | /api/admin/** | @PreAuthorize("hasRole('ADMIN')") |
| Can use method args | no | yes |
| Data-level checks | limited | good |
Memory sentence:
URL security is coarse-grained. Method security is fine-grained.
38. Use Both URL and Method Security
Good design:
URL security:
- public endpoints
- admin area
- authenticated API
- HTTP method rules
Method security:
- service-level roles
- task ownership
- tenant access
- business action permission
Example URL config:
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
Example service method:
@PreAuthorize("@permissionService.canAccessTask(#taskId, authentication)")
public TaskDto findTask(Long taskId) {
...
}
Memory sentence:
Use URL rules for broad access and method security for business access.
39. Controller with Current User
Controller:
@GetMapping("/api/tasks/{taskId}")
public TaskDto getTask(
@PathVariable Long taskId,
@AuthenticationPrincipal AppUserPrincipal principal
) {
return taskService.findTask(taskId, principal);
}
Service:
@Transactional(readOnly = true)
public TaskDto findTask(Long taskId, AppUserPrincipal principal) {
TaskEntity task = taskRepository.findById(taskId)
.orElseThrow(() -> new ResourceNotFoundException("Task", taskId));
if (!task.getTenantId().equals(principal.getTenantId())) {
throw new AccessDeniedException("No access to this task");
}
return toDto(task);
}
This is explicit and easy to understand.
Memory sentence:
Passing current user to service makes data permission checks clear.
40. Method Security Alternative
Instead of passing principal and checking manually:
@PreAuthorize("@permissionService.canAccessTask(#taskId, authentication)")
@Transactional(readOnly = true)
public TaskDto findTask(Long taskId) {
TaskEntity task = taskRepository.findById(taskId)
.orElseThrow(() -> new ResourceNotFoundException("Task", taskId));
return toDto(task);
}
Permission service:
@Component("permissionService")
public class PermissionService {
private final TaskRepository taskRepository;
public PermissionService(TaskRepository taskRepository) {
this.taskRepository = taskRepository;
}
public boolean canAccessTask(Long taskId, Authentication authentication) {
AppUserPrincipal principal = (AppUserPrincipal) authentication.getPrincipal();
return taskRepository.existsByIdAndTenantId(
taskId,
principal.getTenantId()
);
}
}
Repository:
boolean existsByIdAndTenantId(Long id, Long tenantId);
This checks access before loading full task details.
Memory sentence:
Permission checks can use efficient repository methods like
existsBy....
41. Avoid Trusting Client-Sent User IDs
Bad request:
POST /api/tasks
Content-Type: application/json
{
"title": "Secret task",
"userId": 123
}
Bad service:
public TaskDto create(CreateTaskRequest request) {
UserEntity user = userRepository.findById(request.userId()).orElseThrow();
...
}
Problem:
client can pretend to be another user
Better:
take current user from Authentication/SecurityContext
ignore userId from request for ownership
Good:
public TaskDto create(CreateTaskRequest request, AppUserPrincipal principal) {
UserEntity user = userRepository.findById(principal.getId()).orElseThrow();
...
}
Memory sentence:
Never trust client-sent user identity when authenticated user is available.
42. Avoid Only Frontend Authorization
Bad assumption:
The button is hidden in React, so backend is safe.
Wrong.
A user can still call the API directly with:
Postman
curl
browser devtools
custom script
Backend must enforce authorization.
Memory sentence:
Frontend hides buttons. Backend enforces security.
43. Authorization in Repository Queries
Sometimes the safest query includes access scope.
Bad:
Optional<TaskEntity> findById(Long id);
Then later check tenant.
Better for reads:
Optional<TaskEntity> findByIdAndTenantId(Long id, Long tenantId);
Service:
@Transactional(readOnly = true)
public TaskDto findTask(Long taskId, AppUserPrincipal principal) {
TaskEntity task = taskRepository.findByIdAndTenantId(
taskId,
principal.getTenantId()
).orElseThrow(() -> new ResourceNotFoundException("Task", taskId));
return toDto(task);
}
This avoids loading data outside the user's tenant.
Memory sentence:
Scope repository queries by tenant or owner when possible.
44. 404 vs 403 for Data Access
If user requests another tenant's task, should API return 403 or 404?
Both are possible design choices.
403
Means:
resource exists, but user is not allowed
404
Means:
resource not found from this user's perspective
Many APIs return 404 to avoid revealing whether a resource exists.
Example:
taskRepository.findByIdAndTenantId(taskId, principal.getTenantId())
.orElseThrow(() -> new ResourceNotFoundException("Task", taskId));
Memory sentence:
For cross-tenant resources, 404 can avoid leaking existence.
45. Role Explosion Problem
Bad design:
ROLE_ADMIN
ROLE_TASK_READER
ROLE_TASK_WRITER
ROLE_INVOICE_APPROVER
ROLE_CLIENT_EXPORTER
ROLE_PAYROLL_MANAGER
ROLE_DOCUMENT_UPLOADER
If everything becomes a role, roles become messy.
Better:
roles = broad groups
authorities = specific permissions
Example:
ROLE_ACCOUNTANT
TASK_READ
TASK_WRITE
INVOICE_APPROVE
DOCUMENT_UPLOAD
Memory sentence:
Use roles for broad groups and authorities for fine-grained permissions.
46. Role Hierarchy Preview
Sometimes roles have hierarchy.
Example:
ADMIN includes MANAGER permissions.
MANAGER includes USER permissions.
Concept:
ROLE_ADMIN > ROLE_MANAGER > ROLE_USER
Spring Security supports role hierarchy, but it is an advanced topic.
For now, keep simple:
explicit roles and authorities
Memory sentence:
Role hierarchy can reduce repeated rules, but learn basic roles first.
47. Security Testing Preview
URL security test:
@WithMockUser(roles = "ADMIN")
@Test
void adminCanAccessAdminEndpoint() throws Exception {
mockMvc.perform(get("/api/admin/users"))
.andExpect(status().isOk());
}
Method security test:
@WithMockUser(roles = "USER")
@Test
void userCannotDeleteUser() {
assertThatThrownBy(() -> userService.deleteUser(1L))
.isInstanceOf(AccessDeniedException.class);
}
Memory sentence:
Security rules should be tested with allowed and denied users.
48. Common Exam Traps
Trap 1
Authentication and authorization are different.
Trap 2
401 and 403 are different.
Trap 3
Roles are authorities with ROLE_ prefix.
Trap 4
hasRole("ADMIN") checks ROLE_ADMIN.
Trap 5
hasAuthority("ADMIN") checks exactly ADMIN, not ROLE_ADMIN.
Trap 6
Do not use hasRole("ROLE_ADMIN").
Trap 7
Rule order matters in URL authorization.
Trap 8
Specific request matchers must come before anyRequest().
Trap 9
URL rules are coarse-grained.
Trap 10
Method security needs @EnableMethodSecurity.
Trap 11
@PreAuthorize checks before method execution.
Trap 12
@PostAuthorize checks after method execution.
Trap 13
Method security applies to Spring beans.
Trap 14
Self-invocation can bypass method security.
Trap 15
Never rely only on frontend authorization.
Trap 16
Never trust client-sent user ID for authenticated actions.
Trap 17
Data-level permissions often belong in service/domain logic.
Trap 18
Repository queries can include tenant/owner scope.
49. Real Exam Question: Authorization
Question:
What is authorization?
Answer:
Authorization is the process of deciding what an authenticated user is allowed to access or do.
50. Real Exam Question: Role
Question:
What is a role in Spring Security?
Answer:
A role is usually represented as an authority with the ROLE_ prefix, such as ROLE_ADMIN.
51. Real Exam Question: Authority
Question:
What is an authority?
Answer:
An authority is a permission granted to the user, such as ROLE_ADMIN, TASK_READ, or INVOICE_APPROVE.
52. Real Exam Question: hasRole
Question:
What does hasRole("ADMIN") check?
Answer:
It checks whether the current user has authority ROLE_ADMIN.
53. Real Exam Question: hasAuthority
Question:
What does hasAuthority("TASK_READ") check?
Answer:
It checks whether the current user has exactly the authority TASK_READ.
54. Real Exam Question: Rule Order
Question:
Why does authorization rule order matter?
Answer:
Rules are evaluated in order. Specific rules should be placed before general rules like anyRequest().
55. Real Exam Question: Method Security
Question:
How do I enable method security?
Answer:
Add @EnableMethodSecurity to a configuration class.
56. Real Exam Question: @PreAuthorize
Question:
What does @PreAuthorize do?
Answer:
@PreAuthorize checks an authorization expression before the method is invoked. If the expression fails, the method does not run.
57. Real Exam Question: URL vs Method Security
Question:
What is the difference between URL security and method security?
Answer:
URL security protects endpoints at the HTTP request level. Method security protects method calls, usually in the service layer, and can use method parameters and business-specific authorization expressions.
58. Real Exam Question: Data-Level Permission
Question:
What is data-level permission?
Answer:
Data-level permission means access depends on the specific resource or data, such as whether the current user belongs to the same tenant as the requested task.
59. Interview Answer
Question:
Explain roles and authorities in Spring Security.
Good answer:
Spring Security works with authorities. An authority is a permission, such as TASK_READ or ROLE_ADMIN. A role is normally represented as an authority with the ROLE_ prefix. For example, hasRole("ADMIN") checks for ROLE_ADMIN, while hasAuthority("TASK_READ") checks for exactly TASK_READ. I use roles for broad groups and authorities for fine-grained permissions.
60. Interview Answer
Question:
How do you design authorization in a Spring Boot REST API?
Good answer:
I use URL rules in SecurityFilterChain for broad endpoint access, such as public endpoints, admin endpoints, and authenticated APIs. Then I enforce business and data-level permissions in the service layer, either with explicit permission checks or method security like @PreAuthorize. For multi-tenant data, I make sure repository queries are scoped by tenant or owner so users cannot access data outside their permission boundary.
61. Interview Answer
Question:
What is
@PreAuthorize?
Good answer:
@PreAuthorize is a method security annotation that checks an authorization expression before a method runs. It can check roles, authorities, method parameters, the current authentication, or call custom permission beans. To use it, method security must be enabled with @EnableMethodSecurity.
62. Interview Answer
Question:
Why are URL rules not enough?
Good answer:
URL rules are good for broad access, like requiring authentication or an admin role for a path. But many real permissions depend on the actual data, such as task ownership, tenant membership, invoice status, or who created a document. Those checks need to happen in service/domain logic or method security, not only in URL configuration.
63. Interview Answer
Question:
How do you handle tenant-level authorization?
Good answer:
I include the current user’s tenant information in the authenticated principal or load it from the database. Then service methods check that the requested resource belongs to the same tenant. When possible, I also scope repository queries directly, such as findByIdAndTenantId, so data outside the tenant is not loaded in the first place. For cross-tenant access attempts, the API may return 403 or 404 depending on the design.
64. Tiny Code Practice
Security config:
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.requestMatchers(HttpMethod.GET, "/api/tasks/**").hasAuthority("TASK_READ")
.requestMatchers(HttpMethod.POST, "/api/tasks/**").hasAuthority("TASK_WRITE")
.anyRequest().authenticated()
)
.httpBasic(Customizer.withDefaults())
.build();
}
Questions:
- Who can access
/api/auth/login? - Who can access
/api/admin/users? - What authority is needed for
GET /api/tasks? - What authority is needed for
POST /api/tasks? - What happens to all other requests?
Answers:
- Everyone.
- Users with role
ADMIN, meaning authorityROLE_ADMIN. TASK_READ.TASK_WRITE.- They require authentication.
65. Tiny Bug Practice 1
Problem:
.requestMatchers("/api/admin/**").hasRole("ROLE_ADMIN")
Question:
What is wrong?
Answer:
hasRole adds the ROLE_ prefix automatically. Use:
hasRole("ADMIN")
or:
hasAuthority("ROLE_ADMIN")
66. Tiny Bug Practice 2
Problem:
.authorizeHttpRequests(auth -> auth
.anyRequest().authenticated()
.requestMatchers("/api/auth/**").permitAll()
)
Question:
What is wrong?
Answer:
anyRequest() is a general catch-all rule. It must come after specific rules.
Correct:
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
)
67. Tiny Bug Practice 3
Problem:
@PostMapping("/api/tasks")
public TaskDto create(@RequestBody CreateTaskRequest request) {
return taskService.create(request.userId(), request.title());
}
Question:
Why is this dangerous?
Answer:
The client sends userId, so the client can pretend to be another user. Use the authenticated principal from Spring Security instead of trusting user identity from the request body.
68. Tiny Bug Practice 4
Problem:
@Service
public class TaskService {
public TaskDto findTask(Long id) {
return securedFindTask(id);
}
@PreAuthorize("hasAuthority('TASK_READ')")
public TaskDto securedFindTask(Long id) {
return ...
}
}
Question:
What is the trap?
Answer:
This is self-invocation. A method inside the same class calls the secured method directly, possibly bypassing the Spring security proxy. Put @PreAuthorize on the public method called from outside, or move the secured method to another Spring bean.
Practice Questions and Answers
Question 1
What is authorization?
Answer:
Authorization is the process of deciding what an authenticated user is allowed to access or do.
Question 2
What is the difference between authentication and authorization?
Answer:
Authentication proves who the user is. Authorization checks what the user is allowed to do.
Question 3
What is an authority?
Answer:
An authority is a permission granted to a user, such as ROLE_ADMIN, TASK_READ, or INVOICE_APPROVE.
Question 4
What is a role?
Answer:
A role is usually a broad group of permissions and is commonly represented as an authority with the ROLE_ prefix.
Question 5
What does hasRole("ADMIN") check?
Answer:
It checks whether the user has authority ROLE_ADMIN.
Question 6
What does hasAuthority("TASK_READ") check?
Answer:
It checks whether the user has exactly the authority TASK_READ.
Question 7
Why is hasRole("ROLE_ADMIN") usually wrong?
Answer:
Because hasRole adds the ROLE_ prefix automatically. hasRole("ROLE_ADMIN") may effectively look for ROLE_ROLE_ADMIN.
Question 8
Why does URL rule order matter?
Answer:
Rules are evaluated in order. Specific rules must come before general catch-all rules like anyRequest().
Question 9
What does permitAll() mean?
Answer:
permitAll() means anyone can access, including unauthenticated users.
Question 10
What does authenticated() mean?
Answer:
authenticated() means the user must be logged in or otherwise authenticated.
Question 11
What is method security?
Answer:
Method security means applying authorization rules to method calls, often service methods.
Question 12
How do I enable method security?
Answer:
Use @EnableMethodSecurity on a configuration class.
Question 13
What does @PreAuthorize do?
Answer:
@PreAuthorize checks an authorization expression before the method runs.
Question 14
What does @PostAuthorize do?
Answer:
@PostAuthorize checks authorization after the method returns, often using the returned object.
Question 15
How can @PreAuthorize use method parameters?
Answer:
Use #parameterName in the expression, such as @PreAuthorize("#userId == authentication.principal.id").
Question 16
How can @PreAuthorize call a custom permission service?
Answer:
Reference the bean by name, for example @PreAuthorize("@permissionService.canAccessTask(#taskId, authentication)").
Question 17
What is data-level permission?
Answer:
Data-level permission means access depends on the specific resource or data, such as ownership or tenant membership.
Question 18
Why are URL rules not enough for tenant security?
Answer:
URL rules can check broad access to endpoints, but they usually cannot know whether the requested client, task, invoice, or document belongs to the current user’s tenant.
Question 19
Why should I not trust client-sent user IDs?
Answer:
Because the client can manipulate request data and pretend to be another user. Use the authenticated principal instead.
Question 20
What is the self-invocation trap with method security?
Answer:
Self-invocation happens when one method in the same class calls another secured method directly. The call may bypass the Spring proxy, so method security may not apply.
Final Memory Sentences
- Authorization checks permissions.
- Authentication proves identity.
- Authorities are permissions.
- Roles are authorities with
ROLE_prefix. hasRole("ADMIN")checksROLE_ADMIN.hasAuthority("TASK_READ")checks exactlyTASK_READ.- Do not use
hasRole("ROLE_ADMIN"). - URL authorization protects endpoints.
- Rule order matters.
- Specific rules go before
anyRequest(). - URL security is coarse-grained.
- Method security is fine-grained.
- Enable method security with
@EnableMethodSecurity. @PreAuthorizechecks before method execution.@PostAuthorizechecks after method execution.@PreAuthorizecan use method parameters.@PreAuthorizecan call custom permission beans.- Data-level permissions belong in service/domain logic.
- Tenant security must check tenant ownership.
- Never trust client-sent user identity.
- Backend must enforce authorization even if frontend hides buttons.
- Repository queries can include tenant or owner scope.
- Method security works on Spring-managed beans.
- Self-invocation can bypass method security.