Swiftorial Logo
Home
Swift Lessons
Tutorials
Learn More
Career
Resources

Custom Security Expressions in Spring Security

Custom Security Expressions allow you to define custom access control logic in your Spring Boot application. This guide covers key concepts and steps for creating and configuring custom security expressions, including adding dependencies, implementing the custom expressions, and configuring Spring Security.

Key Concepts of Custom Security Expressions

  • Security Expressions: Expressions used to enforce access control in Spring Security.
  • Custom Security Expressions: User-defined expressions that contain custom access control logic.
  • Security Configuration: Configuring Spring Security to use the custom security expressions.

Adding Dependencies

Include the Spring Security dependency in your pom.xml file:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

Implementing the Custom Security Expressions

Create a class that implements custom security expressions:

Example: CustomSecurityExpressionRoot.java

// CustomSecurityExpressionRoot.java
package com.example.myapp.security;

import org.springframework.security.access.expression.SecurityExpressionRoot;
import org.springframework.security.access.expression.method.MethodSecurityExpressionOperations;
import org.springframework.security.core.Authentication;

public class CustomSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {

    public CustomSecurityExpressionRoot(Authentication authentication) {
        super(authentication);
    }

    public boolean isAdmin() {
        return hasRole("ADMIN");
    }

    private boolean hasRole(String role) {
        return getAuthentication().getAuthorities().stream()
                .anyMatch(grantedAuthority -> grantedAuthority.getAuthority().equals("ROLE_" + role));
    }

    @Override
    public void setFilterObject(Object filterObject) {
    }

    @Override
    public Object getFilterObject() {
        return null;
    }

    @Override
    public void setReturnObject(Object returnObject) {
    }

    @Override
    public Object getReturnObject() {
        return null;
    }

    @Override
    public Object getThis() {
        return this;
    }
}

Creating a Custom Security Expression Handler

Create a custom security expression handler to integrate the custom expressions:

Example: CustomMethodSecurityExpressionHandler.java

// CustomMethodSecurityExpressionHandler.java
package com.example.myapp.security;

import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.core.Authentication;

public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {

    @Override
    protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, MethodInvocation invocation) {
        CustomSecurityExpressionRoot root = new CustomSecurityExpressionRoot(authentication);
        return root;
    }
}

Configuring Spring Security

Ensure Spring Security is configured to use the custom security expression handler:

Example: SecurityConfiguration.java

// SecurityConfiguration.java
package com.example.myapp.config;

import com.example.myapp.security.CustomMethodSecurityExpressionHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends GlobalMethodSecurityConfiguration {

    @Override
    protected MethodSecurityExpressionHandler createExpressionHandler() {
        return new CustomMethodSecurityExpressionHandler();
    }
}

Using Custom Security Expressions

Use the custom security expressions in your application:

Example: MyService.java

// MyService.java
package com.example.myapp.service;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;

@Service
public class MyService {

    @PreAuthorize("isAdmin()")
    public String adminMethod() {
        return "Admin access granted";
    }

    @PreAuthorize("hasRole('USER')")
    public String userMethod() {
        return "User access granted";
    }
}

Testing Custom Security Expressions

Test your custom security expressions to ensure they work as expected:

Example: SecurityTests.java

// SecurityTests.java
package com.example.myapp;

import com.example.myapp.config.TestSecurityConfig;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest
@ContextConfiguration(classes = TestSecurityConfig.class)
public class SecurityTests {

    @Autowired
    private WebApplicationContext context;

    private MockMvc mockMvc;

    @Test
    @WithMockUser(username = "admin", roles = {"ADMIN"})
    public void testAdminMethod() throws Exception {
        mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
        mockMvc.perform(get("/admin-method"))
                .andExpect(status().isOk())
                .andExpect(content().string("Admin access granted"));
    }

    @Test
    @WithMockUser(username = "user", roles = {"USER"})
    public void testUserMethod() throws Exception {
        mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
        mockMvc.perform(get("/user-method"))
                .andExpect(status().isOk())
                .andExpect(content().string("User access granted"));
    }
}

Key Points

  • Security Expressions: Expressions used to enforce access control in Spring Security.
  • Custom Security Expressions: User-defined expressions that contain custom access control logic.
  • Security Configuration: Configuring Spring Security to use the custom security expressions.
  • Include the Spring Security dependency in your pom.xml file.
  • Create a class that implements custom security expressions.
  • Create a custom security expression handler to integrate the custom expressions.
  • Ensure Spring Security is configured to use the custom security expression handler.
  • Use the custom security expressions in your application.
  • Test your custom security expressions to ensure they work as expected.

Conclusion

Creating custom security expressions in Spring Security allows you to define custom access control logic tailored to your application's needs. By understanding and implementing custom security expressions, configuring Spring Security, and testing your expressions, you can ensure that your Spring Boot application enforces access control effectively and securely. Happy coding!