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.
Related
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).
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.
I use spring security, spring, hibernate and jsf
authentication work correctly but it always redirects me to the page home.jsf
I want to manage the access of users after authentication
I want to manage the access of users after authentication
if authority = ROLE_ADMIN redirect ves homeadmin.jsf
if authority = ROLE_RH redirect ves homerh.jsf
if authority = ROLE_EXCUTIVE redirect ves homeex.jsf
if authority = ROLE_MANAGER redirect ves homem.jsf
if authority = ROLE_GP redirect ves homegp.jsf
The autority field in the Collaborateur table
the Colaborateur Class is
private Integer idColaborateur;
private Rolecol rolecol;
private String matriculeColaborateur;
private String nomColaborateur;
private String prenomColaborateur;
private String mailColaborateur;
private String pwdColaboratuer;
private String loginColaborateur;
private String adresseColaborateur;
private Boolean flgSuspendu;
private Set<HistoriqueNoteObjctif> historiqueNoteObjctifs = new HashSet<HistoriqueNoteObjctif>(
0);
private Set<Note> notes = new HashSet<Note>(0);
private Set<NoteObjectifs> noteObjectifses = new HashSet<NoteObjectifs>(0);
private Set<CompagneDevaluation> compagneDevaluations = new HashSet<CompagneDevaluation>(
0);
private Set<ColaborateurHierarchique> colaborateurHierarchiques = new HashSet<ColaborateurHierarchique>(
0);
private String authority;
//getter and seter
Datasource configuration is in the file applicationContext.xml
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="root" />
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/modulevsql" />
<property name="password" value="root" />
<property name="maxStatementsPerConnection" value="0" />
<property name="maxAdministrativeTaskTime" value="0" />
<property name="maxConnectionAge" value="0" />
<property name="maxIdleTime" value="0" />
<property name="maxIdleTimeExcessConnections" value="0" />
<property name="maxPoolSize" value="0" />
<property name="maxStatements" value="0" />
</bean>
the User Class is
public class User implements UserDetails {
private static final long serialVersionUID = 1L;
private String name;
private String password;
private Colaborateur user;
public void setUser(Colaborateur user) {
this.user = user;
}
public User(String name) {
FacesContext fc=FacesContext.getCurrentInstance();
UserBean userBean=(UserBean) fc.getApplication().createValueBinding("#{UserBean}").getValue(fc);
userBean.chargerUtilisateur(name);
user = userBean.getUtilisateur();
System.err.println("USERS >>> "+user);
PasswordSupport pswdSupport = new PasswordSupport();
if (user!=null){
System.out.println("User.getLogin() :"+user.getLoginColaborateur());
System.out.println("user.getPwd() :"+user.getPwdColaboratuer());
this.name=user.getMatriculeColaborateur();
this.password=user.getPwdColaboratuer();
System.err.println(pswdSupport.getMD5Hash("1"));
}
}
public Collection<GrantedAuthority> getAuthorities() {
List<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
System.out.println("GrantedAuthorityImpl 1");
System.out.println("GrantedAuthorityImpl 2");
System.out.println("GrantedAuthorityImpl 3");
System.out.println("GrantedAuthorityImpl 4");
grantedAuthorities.add(new GrantedAuthorityImpl("ROLE_VISITEUR"));
return grantedAuthorities;
}
//getter and setter
and this is applicationContext-security.xml file
<?xml version="1.0" encoding="UTF-8"?>
<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.1.xsd">
<global-method-security secured-annotations="enabled">
</global-method-security>
<http pattern="/modules/members/**" access-denied-page="/modules/members/accessDenied.jsf" authentication-manager-ref="MembersAuthenticationManager">
<intercept-url pattern="/modules/members/secure/**" access="ROLE_VISITEUR" />
<intercept-url pattern="/modules/members/secure/homeadmin.jsf" access="ROLE_ADMIN" />
<intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<form-login login-page="/modules/members/login.jsf"
default-target-url="/modules/members/secure/home.jsf"
login-processing-url="/modules/members/j_spring_security_check"
authentication-failure-url="/modules/members/login.jsf" />
<logout logout-url="/modules/members/secure/logout"
logout-success-url="/modules/members/login.jsf" delete-cookies="true" />
</http>
<authentication-manager alias="MembersAuthenticationManager">
<authentication-provider user-service-ref="securityManager">
<password-encoder hash="md5" />
</authentication-provider>
</authentication-manager>
<beans:bean id="securityManager" class="tn.com.security.SecurityManager" />
</beans:beans>
Implement an AuthenticationSuccessHandler and redirect based on the collection of GrantedAuthority objects that's contained within the Authentication that you get passed in.
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authResult) throws IOException, ServletException {
/* Redirect on the successful authentication of the user */
logger.info("Hit the AuthSuccessHandler");
String redirectAddress = null;
Collection<? extends GrantedAuthority> auths = authResult.getAuthorities();
if(auths.contains("ROLE_ADMIN"){
response.sendRedirect(response.encodeURL("homeadmin.jsf");
}
etc etc etc.
You could even add your roles to an Enum and write a switch statement to determine the redirect location.
Make sure you declare your AuthenticationSuccessHandler in your Security Config
<beans:bean id="customAuthenticationSuccessHandler" class="foo.bar.CustomAuthenticationSuccessHandler" />
<form-login login-page="/LoginView"
authentication-success-handler-ref="customAuthenticationSuccessHandler"
authentication-failure-url="/FailedLogin" />
The answer given by JamesENL is correct but with one mention:
You need to iterate over the collection of GrantedAuthority and only afterwards check for the ROLE:
Collection<? extends GrantedAuthority> authorities = authResult.getAuthorities();
for (GrantedAuthority grantedAuthority : authorities) {
if (grantedAuthority.getAuthority().equals("ROLE_USER")) {
response.sendRedirect("/userHome);
return;
} else if (grantedAuthority.getAuthority().equals("ROLE_ADMIN")) {
response.sendRedirect("/adminHome);
return;
}
}
I am getting the following error when creating a bean with the name 'loginDetailsService':
Error
SEVERE: Exception sending context initialized event to listener instance of class
org.springframework.web.context.ContextLoaderListener
org.springframework.beans.factory.BeanCreationException: Error creating bean with name
'loginDetailsService': Injection of autowired dependencies failed; nested exception is
org.springframework.beans.factory.BeanCreationException:Could not autowire field: private
com.kns.jk.repository.UserRepository com.kns.jk.service.LoginDetailsService.userRepository;
nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type [com.kns.jk.repository.UserRepository] found for dependency:
expected at least 1 bean which qualifies as autowire candidate for this dependency.
Dependency annotations:{#org.springframework.beans.factory.annotation.Autowired(required=true)}
My Entity classes:
User.Java
#Entity
#Table(name="users")
public class User{
#Id
private int id;
#OneToOne(cascade=CascadeType.ALL)
#JoinTable(name="user_roles",
joinColumns = {#JoinColumn(name="user_id", referencedColumnName="id")},
inverseJoinColumns = {#JoinColumn(name="role_id", referencedColumnName="id")})
private Role role;
private String password;
private String username;
private String address1;
Role.Java
#Entity
#Table(name="roles")
public class Role {
#Id
private int id;
private String role;
//bi-directional many-to-one association to UserRole
#OneToMany(cascade=CascadeType.ALL)
#JoinTable(name="user_roles",
joinColumns = {#JoinColumn(name="role_id", referencedColumnName="id")},
inverseJoinColumns = {#JoinColumn(name="user_id", referencedColumnName="id")})
private Set<User> userRoles;
public Role() {
}
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
public String getRole() {
return this.role;
}
public void setRole(String role) {
this.role = role;
}
public Set getUserRoles() {
return this.userRoles;
}
public void setUserRoles(Set<User> userRoles) {
this.userRoles = userRoles;
}
UserRepository.java
public interface UserRepository extends JpaRepository<User, Integer>{
public User findByUsername(String username);
public Role getRole(int id);
}
LoginDeatilsService
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import com.kns.jk.repository.UserRepository;
import com.kns.jk.model.Role;
/**
* A custom {#link UserDetailsService} where user information
* is retrieved from a JPA repository
*/
#Service("loginDetailsService")
#Transactional(readOnly = true)
public class LoginDetailsService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
try {
com.kns.jk.model.User domainUser = userRepository.findByUsername(username);
boolean enabled = true;
boolean accountNonExpired = true;
boolean credentialsNonExpired = true;
boolean accountNonLocked = true;
return new User(
domainUser.getUsername(),
domainUser.getPassword().toLowerCase(),
enabled,
accountNonExpired,
credentialsNonExpired,
accountNonLocked,
getAuthorities(domainUser.getRole().getId()));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Retrieves a collection of {#link GrantedAuthority} based on a numerical role
* #param role the numerical role
* #return a collection of {#link GrantedAuthority
*/
public Collection<? extends GrantedAuthority> getAuthorities(Integer role) {
List<GrantedAuthority> authList = getGrantedAuthorities(getRoles(role));
return authList;
}
/**
* Converts a numerical role to an equivalent list of roles
* #param role the numerical role
* #return list of roles as as a list of {#link String}
*/
public List<String> getRoles(Integer role) {
List<String> roles = new ArrayList<String>();
if (role.intValue() == 1) {
roles.add("ROLE_CO");
roles.add("ROLE_AD");
roles.add("ROLE_JS");
roles.add("ROLE_SA");
} else if (role.intValue() == 2) {
roles.add("ROLE_CO");
}
return roles;
}
/**
* Wraps {#link String} roles to {#link SimpleGrantedAuthority} objects
* #param roles {#link String} of roles
* #return list of granted authorities
*/
public static List<GrantedAuthority> getGrantedAuthorities(List<String> roles) {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
for (String role : roles) {
authorities.add(new SimpleGrantedAuthority(role));
}
return authorities;
}
}
security-config.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"
xmlns:context="http://www.springframework.org/schema/context"
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://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<http pattern="/resources" security="none" />
<http auto-config="true" use-expressions="true" >
<intercept-url pattern="/login" access="permitAll"/>
<intercept-url pattern="/logout" access="permitAll"/>
<intercept-url pattern="/denied" access="hasRole('ROLE_JS')"/>
<intercept-url pattern="/" access="hasRole('ROLE_CO')"/>
<intercept-url pattern="/user" access="hasAnyRole('ROLE_JS',ROLE_CO)"/>
<intercept-url pattern="/admin" access="hasAnyRole('ROLE_AD',ROLE_SA)"/>
<form-login login-page="/login"
authentication-failure-url="/login/failure"
default-target-url="/"/>
<access-denied-handler error-page="/denied"/>
<logout invalidate-session="true"
logout-success-url="/logout/success"
logout-url="/logout"/>
</http>
<authentication-manager>
<authentication-provider user-service-ref="loginDetailsService">
<!-- <password-encoder hash="md5"/> -->
</authentication-provider>
</authentication-manager>
<beans:bean id="loggerListener" class="org.springframework.security.authentication.event.LoggerListener"/>
</beans:beans>
mvc-core-config.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<import resource="mvc-view-config.xml"/>
<!--
- POJOs labeled with the #Controller and #Service annotations are auto-detected.
-->
<context:component-scan
base-package="com.kns.jk.web"/>
<mvc:annotation-driven />
<!-- all resources inside folder src/main/webapp/resources are mapped so they can be refered to inside JSP files
(see header.jsp for more details) -->
<mvc:resources mapping="/resources/**" location="/resources/"/>
<!-- uses WebJars so Javascript and CSS libs can be declared as Maven dependencies (Bootstrap, jQuery...) -->
<mvc:resources mapping="/webjars/**" location="classpath:/META-INF/resources/webjars/"/>
<mvc:view-controller path="/" view-name="welcome" />
<!-- bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="formatters">
<set>
<bean class="com.kns.jk.web.ResumeTypeFormatter"/>
</set>
</property>
</bean> -->
<!--
- Message source for this context, loaded from localized "messages_xx" files.
- Files are stored inside src/main/resources
-->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"
p:basename="messages/messages"/>
<!--
- This bean resolves specific types of exceptions to corresponding logical
- view names for error views.
-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<!-- view name resolved using bean of type InternalResourceViewResolver (declared in mvc-view-config.xml) -->
<property name="defaultErrorView" value="exception"/>
<!-- results into 'WEB-INF/jsp/exception.jsp' -->
<property name="warnLogCategory" value="warn"/>
<!-- needed otherwise exceptions won't be logged anywhere -->
</bean>
</beans>
pom.xml
<properties>
<!-- Generic properties -->
<java.version>1.6</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- Spring -->
<spring-framework.version>3.2.4.RELEASE</spring-framework.version>
<spring-data-jpa.version>1.3.4.RELEASE</spring-data-jpa.version>
<!-- Spring Security Version -->
<springsecurity.version>3.2.2.RELEASE</springsecurity.version>
<!-- Java EE / Java SE dependencies -->
<jsp.version>2.2</jsp.version>
<jstl.version>1.2</jstl.version>
<servlet.version>2.5</servlet.version>
<jaxb-impl.version>2.2.7</jaxb-impl.version>
<!-- Hibernate / JPA -->
<hibernate.version>4.2.1.Final</hibernate.version>
<!-- Bean validation -->
<hibernate-validator.version>4.3.1.Final</hibernate-validator.version>
<!-- Database access -->
<tomcat-jdbc.version>7.0.42</tomcat-jdbc.version>
<ehcache.version>2.6.6</ehcache.version>
<hsqldb.version>2.3.0</hsqldb.version>
<!-- AOP -->
<aspectj.version>1.7.3</aspectj.version>
<!-- Logging -->
<logback.version>1.0.13</logback.version>
<slf4j.version>1.7.5</slf4j.version>
<!-- RSS -->
<rome.version>1.0</rome.version>
<!-- Test -->
<junit.version>4.11</junit.version>
<hamcrest.version>1.3</hamcrest.version>
<!-- Dates -->
<jodatime-hibernate.version>1.3</jodatime-hibernate.version>
<jodatime-jsptags.version>1.1.1</jodatime-jsptags.version>
<jodatime.version>2.2</jodatime.version>
<jadira-usertype-core.version>3.1.0.CR8</jadira-usertype-core.version>
<!-- Web dependencies -->
<webjars-bootstrap.version>2.3.0</webjars-bootstrap.version>
<webjars-jquery-ui.version>1.9.2</webjars-jquery-ui.version>
<webjars-jquery.version>1.9.0</webjars-jquery.version>
<dandelion.datatables.version>0.8.14</dandelion.datatables.version>
<mysql.version>5.1.22</mysql.version>
</properties>
Not quite sure why the error is occurring. I have already tried a bunch of solutions available on the Internet.
The key to this is in the original error:
NoSuchBeanDefinitionException:
No qualifying bean of type [com.kns.jk.repository.UserRepository]
UserRepository is an interface, you need to declare an implementation for it in your spring beans definition in mvc-core-config.xml
Adding something like:
<bean id="userRepository" class="com.kns.jk.repository.UserRepositoryImpl" />
Should do the trick. Replace UserRepositoryImpl with the real class name for your implementation of UserRepository.
HTH
I'm using Spring 3.2.0 and the same version of Spring security. On successful login, a user is redirected to one of the protected pages as follows.
public final class LoginSuccessHandler implements AuthenticationSuccessHandler
{
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException
{
Set<String> roles = AuthorityUtils.authorityListToSet(authentication.getAuthorities());
if (roles.contains("ROLE_ADMIN"))
{
response.sendRedirect("admin_side/Home.htm");
return;
}
}
}
I'm using Hibernate. How can I update the login date-time (Last Login) in the database on successful login? I have a submit button on the login page whose POST request doesn't seem to map to a method in its corresponding login controller. The login form's action is actually mapped to the Servlet - j_spring_security_check.
The entire spring-security.xml file is as follows, if it is required.
<?xml version="1.0" encoding="UTF-8"?>
<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.2.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<http pattern="/Login.htm*" security="none"></http>
<http auto-config='true'>
<!--<remember-me key="myAppKey"/>-->
<session-management session-fixation-protection="newSession">
<concurrency-control max-sessions="1" error-if-maximum-exceeded="true" />
</session-management>
<intercept-url pattern="/admin_side/**" access="ROLE_ADMIN" requires-channel="any"/>
<form-login login-page="/" default-target-url="/admin_side/Home.htm" authentication-failure-url="/LoginFailed.htm" authentication-success-handler-ref="loginSuccessHandler"/>
<logout logout-success-url="/Login.htm" invalidate-session="true" delete-cookies="JSESSIONID"/>
</http>
<authentication-manager>
<authentication-provider>
<jdbc-user-service data-source-ref="dataSource"
users-by-username-query="select email_id, password, enabled from user_table where lower(email_id)=lower(?)"
authorities-by-username-query="select ut.email_id, ur.authority from user_table ut, user_roles ur where ut.user_id=ur.user_id and lower(ut.email_id)=lower(?)"/>
</authentication-provider>
</authentication-manager>
<beans:bean id="loginSuccessHandler" class="loginsuccesshandler.LoginSuccessHandler"/>
<global-method-security>
<protect-pointcut expression="execution(* dao.*.*(..))" access="ROLE_ADMIN"/>
</global-method-security>
<!--<global-method-security secured-annotations="enabled" />-->
</beans:beans>
An other way is to register an handler for the AuthenticationSuccessEvent.
#Service
public class UserService implements
ApplicationListener<AuthenticationSuccessEvent> {
#Override
public void onApplicationEvent(AuthenticationSuccessEvent event) {
String userName = ((UserDetails) event.getAuthentication().
getPrincipal()).getUsername();
User user = this.userDao.findByLogin(userName);
user.setLastLoginDate(new Date());
}
}
Why do not do it in authentication success handler directly?
public final class LoginSuccessHandler implements AuthenticationSuccessHandler
{
#Autowired
private UserService userService;
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException
{
String userName = authentication.getPrincipal().getName();
this.userService.updateLastLoginDateForUserByName(userName);
Set<String> roles = AuthorityUtils.authorityListToSet(authentication.getAuthorities());
if (roles.contains("ROLE_ADMIN"))
{
response.sendRedirect("admin_side/Home.htm");
return;
}
}
}
You could also subclass the spring AuthenticationProvider interface and inject it into the <authentication-manager /> element.
The class would be something like
public class AuthenticationProvider extends DaoAuthenticationProvider {
// inject whatever tou want
#Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
return super.authenticate(authentication);
// do what ever you want here
}
}
(supposing you're using DaoAuthenticationProvider)
Then you just need to register beans
<bean class="x.y.z.AuthenticationProvider" id="myAuthProvider" scope="singleton" />
<authentication-manager>
<authentication-provider ref="myAuthProvider">
<jdbc-user-service data-source-ref="dataSource"
users-by-username-query="select email_id, password, enabled from user_table where lower(email_id)=lower(?)"
authorities-by-username-query="select ut.email_id, ur.authority from user_table ut, user_roles ur where ut.user_id=ur.user_id and lower(ut.email_id)=lower(?)"/>
</authentication-provider>
</authentication-manager>
(Don't trust code correctness, I wrote it on the fly. It's just meant to show my idea.)
Stefano