Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Implementing Dynamic Permission Verification in Spring Boot

Tech 3

Background Requirements

Custom login authentication is required where successful authentication generates a token managed by Redis. Post-login, interface-level permission verification must be performed on user-accessed endpoints. Spring Security's annotation-based permission checking is suitable for systems with fixed roles where credentials are static. For example:

@PreAuthorize("hasAuthority('ROLE_TELLER')")
public Account post(Account account, double amount);

Here, ROLE_TELLER is hardcoded. Backend system access requests fall into these categories:

  • Login/logout endpoints (customizable URLs)
  • Interfaces accessible to anonymous users (static resources, demo endpoints)
  • Other endpoints requiring authentication and additional permission checks

Environment Setup

Required dependencies include Spring Security, Redis, and Redis Session support:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
    <version>2.3.4.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
    <version>2.3.1.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <version>2.3.4.RELEASE</version>
</dependency>

Note: Spring Boot version 2.3.4.RELEASE is used. Version compatibility should be verified independently. Swagger is included for testing convenience.

Security Configuration Class

Create SecurityConfiguration.java extending WebSecurityConfigurerAdapter to configure access rules for anonymous endpoints:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    
    @Override
    public void configure(WebSecurity web) {
        web.ignoring().antMatchers(
                "/*.html",
                "/favicon.ico",
                "/**/*.html",
                "/**/*.css",
                "/**/*.js",
                "/error",
                "/webjars/**",
                "/resources/**",
                "/swagger-ui.html",
                "/swagger-resources/**",
                "/v2/api-docs");
    }
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/demo/**", "/about/**").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/custom-login");
    }
}

This configuration allows static resource access without authentication.

Custom Authentication Components

Spring Security uses filter-based authentication. Custom implementations are needed for:

  • Authentication Filter: Intercepts login requests and delegates to authentication manager
  • Success Handler: Processes successful authentication (returns JSON response)
  • Failure Handler: Manages authentication failures
  • Authentication Provider: Pefrorms actual credential verification and authorization

Success Handler Implementation

Implement AuthenticationSuccessHandler:

@Component
public class LoginSuccessHandler implements AuthenticationSuccessHandler {
    
    private static final Logger logger = LoggerFactory.getLogger(LoginSuccessHandler.class);
    
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, 
                                      HttpServletResponse response, 
                                      Authentication auth) throws IOException {
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setCharacterEncoding(StandardCharsets.UTF_8.toString());
        String jsonResponse = JsonUtils.toJson(Response.success(auth));
        if (logger.isDebugEnabled()) {
            logger.debug("Authentication successful!");
        }
        response.getWriter().write(jsonResponse);
    }
}

Failure Handler Implementation

Implement AuthenticationFailureHandler:

@Component
public class LoginFailureHandler implements AuthenticationFailureHandler {
    
    private static final Logger logger = LoggerFactory.getLogger(LoginFailureHandler.class);
    
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, 
                                       HttpServletResponse response, 
                                       AuthenticationException ex) throws IOException {
        String errorMessage = StringUtils.hasText(ex.getMessage()) ? 
                            ex.getMessage() : ErrorCode.AUTH_FAILED.getMessage();
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setCharacterEncoding(StandardCharsets.UTF_8.toString());
        String jsonResponse = JsonUtils.toJson(Response.failure(ErrorCode.AUTH_FAILED, errorMessage));
        if (logger.isDebugEnabled()) {
            logger.debug("Authentication failed!");
        }
        response.getWriter().write(jsonResponse);
    }
}

Authentication Provider Implementation

Implement AuthenticationProvider for database-based verification:

@Component
public class DatabaseAuthenticationProvider implements AuthenticationProvider {
    
    private PasswordEncoder encoder;
    @Autowired
    private UserService userService;
    
    @Override
    public Authentication authenticate(Authentication auth) throws AuthenticationException {
        String username = (String) auth.getPrincipal();
        String password = (String) auth.getCredentials();
        UserWithRoles userData = userService.findUserWithRolesByUsername(username);
        String storedPassword = userData.getPassword();
        String decodedPassword = new String(Base64.getDecoder().decode(password), StandardCharsets.UTF_8);
        encoder = new MD5PasswordEncoder();
        if (!encoder.matches(decodedPassword, storedPassword)) {
Tags: Spring Boot

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.