I'm doing spring boot web application with SQL Server 2019. Now I'm trying to do JWT Authentication & Authorization with Spring Security but my SecurityConfig.java
have error with requestMatchers:
The method requestMatchers(RequestMatcher...) in the type AbstractRequestMatcherRegistry<AuthorizeHttpRequestsConfigurer.AuthorizedUrl> is not applicable for the arguments (String, String)Java(67108979)
I don't know why this happen because I see a lot of people use requestMatchers and run well with no red line above it. Here is SecurityConfig.java:
package com.example.demologin.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
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.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import com.example.demologin.filter.JwtAuthenticationFilter;
import com.example.demologin.service.UserDetailsServiceImp;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private final UserDetailsServiceImp userDetailsServiceImp;
private final JwtAuthenticationFilter jwtAuthenticationFilter;
private final CustomLogoutHandler logoutHandler;
public SecurityConfig(UserDetailsServiceImp userDetailsServiceImp,
JwtAuthenticationFilter jwtAuthenticationFilter,
CustomLogoutHandler logoutHandler) {
this.userDetailsServiceImp = userDetailsServiceImp;
this.jwtAuthenticationFilter = jwtAuthenticationFilter;
this.logoutHandler = logoutHandler;
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(
req->req.requestMatchers("/login/**","/register/**")
.permitAll()
.requestMatchers("/admin_only/**").hasAuthority("ADMIN")
.anyRequest()
.authenticated()
).userDetailsService(userDetailsServiceImp)
.sessionManagement(session->session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.exceptionHandling(
e->e.accessDeniedHandler(
(request, response, accessDeniedException)->response.setStatus(403)
)
.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)))
.logout(l->l
.logoutUrl("/logout")
.addLogoutHandler(logoutHandler)
.logoutSuccessHandler((request, response, authentication) -> SecurityContextHolder.clearContext()
))
.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
return configuration.getAuthenticationManager();
}
}
Here is my pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.18</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demologin</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demologin</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>3.2.5</version>
</dependency>
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<version>9.4.0.jre11</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.12.3</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.12.3</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.12.3</version>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Here is my complete src code : https://github.com/phuccoder/demojwt.git
You will be fine using either
req.antMatchers("/login", "/register")
req.requestMatchers(new AntPathRequestMatcher("/login"), new AntPathRequestMatcher("/register"))
in your SecurityConfig.securityFilterChain()
.
The reason you are getting 403
status code using antMatchers
is because the way you implemented the JwtAuthenticationFilter
you always require an Authorization
header and if you don't provide it you will get a NullPointerException
which leads to the error.
There probably are some logic flaws in your implementation, such as requiring both registration and login to be authenticated (i.e. if the user is not yet registered, how can he be authenticated?) or the JwtAuthenticationFilter.doFilterInternal()
immediately returning when detecting Authorization: Bearer
header (which is not what you want maybe).
Furthermore, there surely is a bug in AuthenticationService.register()
:
you do user.setRole(user.getRole())
while you should do user.setRole(request.getRole())