Master Spring Security 5.7: Configure Without WebSecurityConfigurerAdapter
This guide walks you through upgrading to Spring Boot 2.7.0 and Spring Security 5.7.1, showing the deprecated WebSecurityConfigurerAdapter removal, the new SecurityFilterChain approach, and advanced dynamic permission techniques with complete code examples.
Spring Boot 2.7.0 and Spring Security 5.7.1 were recently released, deprecating the classic WebSecurityConfigurerAdapter. This article demonstrates the new Spring Security configuration style and how to implement dynamic permission control.
Basic Usage
First, update the pom.xml to use Spring Boot version 2.7.0:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>Old Approach
Before Spring Boot 2.7.0, you needed a configuration class extending WebSecurityConfigurerAdapter and overriding three methods:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class OldSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UmsAdminService adminService;
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
// configure HttpSecurity
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService())
.passwordEncoder(passwordEncoder());
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}In Spring Boot 2.7.0, WebSecurityConfigurerAdapter is deprecated, indicating Spring Security is moving away from this pattern.
New Approach
The new method no longer extends WebSecurityConfigurerAdapter. Instead, declare a configuration class and define a SecurityFilterChain bean, moving the HttpSecurity configuration into that method:
@Configuration
public class SecurityConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
// configure HttpSecurity
return httpSecurity.build();
}
}This approach is concise and avoids inheritance.
Advanced Usage
After upgrading, you can also implement dynamic permission control.
Method‑Based Dynamic Permissions
Enable method security with @EnableGlobalMethodSecurity on the configuration class.
Use @PreAuthorize on controller methods to specify required authorities.
@Controller
@RequestMapping("/product")
public class PmsProductController {
@Autowired
private PmsProductService productService;
@ApiOperation("Create product")
@PostMapping("/create")
@ResponseBody
@PreAuthorize("hasAuthority('pms:product:create')")
public CommonResult create(@RequestBody PmsProductParam productParam) {
int count = productService.create(productParam);
return count > 0 ? CommonResult.success(count) : CommonResult.failed();
}
}Path‑Based Dynamic Permissions
Implement a filter that checks the request path against dynamically loaded permission data.
public class DynamicSecurityFilter extends AbstractSecurityInterceptor implements Filter {
@Autowired
private DynamicSecurityMetadataSource dynamicSecurityMetadataSource;
@Autowired
private IgnoreUrlsConfig ignoreUrlsConfig;
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
FilterInvocation fi = new FilterInvocation(servletRequest, servletResponse, filterChain);
// Allow OPTIONS requests
if (request.getMethod().equals(HttpMethod.OPTIONS.toString())) {
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
return;
}
// Allow whitelisted URLs
PathMatcher pathMatcher = new AntPathMatcher();
for (String path : ignoreUrlsConfig.getUrls()) {
if (pathMatcher.match(path, request.getRequestURI())) {
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
return;
}
}
// Perform access decision
InterceptorStatusToken token = super.beforeInvocation(fi);
try {
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
} finally {
super.afterInvocation(token, null);
}
}
// other required methods omitted for brevity
}The filter relies on a DynamicSecurityMetadataSource that loads permission mappings from a custom service.
public class DynamicSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
private static Map<String, ConfigAttribute> configAttributeMap = null;
@Autowired
private DynamicSecurityService dynamicSecurityService;
@PostConstruct
public void loadDataSource() {
configAttributeMap = dynamicSecurityService.loadDataSource();
}
@Override
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
if (configAttributeMap == null) this.loadDataSource();
String url = ((FilterInvocation) object).getRequestUrl();
String path = URLUtil.getPath(url);
PathMatcher pathMatcher = new AntPathMatcher();
List<ConfigAttribute> configAttributes = new ArrayList<>();
for (String pattern : configAttributeMap.keySet()) {
if (pathMatcher.match(pattern, path)) {
configAttributes.add(configAttributeMap.get(pattern));
}
}
return configAttributes;
}
// other methods omitted for brevity
}The DynamicSecurityService supplies the permission map, typically loaded from the database.
public interface DynamicSecurityService {
/** Load resource patterns and corresponding ConfigAttributes */
Map<String, ConfigAttribute> loadDataSource();
}Finally, register the dynamic filter in the security configuration:
@Configuration
public class SecurityConfig {
@Autowired
private DynamicSecurityService dynamicSecurityService;
@Autowired
private DynamicSecurityFilter dynamicSecurityFilter;
@Bean
SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
// other HttpSecurity configurations
if (dynamicSecurityService != null) {
httpSecurity.addFilterBefore(dynamicSecurityFilter, FilterSecurityInterceptor.class);
}
return httpSecurity.build();
}
}Effect Test
Start the mall-tiny-security project and log in with an account that only has permission for /brand/listAll. Access the Swagger UI at http://localhost:8088/swagger-ui/.
Insert the returned token into Swagger's authentication header.
Requests to authorized endpoints return data; unauthorized requests return an access‑denied message.
Conclusion
The new Spring Security configuration is elegant and simple, while still offering good compatibility with older code. Mature frameworks tend to keep backward compatibility during upgrades, allowing developers to adopt the new style gradually.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
macrozheng
Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
