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).
Related
I've created a spring-mvc application. the configurations look like below:
dispatcher-servlet.xml
<beans ... >
<mvc:annotation-driven />
<context:annotation-config />
<aop:aspectj-autoproxy/>
<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/>
<context:component-scan base-package="my.server.controller" />
<context:component-scan base-package="my.server.dao" />
<context:component-scan base-package="my.server.service" />
......
</beans>
applicationContext.xml
<beans ...>
...
<bean id="myUserDetailsService" class="my.server.service.MyUserDetailsService" autowire="byType"/>
</beans>
security.xml
<beans:beans ...>
<http pattern="/css/**" security="none"/>
<http pattern="/login.jsp*" security="none"/>
<http auto-config='true'>
<http-basic />
<logout />
<intercept-url pattern="/login.jsp*" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<intercept-url pattern="/**" access="ROLE_USER" />
</http>
<authentication-manager>
<authentication-provider user-service-ref="myUserDetailsService" >
<password-encoder hash="bcrypt" />
</authentication-provider>
</authentication-manager>
</beans:beans>
security.xml and applicationContext.xml are loaded by the following lines in the web.xml:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml, /WEB-INF/security.xml</param-value>
</context-param>
my.server.dao.UserDao.java
#Component
public interface UserDao {
JUser findByUserName(String username);
}
my.server.dao.UserDaoMemory.java
#Component("userDao")
public class UserDaoMemory implements UserDao {
private static List<JUser> myUsers = new ArrayList<>();
static {
myUsers.add(new JUser("ali", "123","ROLE_USER"));
}
#SuppressWarnings("unchecked")
#Override
public JUser findByUserName(String username) {
List<JUser> users = new ArrayList<>();
users = myUsers;
if (users.size() > 0) {
return users.get(0);
} else {
return null;
}
}
}
my.server.service.MyUserDetailsService.java
#Service
public class MyUserDetailsService implements UserDetailsService {
#Autowired
private UserDao userDao;
#Override
public UserDetails loadUserByUsername(final String throws UsernameNotFoundException {
System.out.println("MyUserDetailsService#loadUserByUsername, userDao:"+userDao);
JUser user = userDao.findByUserName(username);
List<GrantedAuthority> authorities = buildUserAuthority(user.getUserRoles());
return buildUserForAuthentication(user, authorities);
}
// Converts my.server.entity.JUser user to
// org.springframework.security.core.userdetails.User
private User buildUserForAuthentication(JUser user,
List<GrantedAuthority> authorities) {
return new User(user.getUsername(),
user.getPassword(), user.isEnabled(),
true, true, true, authorities);
}
private List<GrantedAuthority> buildUserAuthority(Set<JRole> userRoles) {
Set<GrantedAuthority> setAuths = new HashSet<GrantedAuthority>();
// Build user's authorities
for (JRole userRole : userRoles) {
setAuths.add(new SimpleGrantedAuthority(userRole.getRole()));
}
List<GrantedAuthority> Result = new ArrayList<GrantedAuthority>(setAuths);
return Result;
}
public UserDao getUserDao() {
return userDao;
}
}
My problem is in MyUserDetailsServie where the loadUserByUsername(..) be called; The userDao has not been autowired and is null:
MyUserDetailsService#loadUserByUsername, userDao:null
One probable solution is to change the applicationContext.xml such as(autowire="byName"):
<bean id="myUserDetailsService" class="my.server.service.MyUserDetailsService" autowire="byName"/>
But it is not working!!!!
You have some conflicts in how your context are set up.
The applicationContext.xml (the parent context) would generally be where you define all your shared components (e.g. repositories, security, services, handlers, ...), which you have done correctly.
Your servlet context (e.g. dispatcher-servlet.xml) is a child context, and would have visibility to everything defined in the parent context. So you should limit the scope of the components to just controllers for the servlet context, which means you should only scan for your controllers, but you're scanning everything.
<context:component-scan base-package="my.server.controller" />
<context:component-scan base-package="my.server.dao" />
<context:component-scan base-package="my.server.service" />
it should just be
<context:component-scan base-package="my.server.controller" />
you can move all of these to your applicationContext as well
<mvc:annotation-driven />
<context:annotation-config />
<aop:aspectj-autoproxy/>
unless you're using them specifically for this servlet context.
Anyways, getting back to the problem, since child context is scanning for the service/dao classes, it will find those, but they're not properly configured.
so where do I scan my.server.dao
Put them into the applicationContext.xml for now, and into something like a repositoryContext in the future as your application complexity grows.
Technically you can dump everything into a servlet context if you wanted to, but any time you define a "root" context in addition to a servlet context, you'll create scoping issue. Everything in a child context (e.g. servlet context) can see components in a parent context (e.g anything loaded by your web.xml contextloader), but not the reverse.
I am using Spring 4 with Spring Security 3.2. The application is running perfectly so far. Now I am writing Test Cases for all the REST services using MockMVC and RestAssured.
I have a simple Controller
#RestController
#RequestMapping("/secure")
public class MyController{
#RequestMapping(method = RequestMethod.GET)
public String getAllMedia(){
return "SECURE TEST COMPLETE";
}
}
And I have a spring-rest-servlet.xml file which has <mvc:annotation-driven />
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<mvc:annotation-driven />
<context:component-scan base-package="com.mp.web.controller.common" />
</beans>
And spring-security-context.xml looks like this
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<security:http create-session="stateless"
entry-point-ref="authenticationEntryPoint"
authentication-manager-ref="authenticationManager">
<security:custom-filter ref="customRestFilter"
position="BASIC_AUTH_FILTER" />
<security:intercept-url pattern="/api/p/**" access="ROLE_USER" />
<security:intercept-url pattern="/api/login**"
access="IS_AUTHENTICATED_ANONYMOUSLY" requires-channel="https" />
</security:http>
<bean id="authenticationEntryPoint"
class="com.mp.web.security.CustomAuthenticationEntryPoint" />
<bean id="customRestFilter"
class="com.mp.web.security.filter.AuthenticationTokenProcessingFilter">
<constructor-arg name="authenticationManager"
ref="authenticationManager" />
</bean>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="restAuthenticationProvider" />
</security:authentication-manager>
<bean id="restAuthenticationProvider"
class="com.mp.web.security.auth.RestAuthenticationProvider" />
</beans>
The application is just working as expected but whey I run the test case the spring security filter does not execute 'customRestFilter'
Here is my Test Class
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration({"classpath:application-context.xml",
"classpath:spring-rest-servlet.xml",
"classpath:spring-security-context.xml"})
public class Test {
#Autowired
private FilterChainProxy springSecurityFilterChain;
#Autowired
private WebApplicationContext webApplicationContext;
#Autowired
MyController myController;
#Before
public void init(){
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.addFilter(springSecurityFilterChain).build();
}
#Test
public void test1() throws Exception {
given().mockMvc(mockMvc).standaloneSetup(myController)
.header("Authorization", userHeaderToken) // Token for user
.when().get("/secure")
.then().log().body();
}
}
The userHeaderToken is used to identify which from which user I got the request from and its all handled in 'customRestFilter'. But my requests from Test case never come to the filter.
Can some one guide me how to get the request pass through that filter.
Thanks.
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);
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
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!