NoSuchBeanDefinitionException when add #PreAuthorize annotation - spring

I'm trying to perform integration of my GWT application and Spring Security. And when I add #PreAuthorize("hasRole('ROLE_USER')") annotation to the method of my DAO class following exception appears:
No unique bean of type [server.dao.ElementServiceDAO] is defined:
expected single bean but found 0
DaoServiceLocator can't find DAO bean, but in debug mode I see elementServiceDAO bean in ApplicationContext instance.
My DAO class looks like this:
#Service
public class ElementServiceDAO extends EntityDAO {
#PreAuthorize("hasRole('ROLE_USER')")
#SuppressWarnings("unchecked")
public Layer getFullRenderingTopology() {
...
}
}
DAO service locator's code:
public class DaoServiceLocator implements ServiceLocator {
#Override
public Object getInstance(final Class<?> clazz) {
try {
final HttpServletRequest request = RequestFactoryServlet
.getThreadLocalRequest();
final ServletContext servletContext = request.getSession()
.getServletContext();
final ApplicationContext context = WebApplicationContextUtils
.getWebApplicationContext(servletContext);
return context.getBean(clazz);
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
}
applicationContext-security.xml:
<authentication-manager>
<authentication-provider>
<user-service>
<user name="operator" password="operator" authorities="ROLE_USER, ROLE_ADMIN" />
<user name="guest" password="guest" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
<global-method-security pre-post-annotations="enabled" />
Please give me any advice!

When ElementServiceDAO implements an interface (in your case - transitively via EntityDAO), Spring by default creates an interface-based proxy to apply security aspects. So, elementServiceDAO in your application context is a proxy that isn't instance of ElementServiceDAO, therefore it cannot be retrieved by type.
You either need to
force creation of target-class-based proxies as follows
<global-method-security
pre-post-annotations="enabled" proxy-target-class = "true" />
or create a business interface for ElementServiceDAO and use that interface instead of implementation class.
See also:
7.6 Proxying mechanisms

Related

Spring: autowired DAO is null in custom UserServiceDetails implementation

I am configuring the security in my Spring project and unfortunately I encountered a problem. Why is the autowired userDAO in CustomUserDetailsService.java null?
Here is the code in question:
CustomUserDetailsService.java
#Service("customUserDetailsService")
#ComponentScan(basePackages="...")
public class CustomUserDetailsService implements UserDetailsService {
#Autowired
private UserDAO userDAO;
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// userDAO is null
UserModel user = userDAO.getUserByUsername(username);
...
}
}
spring-security.xml
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.2.xsd">
<http pattern="/resources/**" security="none"/>
<http pattern="/login" security="none"/>
<http auto-config="true">
<intercept-url pattern="/**" access="ROLE_USER" />
<form-login
login-page="/login"
default-target-url="/home"
authentication-failure-url="/login?error"
username-parameter="username"
password-parameter="password" />
<logout logout-success-url="/login?logout" />
</http>
<authentication-manager>
<authentication-provider user-service-ref="customUserDetailsService">
<password-encoder ref="encoder" />
</authentication-provider>
</authentication-manager>
<beans:bean id="encoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
<beans:constructor-arg name="strength" value="10" />
</beans:bean>
<beans:bean id="customUserDetailsService" class="... .service.CustomUserDetailsService" />
Edit:
UserDAOImpl.java
public class UserDAOImpl implements UserDAO {
private JdbcTemplate jdbcTemplate;
public UserDAOImpl(DataSource dataSource) {
jdbcTemplate = new JdbcTemplate(dataSource);
}
public UserModel getUserByUsername(String username) {
String sql = "SELECT * FROM users WHERE username = '" + username + "'";
return jdbcTemplate.query(sql, new ResultSetExtractor<UserModel>() {
public UserModel extractData(ResultSet rs) throws SQLException, DataAccessException {
if (rs.next()) {
UserModel user = new UserModel();
user.setUsername(rs.getString("username"));
user.setPassword(rs.getString("passwort"));
user.setEnabled(rs.getBoolean("enabled"));
return user;
}
return null;
}
});
}
}
Here is the code snippet in the Java-based configuration file:
#Bean
public UserDAO getUserDAO() {
return new UserDAOImpl(getDataSource());
}
Interesting though, this configuration seems to be sort of working. When I autowire the UserDAO into my controller, it works perfectly fine:
MainController.java
#RequestMapping(value = {"", "/", "/home"})
public String homeHandler(Model model) {
// userDAO is not null, works perfectly fine and returns the UserModel-Object as expected
UserModel user = userDAO.getUserByUsername("dx");
...
}
I already read some Q&A, yet so far it concerned manual instatiation of the custom UserDetailsService. What is wrong with my code?
The issue seems to be because you are instantiating the customUserDetailsService in two different places. You have <bean> definition for it in your spring-security.xml file as well as you have #Service annotation on the class itself. In your XML bean definition, you are not injecting the UserDAO so that it is null.
You need to streamline your project to clean the bean definition in either java config or XML files (this is not required and you can have XML + java config but then it makes it very confusing as to what is instantiating which bean). You have few options;
Remove the XML declaration of CustomUserDetailsService. Add <context:component-scan> in your XML and add the package names and also remove the #ComponentScan from CustomUserDetailsService. This will allow spring to scan the packages and register the beans marked with #Service, #Bean, etc annotations. Make sure your java config class is marked with #Configuration annotation.
You can Decide to use XML config for all the beans in which case you need to remove the #Bean, #Service, etc annotations and declare all of them in the spring XML bean definition and make sure each of them has proper dependencies injected.
You definitely need to clean up your bean definitions so correct dependencies are injected.
Thank you for your responses. Setu's answer was the most helpful one. Obviously, the problem was indeed the double instantiation. I added <context:component-scan base-package="... .service" /> and left both annotations #Service("customUserDetailsService") and #ComponentScan(basePackages="...") in CustomUserDetailsService (without #ComponentScan it didn't work).

