0

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

suvodipMondal
  • 656
  • 10
  • 27
  • 1
    why are you writing a custom security solution. Spring security comes with a set of predefined login solutions that you can customize. The tutorial you have been following is teaching bad practices. Please read the chapter on FormLogin which means you can basically remove 70% of your code including your custom filter. https://docs.spring.io/spring-security/reference/servlet/authentication/passwords/form.html also using JWTs as session tokens is very insecure – Toerktumlare Jul 17 '22 at 14:20
  • Yes got your point and it makes sense, but I was learning to customize things there – suvodipMondal Jul 17 '22 at 15:18
  • 2
    learn how to customize spring security correctly then by using the spring security built in JWTFilter and customize that. You have most likely been reading outdated blogs that havn't even read the spring security docs. For instance, spring security already has a jwt lib included called Nimbus, but you are adding yet another one (auth0) which is adding bloat to the application and jar. And as pointed out using JWTs as sessions is very insecure, because for instance in your solution now, you cant logout users, and if a token gets stolen you have no way of stopping malicious usage – Toerktumlare Jul 17 '22 at 18:17
  • 1
    whats the point of using a security framework if you do not intend to use its features and instead write potentially insecure code. – Toerktumlare Jul 17 '22 at 18:17
  • Thanks @Toerktumlare, I will keep that in mind – suvodipMondal Jul 18 '22 at 02:58

1 Answers1

0

Have you tried adding component scan and added the package for the class where filter is defined?

If not try adding the base package, also alternatively try the configuration for filter chain proxy

<bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy">
  <sec:filter-chain-map path-type="ant">
     <sec:filter-chain pattern="/webServices/**" filters="
           securityContextPersistenceFilterWithASCFalse,
           basicAuthenticationFilter,
           exceptionTranslationFilter,
           filterSecurityInterceptor" />
     <sec:filter-chain pattern="/**" filters="
           securityContextPersistenceFilterWithASCTrue,
           formLoginFilter,
           exceptionTranslationFilter,
           filterSecurityInterceptor" />
  </sec:filter-chain-map>
</bean>
Amit Mahajan
  • 895
  • 6
  • 34