Home > OS >  Spring boot Custom authentication filter is not applied
Spring boot Custom authentication filter is not applied

Time:07-17

Hi I am learning about spring security, I am stuck in the custom authentication filter. I have the following files: The main application file: SpringAuthApplication.java

package com.example.jwtauth;

import java.util.ArrayList;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

import com.example.jwtauth.models.Role;
import com.example.jwtauth.models.User;
import com.example.jwtauth.service.UserService;

@SpringBootApplication
public class SpringAuthApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringAuthApplication.class, args);
    }
    
    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    
    @Bean
    CommandLineRunner run(UserService userService) {
        return args -> {
            userService.saveRole(new Role(null, "ROLE_USER"));
            userService.saveRole(new Role(null, "ROLE_MANAGER"));
            userService.saveRole(new Role(null, "ROLE_ADMIN"));
            userService.saveRole(new Role(null, "ROLE_SUPER_ADMIN"));
            
            userService.saveUser(new User(null, "Suvodip Mondal", "s.mondal", "1234", new ArrayList<Role>()));
            userService.addRoleToUser("s.mondal", "ROLE_SUPER_ADMIN");
            
            userService.saveUser(new User(null, "Akash Arora", "a.arora", "1234", new ArrayList<Role>()));
            userService.addRoleToUser("a.arora", "ROLE_ADMIN");
            
            userService.saveUser(new User(null, "Shubham Pathak", "s.pathak", "1234", new ArrayList<Role>()));
            userService.addRoleToUser("s.pathak", "ROLE_MANAGER");
            
            userService.saveUser(new User(null, "Karan Sharma", "k.sharma", "1234", new ArrayList<Role>()));
            userService.addRoleToUser("k.sharma", "ROLE_USER");
        };
    }

}

UserController.java:

package com.example.jwtauth.controllers;

import java.net.URI;
import java.util.List;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

import com.example.jwtauth.models.User;
import com.example.jwtauth.service.UserService;

import lombok.Data;
import lombok.RequiredArgsConstructor;

@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
public class UserController {
    private final UserService userService;

    @GetMapping("")
    public ResponseEntity<List<User>> getAllUsers() {
        return ResponseEntity.ok(userService.listAllUsers());
    }

    @PostMapping("/create")
    public ResponseEntity<User> createUser(@RequestBody User user) {
        // ServletUriComponentsBuilder.fromCurrentContextPath() - http://localhost:8080
        URI uri=  URI.create(ServletUriComponentsBuilder.fromCurrentContextPath().path("/api/user/create").toUriString());
        return ResponseEntity.created(uri).body(userService.saveUser(user));
    }

    @PostMapping("/add-role")
    public ResponseEntity<User> createRole(@RequestBody RoleToUserForm form) {
        userService.addRoleToUser(form.getUsername(), form.getRoleName());
        return ResponseEntity.ok().build();
    }
    
}

@Data
class RoleToUserForm {
    private String username;
    private String roleName;
}

My Security config file SecurityConfig.java:

package com.example.jwtauth.security;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import com.example.jwtauth.service.filter.CustomAuthenticationFilter;

import org.springframework.security.config.http.SessionCreationPolicy;


import lombok.RequiredArgsConstructor;

@SuppressWarnings("deprecation")
@Configuration @EnableWebSecurity @RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    private final BCryptPasswordEncoder encoder;
    private final UserDetailsService userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(encoder);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        System.out.println("-------------------Configure----------------------------------------");
        CustomAuthenticationFilter filter = new CustomAuthenticationFilter(authenticationManager());
//      filter.setFilterProcessesUrl("/api/users/create");
        http.csrf().disable();
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        http.authorizeRequests().anyRequest().permitAll();
        http.addFilter(filter);
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManagerBean();
    }
}

At last my CustomAuthentication filter file: CustomAuthenticationFilter.java:

package com.example.jwtauth.service.filter;

import java.io.IOException;
import java.sql.Date;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;


public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
    private final AuthenticationManager authenticationManager;

    public CustomAuthenticationFilter(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException {
        System.out.println("---------------request------" request);
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
        
        return authenticationManager.authenticate(token);
    }   

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
            Authentication authResult) throws IOException, ServletException {
        User user = (User)authResult.getPrincipal();
        System.out.println("---------------user----------" user);
        Algorithm algorithm = Algorithm.HMAC256("secret".getBytes());
        String access_token = JWT.create()
                .withSubject(user.getUsername())
                .withExpiresAt(new Date(System.currentTimeMillis() 10*60*1000))
                .withIssuer(request.getRequestURI().toString())
                .sign(algorithm);
        String refresh_token = JWT.create()
                .withSubject(user.getUsername())
                .withExpiresAt(new Date(System.currentTimeMillis() 30*60*1000))
                .withIssuer(request.getRequestURI().toString())
                .sign(algorithm);
        
        System.out.println("---------------access token------------" access_token);
        System.out.println("---------------refresh token------------" refresh_token);
        response.setHeader("access_token", access_token);
        response.setHeader("refresh_token", refresh_token);
        response.setContentType(APPLICATION_JSON_VALUE);
    }
}

So I have put logs the attemptAuthentication method but it seems the request is not going there. Also the tutorial I was following they were calling the API with formurlencoded but in my case I am getting HttpMediaTypeNotSupportedException but application/json is working there. But I believe data type is not the problem, the request should go to attemptAuthentication method at least. I am not getting what is wrong there, for reference I am adding project github link: https://github.com/smondal229/UserAuthService

CodePudding user response:

I guess you must add your filter before the default.

http.addFilterBefore(filter, 
        UsernamePasswordAuthenticationFilter.class)

CodePudding user response:

Looking at UsernamePasswordAuthenticationFilter constructors:

public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
    public UsernamePasswordAuthenticationFilter() {
        super(DEFAULT_ANT_PATH_REQUEST_MATCHER);
    }
    
    // ⬇ This constructor accept a AuthenticationManager 
    public UsernamePasswordAuthenticationFilter(AuthenticationManager authenticationManager) { 
        super(DEFAULT_ANT_PATH_REQUEST_MATCHER, authenticationManager);
    }
    // Others omitted
}

My guess is that, you need to use super() consturctor, in order to pass in AuthenticationManager:

    public CustomAuthenticationFilter(AuthenticationManager authenticationManager) {
        super(authManager);
    }

BTW. You don't need to keep AuthenticationManager authenticationManager as a member of the class, because UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter class, and there's a method called getAuthenticationManager in AbstractAuthenticationProcessingFilter:

public abstract class AbstractAuthenticationProcessingFilter extends GenericFilterBean
        implements ApplicationEventPublisherAware, MessageSourceAware {

    private AuthenticationManager authenticationManager;

    protected AuthenticationManager getAuthenticationManager() {
        return this.authenticationManager;
    }
    // Others omitted
}

So you can call getAuthenticationManager directly in your custom filter:

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException {
        System.out.println("---------------request------" request);
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
        
        return getAuthenticationManager().authenticate(token); // <- here
    } 

If OP is trying to setup security for rest api through custom filter, you can take a look this answer I wrote yesterday. It'll help you set up the basics, and it's already a working demo, with some simple role-based and permission-based authentication/authorization, so feel free to add anything you need onto it(like token).

  • Related