1
votes

I have a spring MVC application using spring security 4 and I want to add authorization based on the jwt token in the request. what I need to do in the filter is to

  1. Take token from request header
  2. Send the token to external API and get the user details
  3. Set the fetched details in the Security Context

But when I start the application, I get an error saying An AuthenticationManager is required. I am not sure how the UserDetails service apply for my use case. Hence I have added a dummy return value for testing since without the UserDetailsService application is not working. Any idea on this?

Spring security config class

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(
  prePostEnabled = true
)
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired
@Qualifier("applicationUserService")
UserDetailsService userDetailsService;

@Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
  SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
  authenticationManagerBuilder
    .userDetailsService(userDetailsService)
    .passwordEncoder(bCryptPasswordEncoder());
}

@Override
protected void configure(HttpSecurity http) throws Exception {
  http.csrf().disable().authorizeRequests()
    .antMatchers(HttpMethod.GET, "/home").hasAnyRole("ADMIN")
    .antMatchers(HttpMethod.GET, "/login").hasAnyRole("ADMIN")
    .anyRequest().authenticated()
    .and()
    .addFilter(new AuthorizationFilter(authenticationManager()))
    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}

@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}

Authorization Filter class

public class AuthorizationFilter extends BasicAuthenticationFilter {

private static final Logger LOGGER = LoggerFactory.getLogger(AuthorizationFilter.class);

public AuthorizationFilter(AuthenticationManager authenticationManager) {
  super(authenticationManager);
}

@Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
  LOGGER.info("Request Info : {}", req.getRequestURI());
  // get token
  // fetch details from external API
  // set security context
  List<GrantedAuthority> authorities = new ArrayList<>();
  authorities.add((GrantedAuthority) () -> "ROLE_ADMIN");
  SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken("user1", null, authorities));
  LOGGER.info("security context principle:{}", SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString());
  LOGGER.info("authorities context:{}", SecurityContextHolder.getContext().getAuthentication().getAuthorities().toString());

  chain.doFilter(req, response);
}

UserDetailsService implementation

@Service
@Qualifier("applicationUserService")
public class ApplicationUserServiceImpl implements UserDetailsService {

  @Override
  public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
    return new User("sidath", "123", emptyList());
  }
}
1

1 Answers

0
votes

Try this steps

  1. Define a AbstractAuthenticationProcessingFilter to evaluate request and return token.

public class AwesomeFilter extends AbstractAuthenticationProcessingFilter {
    
    public AuthorizationFilter () {
       super(new AntPathRequestMatcher("/your_post_url", "POST"));
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) {
        
       // Evaluate request...
    
       // Build your custom authentication token with the info
       AwesomeToken token = new AwesomeToken();

       // Authenticate token with authentication manager
        return getAuthenticationManager().authenticate(token);
    }
  1. Define a AuthenticationProvider to supports your AwesomeToken. Spring Security will try to "supports" this.
public class AwesomeProvider implements AuthenticationProvider {
    
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {

      // Evaluate your custom token
      // Call your API, etc

      // Build your user authentication token details with authorities
         Collection<? extends GrantedAuthority> auths = Collections.singletonList(new 
                SimpleGrantedAuthority("ROLE_ADMIN"));
         AwesomeUserToken token = new AwesomeUserToken(auths);

      // Return user token
         return token;
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return (AwesomeToken.class.isAssignableFrom(authentication));
    }
  1. Register classes in Spring Security Config.
    @Override
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth
           .authenticationProvider(new AwesomeProvider());
    }

    @Override
    public void addFilters(HttpSecurity http, AuthenticationManager authenticationManager) throws Exception {

        AwesomeFilter filter = new AwesomeFilter();
        filter.setAuthenticationManager(authenticationManager);
        http
            .addFilterAfter(filter, UsernamePasswordAuthenticationFilter.class);
    }

Finally, when Spring Security detects the request with filter, it will try support with the provider and then return the token with the required authorities.