Spring/Spock - integration test of authentication
Asked Answered
P

0

1

I want to test protected endpoints in my application and I would like login before each test, get token and use this token in next tests.

@ContextConfiguration
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Stepwise
class IntegrationSpec extends Specification {

    @Autowired
    private TestRestTemplate rest

    @Autowired
    private String token

    @Shared
    private HttpEntity request

    def setup() {
        UserData userData = new UserData("Username", "Password");

        def response = rest.postForEntity('/login', userData, Object)

        String tempToken = response.getHeaders().get('Authorization')
        // remove prefix of token
        this.token = tempToken.substring(tempToken.lastIndexOf(" ") + 1)

        String loginToken = token.substring(token.lastIndexOf(" ") + 1)

        final MultiValueMap<String, String> headers = new LinkedMultiValueMap<>()
        headers.add('Authorization', loginToken)
        this.request = new HttpEntity(headers)
    }

    def 'protected resource test'() {
        when:
            def response = rest.exchange('/protected/resource', HttpMethod.GET, request, new ParameterizedTypeReference<String>() {})

        then:
            response.getStatusCode() == HttpStatus.OK
    }
}

but I get an error :

io.jsonwebtoken.SignatureException: JWT signature does not match locally computed signature. JWT validity cannot be asserted and should not be trusted.
    at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:354) ~[jjwt-0.7.0.jar:0.7.0]
    at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:481) ~[jjwt-0.7.0.jar:0.7.0]
    at io.jsonwebtoken.impl.DefaultJwtParser.parseClaimsJws(DefaultJwtParser.java:541) ~[jjwt-0.7.0.jar:0.7.0]
    at mypackages.TokenAuthenticationService.getAuthentication(TokenAuthenticationService.java:39) ~[classes/:na]
    at mypackages.filters.JWTAuthenticationFilter.doFilter(JWTAuthenticationFilter.java:23) ~[classes/:na]

my TokenService looks following:

public class TokenAuthenticationService {

    static final long EXPIRATION_TIME = 10000000;
    static final String SECRET = "secret_word";
    static final String TOKEN_PREFIX = "Bearer";
    static final String HEADER_STRING = "Authorization";

    public static void addAuthentication(HttpServletResponse res, String username) {
        String JWT = Jwts.builder()
                .setSubject(username)
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
                .signWith(SignatureAlgorithm.HS512, SECRET)
                .compact();
        res.addHeader(HEADER_STRING, TOKEN_PREFIX + " " + JWT);
    }

    public static Authentication getAuthentication(HttpServletRequest request) {
        String token = request.getHeader(HEADER_STRING);
        if (token != null) {
            String user = Jwts.parser()
                    .setSigningKey(SECRET)
                    .parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
                    .getBody()
                    .getSubject();

            return user != null ?
                    new UsernamePasswordAuthenticationToken(user, null, new ArrayList<GrantedAuthority>()) :
                    null;
        }

        return null;
    }
}

JWTAuthenticationFilter:

public class JWTAuthenticationFilter extends GenericFilterBean {

    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
        Authentication authentication = TokenAuthenticationService.getAuthentication((HttpServletRequest) servletRequest);

        SecurityContextHolder.getContext()
                .setAuthentication(authentication);

        filterChain.doFilter(servletRequest, servletResponse);
    }
}

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public UserDetailsService customUserDetailsService() {
        return new CustomUserDetailsService();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                .antMatchers(HttpMethod.POST, "/login").permitAll()
                .antMatchers(HttpMethod.POST, "/register").permitAll()
                .anyRequest().authenticated()
                .and()
                .addFilterBefore(new JWTLoginFilter("/login", authenticationManager()), UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(new JWTAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(customUserDetailsService());
    }
}
Pinelli answered 17/7, 2017 at 11:15 Comment(2)
Authorization →Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJEZWZhdWx0VXNlciIsImV4cCI6MTUwMDI5NDc4OH0.xPrnsO8000RjvlLTE6VH6mHpyxu6bumj6t-VC0chBO0OxFje4l6Bx5JEo5pwJdCLk2w3Li8EyMNUFjJi4BxIXA - from postmanPinelli
The payload of your token is not correct because does not include valid JSON. Probably username contains invalid characters or the JWT has been altered between server and client. You can check it in jwt.io. The site shows the payload as emptyEveretteeverglade

© 2022 - 2024 — McMap. All rights reserved.