I am having a spring boot application, where during the startup of the application, i am trying to inject the Spring-Security Java configuration using Javaassist. I am generating the java config dynamically from values from DB. Here is the code,
public class WarLock implements SpringApplicationRunListener {
private final SpringApplication application;
public WarLock(SpringApplication application, String[] args) throws IOException {
this.application = application;
}
@Override
public void started() {
try {
System.out.println("Re-write security config called");
rewriteSecurityConfigClass();
} catch (NotFoundException | CannotCompileException e) {
e.printStackTrace();
}
}
private void rewriteSecurityConfigClass() throws NotFoundException, CannotCompileException {
SecurityConfig config = new SecurityConfig();
ClassPool cp = ClassPool.getDefault();
cp.appendClassPath(new LoaderClassPath(application.getClassLoader()));
CtClass compiledClass = cp.get(config.getClass().getName());
CtClass[] argClasses = { cp.get(HttpSecurity.class.getName()) };
CtMethod method = compiledClass.getDeclaredMethod("configure",argClasses);
method.setBody("http .csrf().disable() "+
".authorizeRequests() "+
" .antMatchers(\"/css/**\", \"/index\").permitAll() "+
" .antMatchers(\"/user/**\").hasAuthority(\"USER\") "+
" .antMatchers(\"/tryadmin\").hasAuthority(\"ADMIN\") "+
" .antMatchers(\"/try\").hasAuthority(\"USER\") "+
" .and() "+
".authenticationProvider(authenticationProvider()) "+
" .exceptionHandling() "+
" .authenticationEntryPoint(entryPoint) "+
" .and() "+
".formLogin() "+
" .usernameParameter(\"username\") "+
" .passwordParameter(\"password\") "+
" .successHandler(loginSuccessHandler) "+
" .failureHandler(loginFailureHandler) "+
" .and() "+
".logout() "+
" .permitAll() "+
" .logoutRequestMatcher(new AntPathRequestMatcher(\"/login\", \"DELETE\")) "+
" .logoutSuccessHandler(logoutSuccessHandler) "+
" .deleteCookies(\"JSESSIONID\") "+
" .invalidateHttpSession(true) "+
" .and() "+
".sessionManagement() "+
" .enableSessionUrlRewriting(true) "+
" .maximumSessions(1); ");
compiledClass.toClass();
But the code is failing while startup,
javassist.CannotCompileException: [source error] authorizeRequests() not found in org.springframework.security.config.annotation.web.HttpSecurityBuilder
It is looking for authorizeRequests() inside the class HTTPSecurityBuilder but it actually have to look into the class "HttpSecurity". How can I fix this? Thanks in advance.
Javassist does not support generic types but only respects the erasure of any method. As you can see in from the javadoc of CsrfConfigurer, the disable method is generic which is why Javassist assumes the wrong type and cannot resolve the correct method.
The only way to handle this is to add an appropriate type casting after any generic step of the Spring Security DSL which, unfortunately, is almost every step.
If you are looking for an alternative which does not suffer this limitation, have a look at my library Byte Buddy which works with compiled code rather than strings and does not suffer this limitation.