Letting Spring security pick implementation of class implementing custom authentication provider - spring

We have a webapp which implements custom authentication via AuthenticationProvider.
This works fine now. But we want to provide an option for customer to implement their own authentication class implementing AuthenticationProvider. So they will delete our jar from app and add their jar to classpath.
It appears in security xml we need to specify only class implementing AuthenticationProvider but can't tell spring to pick any class implementing interface AuthenticationProvider
Current XML and Class implementation
<authentication-manager alias="authenticationManager">
<authentication-provider ref="customAuthenticationProvider"/>
</authentication-manager>
<beans:bean id="customAuthenticationProvider" class="w.x.y.z.CustomAuthenticationProvider"></beans:bean
#Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
//Implementation
}
#Override
public boolean supports(Class<?> arg0) {
return true;
}
}
Is there anyway I can tell spring to pick any class implementing AuthenticationProvider?

Maybe you can do it by using type autowiring and factory method:
1-The CustomAuthenticationProvider it will be injected by type autowiring defined only in the jar added by your client and the deleted jar(it must be exactly one instance of AuthenticationProvider).
2-And then use a factory method to inject this provider into the authentication-manager.
1-first step
public class AuthenticationProviderFactory {
#Autowired
private AuthenticationProvider authProvider;
public AuthenticationProvider getAuthenticationProvider() {
return authProvider;
}
}
2-second step
<bean name="authenticationProviderFactory"
class="w.x.y.z..AuthenticationProviderFactory"></bean>
<bean name="authenticationProvider" factory-bean="authenticationProviderFactory"
factory-method="getAuthenticationProvider">
</bean>
<authentication-manager alias="authenticationManager">
<authentication-provider ref="authenticationProvider"/>
</authentication-manager>
!!!! The deleted jar and the new jar must have the same applicationContext.xml name(where the AuthenticationProvider is declared) to make the replace working.
<import resource="applicationContextAuthProvider.xml"/>

Related

Spring configure method not called

I tried to configure spring to allow encoded slashes in URL, but I cannot get the configuration to be applied
I've tried adding #EnableWebSecurity but it didn't seem to change anything. When I put breakpoints, the #Bean method is called but not configure().
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Bean
public HttpFirewall allowUrlEncodedSlashHttpFirewall() {
DefaultHttpFirewall firewall = new DefaultHttpFirewall();
firewall.setAllowUrlEncodedSlash(true);
return firewall;
}
#Override
public void configure(WebSecurity web) throws Exception {
web.httpFirewall(allowUrlEncodedSlashHttpFirewall());
}
}
I managed to make it work using xml configuration. I added the following in my applicationContext.xml:
<bean id="allowUrlEncodedSlashHttpFirewall"
class="org.springframework.security.web.firewall.DefaultHttpFirewall">
<property name="allowUrlEncodedSlash" value="true"/>
</bean>
<security:http-firewall ref="allowUrlEncodedSlashHttpFirewall"/>

Spring AuthenticationSuccessHandler autowired Service/DAO is null

I am having a AuthenticationSuccessHandler which should hold a UserService. My problem is that i am getting a NullPointerException on the userService at this line:
logger.debug(userService.getAllUsers().toString());
AuthenticationSuccessHandlerImpl:
#Repository
public class AuthenticationSuccessHandlerImpl implements AuthenticationSuccessHandler {
private static final Logger logger = LoggerFactory.getLogger(AuthenticationSuccessHandlerImpl.class);
#Autowired
private UserService userService;
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
logger.debug(userService.getAllUsers().toString());
response.setStatus(HttpServletResponse.SC_OK);
response.sendRedirect("index");
}
}
I Have also tried #Service and #Component Annotation but the same error.
It works in all other Services and Controller from my project but not in this Handler.
I also have this line of code in my config:
<context:component-scan base-package="com.net4you.*" />
<tx:annotation-driven />
EDIT:
AuthenticationSuccessHandlerImpl is not created with new, but with:
<security:form-login login-page="/login" default-target-url="/index" authentication-failure-url="/fail2login"
authentication-success-handler-ref="customAuthenticationSuccessHandler"/>
<beans:bean id="customAuthenticationSuccessHandler" class="com.net4you.slamanagement.helper.AuthenticationSuccessHandlerImpl"/>
Project structure:
Debugger:
1) you dont need .*
<context:component-scan base-package="com.net4you.*" />
this is enough
<context:component-scan base-package="com.net4you" />
2) are you sure that userService is null?
3) configuration looks fine, maybe getAllUsers() method is returning null,
can you show its implementation?
UPDATE
You can solve this problem in two ways
1) remove
<beans:bean id="customAuthenticationSuccessHandler" class="com.net4you.slamanagement.helper.AuthenticationSuccessHandlerImpl"/>
and annotate AuthenticationSuccessHandlerImpl like this
#Component("customAuthenticationSuccessHandler")
public class AuthenticationSuccessHandlerImpl implements AuthenticationSuccessHandler {
private static final Logger logger = LoggerFactory.getLogger(AuthenticationSuccessHandlerImpl.class);
#Autowired
private UserService userService;
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
logger.debug(userService.getAllUsers().toString());
response.setStatus(HttpServletResponse.SC_OK);
response.sendRedirect("index");
}
}
2) the second way would be creating setter of userservice in AuthenticationSuccessHandlerImpl , create userservice bean in xml manually and then inject it directly using setter property of authenticationSuccessHandlerImpl in xml, this solution is more complicated and i would suggest you to use first one
Your implementation doesn't work because you define a bean in xml without setting the userservice for it (#autowired doesn't work if you define bean manually) and then you inject this bean into form-login

