I have this implementation of a login function on my server following this post basically:
#Transactional
public void login(String userId, String password) {
LOGGER.debug("Login for " + userId);
User user = new User(userId, password, true, true, true, true, new ArrayList<GrantedAuthority>());
Authentication auth = new UsernamePasswordAuthenticationToken(user, password,
new ArrayList<GrantedAuthority>());
try {
auth = this.authenticationProvider.authenticate(auth);
} catch(BadCredentialsException e) {
LOGGER.debug("Bad credentials ..");
throw new RuntimeException(e.getMessage());
}
LOGGER.debug("User successfully authenticated [userId="+userId+"]");
SecurityContext sc = new SecurityContextImpl();
sc.setAuthentication(auth);
SecurityContextHolder.setContext(sc);
}
This appears to work so far without any troubles. However, if I execute this request right after login
#PreAuthorize("hasPermission('ADMIN')")
public void test(String msg) {
LOGGER.debug("test: " + msg);
}
the result is this exception here:
Caused by: org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.credentialsNotFound(AbstractSecurityInterceptor.java:378)
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:222)
at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:64)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:653)
at com.mz.server.web.service.LoginService$$EnhancerBySpringCGLIB$$caf0e62d_2.test(<generated>)
at com.mz.server.web.servlet.LoginServletImpl.test(LoginServletImpl.java:99)
Reading about SecurityContextHolder.setContext() it says:
Associates a new SecurityContext with the current thread of execution.
so I guess my problem here is that I don't understand how Spring can recall a user that just logged into my system and now wants to proceed with authenticated request.
And why am I not just getting redirected to /login?
Spring context:
<!-- //////////////////////////////////////////////////////////////////////////////// -->
<!-- // BEGIN Spring Security -->
<sec:http auto-config="true" use-expressions="true"/>
<bean id="httpSessionSecurityContextRepository" class='org.springframework.security.web.context.HttpSessionSecurityContextRepository'>
<property name='allowSessionCreation' value='false' />
</bean>
<bean id="securityContextPersistenceFilter" class="org.springframework.security.web.context.SecurityContextPersistenceFilter">
<constructor-arg ref="httpSessionSecurityContextRepository" />
</bean>
<bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy">
<constructor-arg>
<list>
<sec:filter-chain pattern="/**" filters="securityContextPersistenceFilter" />
</list>
</constructor-arg>
</bean>
<bean id="authenticationListener" class="com.mz.server.web.auth.CustomAuthenticationListener"/>
<bean id="authenticationProvider" class="com.mz.server.web.auth.CustomAuthenticationProvider"/>
<bean id="userDetailsService" class="com.mz.server.web.service.CustomUserDetailsService"/>
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider ref="authenticationProvider"/>
</sec:authentication-manager>
<sec:global-method-security
authentication-manager-ref="authenticationManager"
pre-post-annotations="enabled"/>
<!-- // END Spring Security -->
<!-- //////////////////////////////////////////////////////////////////////////////// -->
web.xml
<!-- //////////////////////////////////////////////////////////////////////////////// -->
<!-- // BEGIN Filters -->
<!-- Spring Security -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/**</url-pattern>
</filter-mapping>
<!-- // END FILTERS -->
<!-- //////////////////////////////////////////////////////////////////////////////// -->
I know your pain, I had a hell of a time figuring out Spring Security. Really debating whether it was worth it. But you probably need to add the SecurityContextPersistenceFilter. This will automatically add your Credentials to the SecurityContext using the HttpSession from the JSessionID typically. I have a custom authentication handler so there is some extra parts to my code that are not relevant but I think this should probably get you pointed in the right direction.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:sec="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">
<bean id="httpSessionSecurityContextRepository"
class='org.springframework.security.web.context.HttpSessionSecurityContextRepository'>
<property name='allowSessionCreation' value='false' />
</bean>
<bean id="securityContextPersistenceFilter"
class="org.springframework.security.web.context.SecurityContextPersistenceFilter">
<constructor-arg ref="httpSessionSecurityContextRepository" />
</bean>
<bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy">
<constructor-arg>
<list>
<sec:filter-chain pattern="/rest/**"
filters="securityContextPersistenceFilter" />
</list>
</constructor-arg>
</bean>
<sec:global-method-security
authentication-manager-ref="authenticationManager"
secured-annotations="enabled" />
Related
First of all I kept the listener in web.xml
<listener>
<listener-class>
org.springframework.security.web.session.HttpSessionEventPublisher
</listener-class>
</listener>
Then my springSecurity.xml goes like
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:security="http://www.springframework.org/schema/security"
xmlns:context="http://www.springframework.org/schema/context"
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.2.xsd">
<security:http auto-config="true" use-expressions="true">
<security:intercept-url pattern="/*" access="permitAll" />
<security:session-management invalid-session-url="/" session-fixation-protection="newSession">
<security:concurrency-control max-sessions="1" error-if-maximum-exceeded="true" session-registry-alias="sessionRegistry"/>
</security:session-management>
<!-- access denied page -->
<security:access-denied-handler error-page="/loginerror" />
<security:form-login
login-page="/login?login_error=1"
default-target-url="/employee/listEmployee"
authentication-failure-url="/login/error"
/>
<security:logout invalidate-session="true" logout-success-url="/login" delete-cookies="JSESSIONID" />
<!-- enable csrf protection -->
<!-- <csrf/>-->
</security:http>
<!-- Select users and user_roles from database -->
<security:authentication-manager>
<security:authentication-provider ref="authenticationProvider"></security:authentication-provider>
</security:authentication-manager>
<bean id="authenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService">
<bean id="userAuthenticationService" class="com.elitenet.los.security.UserDetailsServiceImpl" />
</property>
<property name="passwordEncoder">
<bean class="org.springframework.security.authentication.encoding.ShaPasswordEncoder" />
</property>
</bean>
The controller goes like:I need the list of userNames which are logged in. But the sessionRegistry isn't working.
#Autowired
#Qualifier("sessionRegistry")
private SessionRegistry sessionRegistry;
#RequestMapping(value = "/showUserStatus",method = RequestMethod.GET)
public ModelAndView showUserStatus() {
List<String> usersNamesList = new ArrayList<String>();
List<User> userList = new ArrayList<User>();
try {
List<Object> principals =sessionRegistry.getAllPrincipals();//the principals here is empty
for (Object principal: principals) {
//import org.springframework.security.core.userdetails for User class
//User is a built in class of spring security core
if (principal instanceof User) {
getLog().info(((User) principal).getUserName());
getLog().info("going to list userNameList");
usersNamesList.add(((User) principal).getUserName());
}
}
getLog().info("going to list user");
userList = getUserService().getList();
} catch (Exception er) {
getLog().error("error while listing userList" + er);
}
return new ModelAndView("/user/showUserStatus", "userList", userList);
}
Can anyone help me what am I doing wrong
Please try mentioning in xml file
<bean id="sessionRegistry"
class="org.springframework.security.core.session.SessionRegistryImpl" />
#Controller class
Try injecting like below
#Resource(name="sessionRegistry")
private SessionRegistryImpl sessionRegistry;
I think you are almost there. The only thing you've probably missed is the use of session-registry-alias. By using that attribute on the concurrency-control element you expose the session registry, so that it can be injected to your own beans.
Now you have a reference to the session registry that will be populated by the ConcurrentSessionControlStrategy which is set up implicitly by the above configuration. To use it you would just inject it to your bean as normal:
<security:session-management>
<security:concurrency-control max-sessions="10" session-registry-ref="sessionRegistry"/>
</security:session-management>
<bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl"/>
or something like below
<bean id="sas" class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy"
p:maximumSessions="1" >
<constructor-arg name="sessionRegistry" ref="sessionRegistry" />
</bean>
I know there are some questions about this topic but mine is a little different. I'm trying to include openID authentication into my project developed with Spring, Spring-security and Spring-MVC.
To achieve the openID auth, some clases are necessary: AccessDeniedHandler and UserDetailsService are configured in applicationContext-security.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:security="http://www.springframework.org/schema/security"
xmlns="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.1.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<!-- turn on global security -->
<security:global-method-security secured-annotations="enabled"/>
<bean id="openIdAuthFailureHandler" class="es.institution.dept.security.MyAccessDeniedHandler"/>
<bean id="userDetailsService" class="es.institution.dept.service.impl.UserDetailsServiceImpl"/>
<security:http auto-config="true">
<security:intercept-url pattern="/welcome*" access="ROLE_USER, ROLE_ADMIN" />
<security:intercept-url pattern="/user/*" access="ROLE_USER, ROLE_ADMIN" />
<security:intercept-url pattern="/rest/*" access="ROLE_USER, ROLE_ADMIN" />
<security:intercept-url pattern="/admin/*" access="ROLE_ADMIN" />
<security:logout logout-success-url="/" />
<security:openid-login login-page="/openidLogin" default-target-url="/welcome" authentication-failure-url="/loginfailed" user-service-ref="userDetailsService"/>
<security:access-denied-handler ref="openIdAuthFailureHandler"/>
</security:http>
<security:authentication-manager>
<security:authentication-provider>
<security:password-encoder hash="md5"/>
<security:jdbc-user-service data-source-ref="dataSource"
users-by-username-query="
SELECT username, password, active as enabled
FROM users WHERE username=?"
authorities-by-username-query="
select ur.username, ur.rolename as authority from users_roles ur
where ur.username=?" />
</security:authentication-provider>
</security:authentication-manager>
</beans>
UserDetailsService is called by Spring when it needs to know user data (username, password, roles...) For this reason, I need to call one of my services (UserService) in UserDetailsService:
public class UserDetailsServiceImpl implements UserDetailsService{
#Autowired
UserService userService;
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
es.institution.dept.model.User user = userService.getUserByUsername("mannuk");
if(user == null)
throw new UsernameNotFoundException("User does not exist");
return new User(user.getUsername(), user.getPassword(), user.isActive(), false, false, false, getGrantedAuthorities(username));
}
public List<GrantedAuthority> getGrantedAuthorities(String username) {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
for (Role role : userService.getAllRoles(username)) {
authorities.add(new SimpleGrantedAuthority(role.getRoleName()));
}
return authorities;
}
}
I tried two options:
1)Define #Service annotation in UserDetailsService which throws an Exception during the start up. It says that UserDetails bean does not exist (it is necessary in applicationSecurity-context.xml)
2)Declare a bean definition in applicationContext-security.xml. The startup is OK (no errors) but the UserService is not autowired.
This is my applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jdbc/adminDB"/>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="/WEB-INF/mybatis-config.xml" />
</bean>
<bean id="usersMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="es.institution.dept.dao.UserMapper" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
<bean id="rolesMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="es.institution.dept.dao.RoleMapper" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
<bean id="groupMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="es.institution.dept.dao.GroupMapper" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
<bean id="policyMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="es.institution.dept.dao.PolicyMapper" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
<!-- Json converter bean -->
<bean id="jacksonMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="objectMapper" ref="jacksonObjectMapper" />
</bean>
<bean id="jacksonObjectMapper" class="org.codehaus.jackson.map.ObjectMapper"></bean>
</beans>
This is my app-servlet.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.1.xsd">
<!-- Enabling Spring beans auto-discovery -->
<context:component-scan base-package="es.institution.dept" />
<!-- Enabling Spring MVC configuration through annotations -->
<mvc:annotation-driven />
<!-- Enabling Spring Async tasks through annotations -->
<task:annotation-driven />
<mvc:view-controller path="/" view-name="login" />
<!-- Load resources -->
<mvc:resources mapping="/resources/**" location="/resources/"/>
<!-- Bean definitions i18n -->
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
<property name="defaultLocale" value="en" />
</bean>
<!-- Intercepts the change of the locale: example.html?ln=en -->
<bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
<property name="paramName" value="ln" />
</bean>
<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping" >
<property name="interceptors">
<list>
<ref bean="localeChangeInterceptor" />
</list>
</property>
</bean>
<!-- Register the messages.properties -->
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="WEB-INF/classes/locale/messages" />
</bean>
<!-- Defining which view resolver to use -->
<bean class= "org.springframework.web.servlet.view.InternalResourceViewResolver" >
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>
Note that UserService is working fine in other places like controllers. It seems to be a problem with the UserDetailsService itself.
If you need more info do not hesitate to write to me. I hope to solve this issue. The solution will be voted and checked.
Beans declared in root application context (applicationContext.xml and applicationContext-security.xml) cannot access beans declared in servler-specific context (app-servlet.xml)
Components of Spring Security (including UserDetailsService) must be declared in root application context
So, you need to declare UserService in applicationContext.xml instead of picking it up by <context:component-scan> in app-servlet.xml.
I am on day 3 of trying to get this to work and haven't had much luck.
Here's the use scenario (just summarizing using my own words here):
Within the existing application we need to integrate Spring security. User login configuration cannot change and we want to use standard annotations within spring (#Secured and #PreAuthorize) to be able to lock down access to RESTful endpoints. Internal objects can be wrapped but are not permitted to be altered.
I added all of the core dependencies in Maven for security:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring.version}</version>
</dependency>
I took the custom User class that was in the system and created two wrappers for it to plug into the Spring Security Framework:
CustomAuthentication implementing org.springframework.security.core.Authentication
CustomUserDetails implementing org.springframework.security.core.userdetails.UserDetails
I then created a org.springframework.security.core.userdetails.UserDetailsService implementation that is annotated using #Service("userDetailService"). That completed the basic Java-side implementation for the Spring Security that I thought I needed in order to get things running.
I created a class com.myapp.rest.SecurityTestController that looks like this:
#Controller
#RequestMapping("/security-test")
public class SecurityTestController {
#RequestMapping("wide-open/{name}")
#ResponseBody
public String restWideOpen(#PathVariable String name, HttpSession session) {
return "Hello, " + name + ", from a wide-open RESTful service.";
}
#Secured("ROLE_XYZ")
#RequestMapping("require-auth/{name}")
#ResponseBody
public String restRequireAuthorization(#PathVariable String name, HttpSession session) {
return "Hello, " + name + ", from a RESTful service requiring authorization.";
}
}
I also updated the internal logging code to add (which was implemented as a #Conroller by the original developer):
Authentication customAuthentication = new CustomAuthentication(user);
SecurityContextHolder.getContext().setAuthentication(customAuthentication);
On logout I just simply added:
SecurityContextHolder.clearContext();
That all seems fine, compiles with not issues, etc. So I decided to move on to the configuration side of things in order to get the application working with security. Without any changes I was able to access both interfaces using the basic URL (in this case that happens to be localhost:8080/myapp/security-test/...).
So here's the configs:
web.xml
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<display-name>MyApp</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>myapp-web</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>myapp-web</servlet-name>
<url-pattern>/myapp/*</url-pattern>
</servlet-mapping>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/myapp/**</url-pattern>
</filter-mapping>
</web-app>
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
<context:annotation-config />
<context:component-scan base-package="com.myapp" />
<context:property-placeholder location="/WEB-INF/servlet.properties" />
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="messages" />
</bean>
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="${maxProfileImageSize}"/>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="${datasource.url}"/>
<property name="username" value="${datasource.username}"/>
<property name="password" value="${datasource.password}"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="typeAliasesPackage" value="com.myapp.bean" />
</bean>
<bean id="velocityEngine" class="org.springframework.ui.velocity.VelocityEngineFactoryBean">
<property name="resourceLoaderPath" value="/WEB-INF/views/velocity/"/>
<property name="configLocation" value="/WEB-INF/velocity.properties"/>
</bean>
<bean id="properties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="location" value="/WEB-INF/servlet.properties"/>
</bean>
</beans>
applicationContext-security.xml
<beans ...>
<sec:global-method-security secured-annotations="enabled" pre-post-annotations="enabled" />
<sec:http auto-config="true" use-expressions="true">
<sec:intercept-url pattern="/myapp/login" access="permitAll" />
</sec:http>
<sec:authentication-manager>
<sec:authentication-provider user-service-ref="userDetailService" />
</sec:authentication-manager>
</beans>
And finally: myapp-web-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
<context:component-scan base-package="com.myapp" />
<context:annotation-config />
<context:property-placeholder location="/WEB-INF/servlet.properties" />
<mvc:annotation-driven/>
<mvc:interceptors>
<bean id="sessionInterceptor" class="com.myapp.web.interceptor.SessionInterceptor" />
</mvc:interceptors>
<bean id="handlerAdapter" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<util:list>
<ref bean="jsonHttpMessageConverter" />
</util:list>
</property>
</bean>
<bean id="jsonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" />
<bean id="handlerMapping" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="useTrailingSlashMatch" value="true" />
</bean>
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>
Now comes the problem:
Everything reminds exactly the same. I can access the RESTful services without having the roles be validated. When I start Tomcat in debug my UserDetailsService implementation never actually gets invoked.
What am I missing? This is completely frustrating me as I've never had problems with Spring Security like this before.
UPDATE
I figured it out.
Of all things to miss, I needed to add this to myapp-web-servlet.xml:
<sec:global-method-security secured-annotations="enabled" pre-post-annotations="enabled" />
Now I just need to figure out how to get it to appropriately load my roles between session restarts.
I think the problem might be with your mappings:
<filter>
<filter-name>myFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>myFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
It seems to be all right. Authorization is working, and users get the roles. I protected the methods of using annotations (for example #PreAuthorize("hasPermission(#post, 'READ')") ), it works and I get access denied. I created a database in which he described the rights of users to objects. I created a database which contains the user's permission on objects.
My problem is that after the user's authorization, it is not getting permissions, and even if the user has the authority to object, he gets access denied. Also, I noticed that after a user login in the log file of the server should get a string which will write what permission he got, but I have no such line.
Pieces of my files:
web.xml
...
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring-security.xml
/WEB-INF/acl-context.xml
/WEB-INF/mvc-dispatcher-servlet.xml
</param-value>
</context-param>...
mvc-dispatcher-servlet.xml
...
<security:global-method-security pre-post-annotations="enabled">
<security:expression-handler ref="expressionHandler" />
</security:global-method-security>
<context:annotation-config />
<tx:annotation-driven />
<tx:jta-transaction-manager />
<context:component-scan base-package="com.bla-bla.bla.controllers" />
<mvc:annotation-driven />
...
spring-security.xml
...
<security:http auto-config="true" use-expressions="true"
access-denied-page="/auth/denied.html">
<security:intercept-url pattern="/auth/login.html" access="permitAll" />
<security:form-login login-page="/auth/login.html"
authentication-failure-url="/auth/login.html?error=true"
default-target-url="/index.html" />
<security:logout invalidate-session="true"
logout-success-url="/auth/login.html" logout-url="/auth/logout.html" />
</security:http>
<security:authentication-manager>
<security:authentication-provider
user-service-ref="userService">
<security:password-encoder ref="pswEncoder" />
</security:authentication-provider>
</security:authentication-manager>
<bean id="userService"
class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
<property name="dataSource" ref="jndiJboss" />
<property name="usersByUsernameQuery"
value="SELECT login, pass, enabled FROM accounts WHERE login=?" />
<property name="authoritiesByUsernameQuery"
value="SELECT login, authority FROM accounts WHERE login=?" />
</bean>
<bean
class="org.springframework.security.authentication.encoding.Md5PasswordEncoder"
id="pswEncoder" />
...
acl-context.xml
...
<bean id="expressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler"
p:permissionEvaluator-ref="permissionEvaluator"
p:roleHierarchy-ref="roleHierarchy" />
<bean class="org.springframework.security.acls.AclPermissionEvaluator" id="permissionEvaluator">
<constructor-arg ref="aclService"/>
</bean>
<bean class="org.springframework.security.acls.jdbc.JdbcMutableAclService" id="aclService">
<constructor-arg ref="jndiJboss"/>
<constructor-arg ref="lookupStrategy"/>
<constructor-arg ref="aclCache"/>
</bean>
<bean id="lookupStrategy" class="org.springframework.security.acls.jdbc.BasicLookupStrategy">
<constructor-arg ref="jndiJboss"/>
<constructor-arg ref="aclCache"/>
<constructor-arg ref="aclAuthorizationStrategy"/>
<constructor-arg ref="auditLogger"/>
</bean>
<bean id="jndiJboss" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:/JBossDB"/>
</bean>
<bean id="aclCache" class="org.springframework.security.acls.domain.EhCacheBasedAclCache">
<constructor-arg>
<bean class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager">
<bean class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/>
</property>
<property name="cacheName" value="aclCache"/>
</bean>
</constructor-arg>
</bean>
<bean id="aclAuthorizationStrategy" class="org.springframework.security.acls.domain.AclAuthorizationStrategyImpl">
<constructor-arg>
<list>
<bean class="org.springframework.security.core.authority.GrantedAuthorityImpl">
<constructor-arg value="ROLE_ADMIN"/>
</bean>
<bean class="org.springframework.security.core.authority.GrantedAuthorityImpl">
<constructor-arg value="ROLE_ADMIN"/>
</bean>
<bean class="org.springframework.security.core.authority.GrantedAuthorityImpl">
<constructor-arg value="ROLE_ADMIN"/>
</bean>
</list>
</constructor-arg>
</bean>
<bean id="auditLogger" class="org.springframework.security.acls.domain.ConsoleAuditLogger"/>
<bean id="roleHierarchy" class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl">
<property name="hierarchy">
<value>
ROLE_ADMIN > ROLE_USER
</value>
</property>
</bean>
...
An example of a protected method of the controller
#Override
#RequestMapping(value = "/post/delete.html", method = RequestMethod.GET)
#Transactional
#PreAuthorize("hasPermission(#post, 'READ')")
public String delete(final Post post) {
//some actions
return "post/view";
}
In what could be the problem?
UPD. My problem was in the wrong filling acl_object_identity
Post the code that uses the ACLs (secured method invocations) and also highlight those classes in the Spring config files. Also, why are you placing the <security:global-method-security /> element in your MVC config file?
I want to add the remember-me into my login page, by reading here , it needs a UserDetailsService. But my UserDetailsService is not getting called, can anyone point where i am wrong? Thanks.
The spring-security.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<!-- configure Spring-Security
auto-config is false.
use-expressions is true: see http://static.springsource.org/spring-security/site/docs/3.1.x/reference/el-access.html
access-denied-page: which page is redirected when login is denied
entry-point-ref: This attribute allows this behaviour to be overridden by defining a customized
AuthenticationEntryPoint bean which will start the authentication process
-->
<security:http auto-config="false" use-expressions="true" entry-point-ref="authenticationEntryPoint" >
<!-- define how to handle the url /auth/login, primitAll is used since we defined use-expressions=true -->
<security:intercept-url pattern="/login" access="permitAll"/>
<security:intercept-url pattern="/search" access="hasRole('ROLE_USER')"/>
<!-- The logout element adds support for logging out by navigating to a particular URL.
The default logout URL is /j_spring_security_logout,
but you can set it to something else using the logout-url attribute -->
<security:logout
invalidate-session="true"
logout-success-url="/login" />
<security:custom-filter ref="blacklistFilter" before="FILTER_SECURITY_INTERCEPTOR"/>
<security:custom-filter ref="authenticationFilter" position="FORM_LOGIN_FILTER"/>
</security:http>
<!-- Custom filter to deny unwanted users even though registered -->
<bean id="blacklistFilter" class="com.myapp.filter.BlacklistFilter" />
<!-- Custom filter for username and password. we need to create another 4 beans -->
<bean id="authenticationFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter"
p:rememberMeServices-ref="rememberMeServices"
p:authenticationManager-ref="customAuthenticationManager"
p:authenticationFailureHandler-ref="customAuthenticationFailureHandler"
p:authenticationSuccessHandler-ref="customAuthenticationSuccessHandler" />
<!-- Bean 1: Custom authentication manager. -->
<bean id="customAuthenticationManager" class="com.myapp.manager.CustomAuthenticationManager" />
<!-- bean 2: set the default failure url here -->
<bean id="customAuthenticationFailureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"
p:defaultFailureUrl="/login?error=true" />
<!-- bean 3: set the default target url here -->
<bean id="customAuthenticationSuccessHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler"
p:defaultTargetUrl="/search" />
<!-- bean 4: remember me -->
<bean id="rememberMeServices"
class="org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
<property name="userDetailsService" ref="userDetailsService"/>
<property name="key" value="myapp"/>
</bean>
<bean id="userDetailsService" class="com.myapp.service.UserDetailsServiceImpl" />
<bean id="authenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint"
p:loginFormUrl="/login"/>
<security:authentication-manager/></beans>
Thanks, Ralph
I added the filter, but UserDetailsServiceImpl is still not called, there is a stop point.
public UserDetails loadUserByUsername(String email)
throws UsernameNotFoundException {
logger.info("User details service is called");
return null;
}
and now the configuration is :
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<!-- configure Spring-Security
auto-config is false.
use-expressions is true: see http://static.springsource.org/spring-security/site/docs/3.1.x/reference/el-access.html
access-denied-page: which page is redirected when login is denied
entry-point-ref: This attribute allows this behaviour to be overridden by defining a customized
AuthenticationEntryPoint bean which will start the authentication process
-->
<security:http auto-config="false" use-expressions="true" entry-point-ref="authenticationEntryPoint" >
<!-- define how to handle the url /auth/login, primitAll is used since we defined use-expressions=true -->
<security:intercept-url pattern="/login" access="permitAll"/>
<security:intercept-url pattern="/search" access="hasRole('ROLE_USER')"/>
<!-- The logout element adds support for logging out by navigating to a particular URL.
The default logout URL is /j_spring_security_logout,
but you can set it to something else using the logout-url attribute -->
<security:logout
invalidate-session="true"
logout-success-url="/login" />
<security:custom-filter ref="blacklistFilter" before="FILTER_SECURITY_INTERCEPTOR"/>
<security:custom-filter ref="authenticationFilter" position="FORM_LOGIN_FILTER"/>
<security:custom-filter ref="rememberMeFilter" position="REMEMBER_ME_FILTER"/>
</security:http>
<!-- Custom filter to deny unwanted users even though registered -->
<bean id="blacklistFilter" class="com.myapp.filter.BlacklistFilter" />
<!-- Custom filter for username and password. we need to create another 4 beans -->
<bean id="authenticationFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter"
p:rememberMeServices-ref="rememberMeServices"
p:authenticationManager-ref="customAuthenticationManager"
p:authenticationFailureHandler-ref="customAuthenticationFailureHandler"
p:authenticationSuccessHandler-ref="customAuthenticationSuccessHandler" />
<!-- Bean 1: Custom authentication manager. -->
<bean id="customAuthenticationManager" class="com.myapp.manager.CustomAuthenticationManager" />
<!-- bean 2: set the default failure url here -->
<bean id="customAuthenticationFailureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"
p:defaultFailureUrl="/login?error=true" />
<!-- bean 3: set the default target url here -->
<bean id="customAuthenticationSuccessHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler"
p:defaultTargetUrl="/search" />
<!-- bean 4: remember me -->
<bean id="rememberMeServices"
class="org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
<property name="userDetailsService" ref="userDetailsService"/>
<property name="key" value="myapp"/>
</bean>
<bean id="userDetailsService" class="com.myapp.service.UserDetailsServiceImpl" />
<bean id="rememberMeFilter" class="org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter">
<property name="rememberMeServices" ref="rememberMeServices"/>
<property name="authenticationManager" ref="customAuthenticationManager" />
</bean>
<bean id="authenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint"
p:loginFormUrl="/login"/>
<security:authentication-manager alias="theAuthenticationManager"/></beans>
It looks like you forgot to add the RememberMeAuthenticationFilter. -- Have a look at the example in the doc you mentioned, you will see what I mean.
Try to reduce your configuration first, to the very default configuration like in http://www.i-develop.be/blog/2010/02/04/spring-security-remember-me/