I have read How to apply spring security filter only on secured endpoints?, which seems to be the closest to my question, but does not sufficiently answer it.
Further below you will see a WebSecurityConfigurerAdapter-configuration I am currently using. It will not remain like this as I will not expose h2-console later on.
My problem is, that JwtAuthenticationFilter
is always executed. I'd rather want the filter to be executed on requests, which demand authentication (in my particular case: only what's described here:
.authorizeRequests()
.anyRequest()
.authenticated()
).
How to achieve this?
P.s.: my application login works as expected while H2-console does, too, but keeps throwing io.jsonwebtoken.SignatureException
, because the JWT H2-console generates and uses is naturally different from the one my application uses.
WebSecurityConfigurerAdapter:
package com.particles.authservice;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import com.particles.authservice.jwt.JwtAuthenticationEntryPoint;
import com.particles.authservice.jwt.JwtAuthenticationFilter;
import com.particles.authservice.service.UserService;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private UserService userService;
@Autowired
private JwtAuthenticationEntryPoint unauthorizedHandler;
@Override
public void configure(final AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder
.userDetailsService(userService)
.passwordEncoder(passwordEncoder());
}
@Override
protected void configure(final HttpSecurity http) throws Exception {
//@formatter:off
http
.cors()
.and()
.csrf()
.disable()
.headers()
.frameOptions()
.disable()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/",
"/favicon.ico",
"/**/*.png",
"/**/*.gif",
"/**/*.svg",
"/**/*.jpg",
"/**/*.html",
"/**/*.css",
"/**/*.js")
.permitAll()
.antMatchers("/h2-console/**").permitAll()
.antMatchers(HttpMethod.POST, "/register")
.permitAll()
.antMatchers(HttpMethod.GET, "/confirm")
.permitAll()
.antMatchers(HttpMethod.POST, "/login")
.permitAll()
.antMatchers(HttpMethod.GET, "/user")
.permitAll()
.and()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint(unauthorizedHandler)
.and()
.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
;
//@formatter:on
}
@Bean(BeanIds.AUTHENTICATION_MANAGER)
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() {
return new JwtAuthenticationFilter();
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Edit: here's the JwtAuthenticationFilter. If you need the TOs as well, let me know.
JwtAuthenticationFilter:
package com.particles.authservice.jwt;
import java.io.IOException;
import java.util.Optional;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import com.particles.authservice.tos.UserJwt;
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private static final String AUTHORIZATION_HEADER_PREFIX = "Authorization";
private static final String AUTHORIZATION_HEADER_BEARER_PREFIX = "Bearer ";
private static final int AUTHORIZATION_HEADER_BEARER_PREFIX_LENGTH = AUTHORIZATION_HEADER_BEARER_PREFIX.length();
@Autowired
private JwtService jwtService;
@Override
protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response, final FilterChain filterChain)
throws ServletException, IOException {
if (request.getHeader(AUTHORIZATION_HEADER_PREFIX) != null) {
final Optional<String> optToken = extractTokenFromRequest(request);
if (optToken.isPresent() && StringUtils.hasText(optToken.get()) && jwtService.isTokenValid(optToken.get())) {
// if token exists and is valid, retrieve corresponding UserJwt-object
final UserJwt jwt = jwtService.getJwtFromToken(optToken.get());
final UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(jwt.getUser(), null,
jwt.getUser().getAuthorities());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
}
filterChain.doFilter(request, response);
}
/**
* This method extracts a JWT from a {@link HttpServletRequest}-object.
*
* @param request
* ({@link HttpServletRequest}) request, which supposedly contains a JWT
* @return (Optional<String>) JWT as String
*/
private Optional<String> extractTokenFromRequest(final HttpServletRequest request) {
final String bearerToken = request.getHeader(AUTHORIZATION_HEADER_PREFIX);
String bearerTokenContent = null;
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(AUTHORIZATION_HEADER_BEARER_PREFIX)) {
bearerTokenContent = bearerToken.substring(AUTHORIZATION_HEADER_BEARER_PREFIX_LENGTH, bearerToken.length());
}
return Optional.ofNullable(bearerTokenContent);
}
}
If you need to see any other classes, tell me and I will paste them here.