I'm a newbie. My spring boot version is 2.7.0
This is my security configurattion
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
@RequiredArgsConstructor
public class SecurityConfiguration {
private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
private final CustomAuthorizationFilter customAuthorizationFilter;
/**
* Filter beans are registered with the servlet container automatically.
* CustomAuthorizationFilter with @Component annotation.
* CustomAuthorizationFilter is added to servlet filter chain and security filter chain.
* Hence, it is executed twice (it is also registered twice).
*/
@Bean
public FilterRegistrationBean registration(CustomAuthorizationFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean(filter);
registration.setEnabled(false);
return registration;
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.cors();
http.csrf().disable(); //because don't use form login
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint);
http.authorizeRequests().antMatchers(
API_V1_AUTH_ROOT_URL "/**",
API_V1_COMMON_ROOT_URL "/**").permitAll();
http.authorizeRequests().antMatchers(
API_V1_USER_ROOT_URL "/**",
API_V1_COURSE_ROOT_URL "/**").authenticated();
http.authorizeRequests().antMatchers("/", "/index", "/css/*", "/js/*").permitAll();
http.addFilterBefore(customAuthorizationFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
This is CustomAuthorizationFilter
@Slf4j
@RequiredArgsConstructor
@Component
public class CustomAuthorizationFilter extends OncePerRequestFilter {
private final JWTProvider jwtProvider;
private final UserService userService;
@Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException
{
String uri = request.getServletPath();
AntPathMatcher pathMatcher = new AntPathMatcher();
boolean matchIgnoreUrl = EXCLUDE_URL_PATTERNS.stream()
.anyMatch(p -> pathMatcher.match(p, uri));
log.info("[{}] matchIgnoreUrl is {}", uri, matchIgnoreUrl);
if (matchIgnoreUrl) return true;
else {
boolean matchedAuthenticateUrl = AUTHENTICATE_URL_PATTERNS.stream()
.anyMatch(p -> pathMatcher.match(p, uri));
log.info("[{}] MatchedAuthenticateUrl is {}", uri, matchedAuthenticateUrl);
//Ignore query database if request is a missing url page
return !matchedAuthenticateUrl;
}
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
Principal principal = request.getUserPrincipal();
String uri = request.getRequestURI();
log.info("[{}] Match in Should Filter", uri);
String authorizationHeader = request.getHeader(AUTHORIZATION);
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
String token = authorizationHeader.substring("Bearer ".length());
// return null
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String username = jwtProvider.getUsernameFromToken(token);
//Query to database every request come
UserDetails userDetails = userService.loadUserByUsername(username);
//Set authentication to Security Context
SecurityUtils.authenticateUser(request, userDetails);
}
filterChain.doFilter(request, response);
}
}
The idea is: Authentication object used in controllers.
- The first request is sent to the server, the server queries the database to get the user's information and sets the authentication object to the security context.
- Next request if same user (username-base), i want the server to not need to query the database. It will use Authentication object from first request.
The problem
- In the second request, Authentication object is null, why ???
- How can i implement my idea ???
- Is there another way to validate a token as valid (eg. username decoded from token can be inactivate) that optimizes the number of database queries per request ??? (is the caching ???)
- Is it that every time the request completes, the security context will be cleaned ???
CodePudding user response:
- 4. Yes,
SecurityContextHolder
inServletWeb
is default usingTheadLocal
. So it'll be clear when the thread (your request) ends. - 1. 4 is the reason
- 2. You should use session/cookie instead of JWT. Normally, when using JWT, we expect a STATELESS service.
- 3. To validate the token, using your signing key is enough, don't need to call DB, just need to keep your key secret. Something like:
import io.jsonwebtoken.Jws;
Jwts.parserBuilder()
.setSigningKey(yourKey)
.build()
.parseClaimsJws(yourToken);
Your service now is secured based on the characteristic of JWT token.