Read the already validated JWT token in a different micro service

Viewed 279

I have two microservices i.e Gateway service and User service. Gateway service redirects the calls of login to user service. User service will generate jwt token with info like roles and permissions etc and append it to response header.

Now clients can call user service for user create. In the request jwt token is passed and gateway service validates the token and forwards the call to user service. As I need to check whether the logged in user has write access or not (based on @PreAuthorize) I need to read the JWT token again in user service and extract the roles and permissions.

Gateway service WebSecurity class configure method

protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();
        http.headers().frameOptions().disable();

        http.authorizeRequests()
        .antMatchers(HttpMethod.POST, "/user/signup").permitAll()
        .antMatchers(HttpMethod.POST, "/user/signin").permitAll()
        .anyRequest().authenticated()
        .and()
        .addFilter(new AuthenticationFilter(authenticationManager(), environment));


        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

AuthenticationFilter.Class

public class AuthenticationFilter extends BasicAuthenticationFilter{

    private final Environment environment;

    public AuthenticationFilter(AuthenticationManager authenticationManager, Environment environment) {
        super(authenticationManager);

        this.environment = environment;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        String authHeader = request.getHeader(environment.getProperty("auth.token.header.name"));

        if (authHeader == null || !authHeader.startsWith("Bearer")) {
            chain.doFilter(request, response);
            return;
        }

        UsernamePasswordAuthenticationToken authentication = getAuthentication(request);

        SecurityContextHolder.getContext().setAuthentication(authentication);

        chain.doFilter(request, response);
    }

    private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
        String authHeader = request.getHeader(environment.getProperty("auth.token.header.name"));

        //validate jwt token code
    }

In the application.properties enable this so that jwt token info in the header is passed to user service from gateway.

zuul.routes.user-service.sensitive-headers=Cookie,Set-Cookie

User Service WebSecurity class configure method

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();
        http.authorizeRequests().antMatchers("/**").hasIpAddress(environment.getProperty("api.gateway.ip"))
        .and()
        .addFilter(getAuthenticationFilter());
        http.headers().frameOptions().disable();
    }

    private AuthenticationFilter getAuthenticationFilter() throws Exception{
                AuthenticationFilter filter = new AuthenticationFilter(usersService, environment, authenticationManager());
        filter.setFilterProcessesUrl("/signin");
        return filter;
    }

UserService AuthenticationFilter class

public class AuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private final AuthenticationService userService;
    private final Environment environment;

    @Autowired
    public AuthenticationFilter(AuthenticationService userService, Environment environment, AuthenticationManager authManager) {
        this.userService = userService;
        this.environment = environment;
        super.setAuthenticationManager(authManager);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException {
        try {
            LoginRequestModel creds = new ObjectMapper().readValue(request.getInputStream(), LoginRequestModel.class);

            return getAuthenticationManager().authenticate(
                        new UsernamePasswordAuthenticationToken(creds.getEmail(), creds.getPasssword(), new ArrayList<>())
                    );
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
            Authentication authResult) throws IOException, ServletException {
        //generate jwt token code
    }
}

Now how can I read the values of jwt token sent by gateway service and set to security context authentication. Do I need to write one more filter. Where that filter would come. That filter should not be executed for sign up or login. Any help would be appreciated.

1 Answers

Your first filter is set to cater for /signin as per your code:

filter.setFilterProcessesUrl("/signin");

Now, you would need a second filter, to cater for everything else, example:

.and()
.addFilter(getAuthenticationFilter())
.addFilter(getAuthorizationFilter());

...

@Bean
public AuthorizationFilter getAuthorizationFilter(){
    AuthorizationFilter a = new AuthorizationFilter(authenticationManager());
    a.setSecret(secret);

    return a;
}

You could for example:

public class AuthorizationFilter extends BasicAuthenticationFilter {
    ....
    @Override
    protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res,
        FilterChain chain) throws IOException, ServletException {

        //try/catch

        String jwt = getJWT(r);

        if(jwt != null){
            Authentication a = getAuthentication(jwt);

            if(a != null){
                SecurityContextHolder.getContext().setAuthentication(a);
            }
        }

        chain.doFilter(req, res);
    }

    private String getJWT(HttpServletRequest r){
        String bearerToken = r.getHeader("Authorization");

        //Do your checks here

        return ...;
    }

    private Authentication getAuthentication(String jwt){
        //Parse the jwt etc

        return new UsernamePasswordAuthenticationToken(...);
    }
}

As for Zuul, you also need to add: ignoredServices: '*'

Related