Spring: Autowired is null in ejb class

I have the following situation:
#Controller
public class myController {
#Autowired
private IProxy service;
public ModelAndView init(HttpServletRequest request, HttpServletResponse response) throws Exception {
List<String> list = service.getName();
}
}
Then my Service is define as follow:
public interface IProxy {
public List<String> getName();
}
Proxy class is responsible for the lookup to the remote bean
#Service("service")
public class Proxy implements IProxy {
...
public List<String> getName() {
return myClass.getName();
}
And the implementation is the following:
#Interceptors(interceptor.class)
#Stateless
#Resource(name = "java:/db")
#Remote(MyClassRemote.class)
public class MyClassImpl extends MyEjb implements MyClassRemote{
#PersistenceContext(unitName = "db")
private EntityManager em;
#Resource
private SessionContext sctx;
#Autowired
public IMyRepo myRepo;
#Override
public List<String> getName() {
try {
return myRepo.getName(em);
}
catch (Exception ex) {
ex.printStackTrace();
throw ex;
}
finally {}
}
So, the problem is that here myRepo is null. I don't know why because IMyRepo and his implementation are always located within the path scanned by Spring.
Just one clarification: MyRepo class that implements IMyRepo is annotated with #Repository.
Any idea?
you can inject spring beans in EJB using Spring interceptors, as explained here in the official documentation. Basically you'll need to adjust your class as follows:
// added the SpringBeanAutowiringInterceptor class
#Interceptors({ interceptor.class, SpringBeanAutowiringInterceptor.class })
#Stateless
#Resource(name = "java:/db")
#Remote(MyClassRemote.class)
public class MyClassImpl extends MyEjb implements MyClassRemote{
// your code
}
You'll also need to define the context location in a beanRefContext.xml file (with your own application context file):
application-context.xml version
<bean id="context"
class="org.springframework.context.support.ClassPathXmlApplicationContext">
<constructor-arg>
<list>
<value>application-context.xml</value>
</list>
</constructor-arg>
</bean>
Java Configuration version:
<bean id="context"
class="org.springframework.context.annotation.AnnotationConfigApplicationContext">
<constructor-arg>
<list>
<value type="java.lang.Class">com.your.app.Configuration</value>
</list>
</constructor-arg>
</bean>
Spring beans and EJB are two different things, you can't just inject a Spring bean in an EJB, because that EJB is no Spring bean, so Spring doesn't know there is a field which should be injected by Spring (unless you use some fancy AOP stuff, which can enable injection into non-Spring-managed beans).

Spring dependency injection in Security.Authenticator

I have written a custom authenticator for my Play 2.3.4 application which has spring dependency injection configured. I want to be able to inject an 'authService' into my authenticator, but this is not working. My authService is always null. Is it possible to configure autowired injection into authenticators? My authenticator is as follows:
package controllers;
#org.springframework.stereotype.Component
public class AppAuthenticator extends Security.Authenticator {
#Autowired
#Qualifier("authService")
private AuthService authService;
#Override
public String getUsername(Context ctx) {
...
authService.getAuthenticatedUser();
...
}
public void setAuthService(AuthService authService) {
this.authService = authService;
}
...
}
And my Spring configuration includes:
<context:component-scan base-package="controllers"/>
<bean id="authService" class="com.my.auth.class.AuthServiceClass" />
<bean class="play.mvc.Security$AuthenticatedAction" scope="prototype" />
In summary, spring doesn't seem to be injecting properties into my Sercuirty.Authenticator. How do you configure this correctly?

Accessing spring bean from logging appender class

I have log4j DailyRollingFileAppender class in which setFile() method I need to check database value to decide which file to used for logging.
DailyRollingFileAppender class
public void setFileName()
{
isLoginEnabled = authenticationManager.checkLoginLogging();
}
Here 'authenticationManager' is object of class used to make database call using spring dependency injection feature.
spring-beans.xml
<bean id="dailyRollingFileAppender" class="com.common.util.DailyRollingFileAppender">
<property name="authenticationManager">
<ref bean="authenticationManager"/>
</property>
</bean>
<bean id="authenticationManager" class="com.security.impl.AuthenticationManagerImpl">
<property name="userService">
<ref bean="userService"/>
</property>
</bean>
Now when I start my application log4j gets initiated first and since spring-beans is yet to invoked it throws NullPointerException in method setFileName().
So is there a way I can make call to 'authenticationManager.checkLoginLogging();' from DailyFileAppender class so that when log4j loads it should able to get database value?
A few years late, but I hope this is of help to someone.
I was after similar functionality - I have a custom appender, and i wanted to use an autowired bean to perform some logging using a service we'd built. By making the appender implement the ApplicationContextAware interface, and making the field that i'd normally autowire static, i'm able to inject the spring-controlled bean into the instance of the appender that log4j has instantiated.
#Component
public class SslErrorSecurityAppender extends AppenderSkeleton implements ApplicationContextAware {
private static SecurityLogger securityLogger;
#Override
protected void append(LoggingEvent event) {
securityLogger.log(new SslExceptionSecurityEvent(SecurityEventType.AUTHENTICATION_FAILED, event.getThrowableInformation().getThrowable(), "Unexpected SSL error"));
}
#Override
public boolean requiresLayout() {
return false;
}
#Override
public synchronized void close() {
this.closed = true;
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) {
if (applicationContext.getAutowireCapableBeanFactory().getBean("securityLogger") != null) {
securityLogger = (SecurityLogger) applicationContext.getAutowireCapableBeanFactory().getBean("securityLogger");
}
}
}

Resources