TestingAuthenticationToken and #PreAuthorizedTest -anotation ignored

I have following test-config.xml
<authentication-manager alias="authenticationManager">
<authentication-provider ref="testProvider" />
<authentication-provider>
<user-service>
<user name="department1000" password="password" authorities="ROLE_1000" />
<user name="user" password="password2" authorities="ROLE_ALL_DEPT_ACCESS" />
<user name="user1" password="password3" authorities="ROLE_STUDENT" />
</user-service>
</authentication-provider>
</authentication-manager>
<beans:bean id="testProvider" class="org.springframework.security.authentication.TestingAuthenticationProvider">
</beans:bean>
I need a method that simulates authentication and giving the role:
protected void simulateRole(String role) {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority(role));
token = new TestingAuthenticationToken("username","password", authorities);
securityContext.setAuthentication((getAuthenticationManager().authenticate(token)));
Then I need to call the #PreAuthorized anotated controller method for test:
#Test(expected = AccessDeniedException.class)
public void testShowAccessDenied() {
super.simulateRole("ROLE_STUDENT");
controller.show(new ModelMap(), super.getAuthenticationPrincipal(), Locale.getDefault(), new D(), new E());
super.getSecurityContext().getAuthentication().getDetails();
I think I'm not setting the required Principal right, since test is not throwing AccessDeniedException
public Principal getAuthenticationPrincipal() {
return (Principal) securityContext.getAuthentication().getDetails();
Changing the type of controller method arguments would cause a lot of mess. Any way to get this working?
First you are missing the global method security from your configuration. You should add the following to test-config.xml:
<global-method-security pre-post-annotations="enabled" />
NOTE: For this to work in a servlet environment, you need to ensure to add the global-method-security tag to your DispatcherServlet config and not the root configuration as described in the FAQ
I'm assuming your controller does not implement an interface, so you will need to ensure that you have cglib on your classpath to support class based proxies. If you are using Spring 4.x+ you can add objenesis to your classpath and your proxied classes no longer need default constructors.
Second you need to ensure the controller in your test was created by Spring. When Spring creates the controller it uses the information from to proxy the class and add the security to it.
If you are still having issues, please post your complete test that fails, the method you are testing on the controller, and complete test code.

How to execute SQL script only once at startup in Spring?

I have a web application based on Spring JDBC and Jersey RESTful web service. I'm using the following Spring JDBC template class to initiate the dataSource and execute an SQL script (update_condition_table.sql):
public class CustomerJDBCTemplate implements CustomerDAO {
private DataSource dataSource;
private JdbcTemplate jdbcTemplateObject;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
this.jdbcTemplateObject = new JdbcTemplate(dataSource);
Resource rc = new ClassPathResource("update_condition_table.sql");
JdbcTestUtils.executeSqlScript(jdbcTemplateObject, rc, false);
}
// ......other methods
}
The bean configuration file is beans.xml:
<!-- Initialization for data source -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/customer" />
<property name="username" value="root" />
<property name="password" value="mypassword" />
</bean>
<!-- Definition for customerJDBCTemplate bean -->
<bean id="customerJDBCTemplate" class="com.example.db.CustomerJDBCTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
The Jersey controller class contains the instantiation of class CustomerJDBCTemplate and serves as the REST web service:
#Path("/customer")
public class CustomerService {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
CustomerJDBCTemplate dbController = (CustomerJDBCTemplate) context.getBean("customerJDBCTemplate");
// ... some GET/POST methods
}
When I launched my web app by entering the index URL in the browser, the SQL script gets executed by the customerJDBCTemplate bean. However, when I clicked to navigate to other pages, it crashed and reported that the SQL script cannot be executed again. So obviously the SQL script was executed again after initialization of dataSource and initial launch of the index web page. How to avoid this by just running the SQL script only once upon initial startup of the web app?
Looks like I need to move the bean instantiate code out of CustomerService class, but where should I put that code?
I figured it out that I should set the bean application context to be static within CustomerService class and do it in the static initialization block as follows:
#Path("/customer")
public class CustomerService {
private static ApplicationContext context;
private static CustomerJDBCTemplate dbController;
static {
context = new ClassPathXmlApplicationContext("beans.xml");
dbController = (CustomerJDBCTemplate) context.getBean("customerJDBCTemplate");
}
//... other methods
}
I guess the reason is Jersey creates a different instance of CustomerService for each HTTP session (correct me if I'm wrong). So if I set the bean context as instance variable, it will do the initialization for every HTTP request.
Have your CustomerJDBCTemplate implement InitializingBean. afterPropertiesSet will get called once, right after all properties have been set by Spring's BeanFactory.
For example:
public class CustomerJDBCTemplate implements CustomerDAO, InitializingBean {
...
// ......other methods
public void afterPropertiesSet() throws Exception {
//do your initializing, or call your initializing methods
}
}

Spring Security : How to access a protected service from the init method of a context bean?

My setup :
In a Spring MVC 3.1 application, I have a Spring Security protected service :
#PreAuthorize("isAnonymous()") // ("hasRole('USER')") doesn't work either
public interface UserService extends UserDetailsService, PasswordEncoder
I'm trying to use this service from the init() method of a bean declared in my context :
<bean class="com.xxx.scripts.FillDbWithInitialValues" init-method="contextInit" />
The class :
public class FillDbWithInitialValues
{
#Autowired
UserService userService;
public void contextInit()
{
User test = userService.getUser(1);
}
}
Extract of my security.xml file :
<sec:global-method-security pre-post-annotations="enabled" />
<sec:http auto-config="true" use-expressions="true">
(...)
</sec:http>
<sec:authentication-manager alias="authManager">
<sec:authentication-provider user-service-ref="userService">
<sec:password-encoder ref="userService" />
</sec:authentication-provider>
</sec:authentication-manager>
The problem :
When I start the application, I get an exception :
org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
Why is this happening?
How can my bean be "authenticated" so it can use the service?
If there's no user authenticated there's no way of checking his roles. If you want that bean to be authenticated before calling the method i think you could try something in this way:
SecurityContextHolder.getContext().setAuthentication(new user here);
User test = userService.getUser(1);

Spring Auto login issue

I'm trying to implement the below spring auto login, but my authenticationManager instance throws the below exception and is not autowired. How do I get an instance of it from Spring manually? I'm not using a spring controller, I'm using a JSF request scoped bean. I get the below exception at runtime when the container tries to autowire the authenticationManager. The requestCache comes in fine. Should I be using a method on my UserDetailsService implementation (userManager)? I don't see an appropriate method exposed by UserDetailsService that takes a UsernamePasswordAuthenticationToken objet. Any ideas?
config:
<http access-denied-page="/auth/denied.html">
<intercept-url
pattern="/**/*.xhtml"
access="ROLE_NONE_GETS_ACCESS" />
<intercept-url
pattern="/auth/**"
access="ROLE_ANONYMOUS,ROLE_USER" />
<intercept-url
pattern="/auth/*"
access="ROLE_ANONYMOUS" />
<intercept-url
pattern="/registered/*"
access="ROLE_USER" />
<intercept-url
pattern="/*"
access="ROLE_ANONYMOUS" />
<form-login
login-processing-url="/j_spring_security_check.html"
login-page="/auth/login.html"
default-target-url="/registered/home.html"
authentication-failure-url="/auth/login.html" />
<logout invalidate-session="true"
logout-success-url="/"
logout-url="/auth/logout.html"/>
<anonymous username="guest" granted-authority="ROLE_ANONYMOUS"/>
<remember-me user-service-ref="userManager" key="e37f4b31-0c45-11dd-bd0b-0800200c9a66"/>
</http>
<!-- Configure the authentication provider -->
<authentication-manager alias="am">
<authentication-provider user-service-ref="userManager">
<password-encoder ref="passwordEncoder" />
</authentication-provider>
</authentication-manager>
Exception
Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: protected org.springframework.security.authentication.AuthenticationManager com.dc.web.actions.SignUpDetail.authenticationManager; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [org.springframework.security.authentication.AuthenticationManager] is defined: expected single matching bean but found 2: [org.springframework.security.authentication.ProviderManager#0, org.springframework.security.authenticationManager]
javax.faces.webapp.FacesServlet.service(FacesServlet.java:325)
#Named
#Scope("request")
public class Signup
{
#Inject
RequestCache requestCache;
#Inject
protected AuthenticationManager authenticationManager;
public String login(){
authenticateUserAndSetSession(utilities.getLoggedInUser(), (HttpServletRequest) FacesUtils.getExternalContext().getRequest());
return "/home.html";
}
private void authenticateUserAndSetSession(Users user,
HttpServletRequest request)
{
UserDetails details = userManager.loadUserByUsername(user.getUsername());
UsernamePasswordAuthenticationToken usernameAndPassword =
new UsernamePasswordAuthenticationToken(
user.getUsername(), "pwd", details.getAuthorities());
// Authenticate, just to be sure
Authentication auth = authenticationManager.authenticate(usernameAndPassword);
// Place the new Authentication object in the security context.
SecurityContextHolder.getContext().setAuthentication(auth);
}
It's caused by the fact that Spring Security internally declares the second AuthenticationManager, so that you have two of them. You need to choose one of them with alias:
<authentication-manager alias = "am">
...
</authentication-manager>
#Inject #Named("am")
protected AuthenticationManager authenticationManager;
#c12 If you are using customized role based authorization it is possible that the internal authorities collection of your UserDetails is not populated inside of the loadUserByUsername method of your UserManager class; If that is the case you will require to associate particular roles manually to the authorities collection. Check out snippet below (ideally it should be a part of the loadUserByUsername method body); It is assumed that...
a) MyUser is your own user object
b) MyUserRole is your own role object
c) userDetails is what you will return from the loadUserByUsername method
MyUser myUser = userDao.getUserByUsername(username);
Set<GrantedAuthority> grantedAuthSet = new HashSet<GrantedAuthority>();
Set<MyUserRole> myUserRoleSet = myUser.getRoleSet();
for (Iterator<MyUserRole> iterator = myUserRoleSet.iterator(); iterator.hasNext();) {
MyUserRole userRole = (MyUserRole) iterator.next();
grantedAuthSet.add(new SimpleGrantedAuthority(userRole.getName()));
}
List<GrantedAuthority> grantedAuthList = new ArrayList<GrantedAuthority>(grantedAuthSet);
myUser.setAuthorities(grantedAuthList);
UserDetails userDetails = new User(myUser.getUsername(), myUser.getPassword(), true, true, true, true, myUser.getAuthorities());
Hope this helps!

Resources