Spring-security : Maintaing active sessions even after JVM restart - java

I am working on a Spring-MVC project running on Tomcat in which I have Spring-security for authentication and authorization. As our project is currently in development, we have more deployments than usual. Generally what happens after a deployment is that all the clients are logged out as the server is restarted.
Is there any way where I can put the session-data in database or somewhere, so even after Tomcat/JVM crashes/restarts, already logged-in users wont have a problem.
Is there any strategy to solving this problem? Any idea how?
security-applicationContext.xml :
<!-- Global Security settings -->
<security:http pattern="/resources/template/demo/clients" security="none"/>
<security:http create-session="ifRequired" use-expressions="true" auto-config="false" disable-url-rewriting="true">
<security:form-login login-page="/login" username-parameter="j_username" password-parameter="j_password"
login-processing-url="/j_spring_security_check" default-target-url="/dashboard"
always-use-default-target="true" authentication-failure-url="/denied"/>
<security:remember-me key="_spring_security_remember_me" user-service-ref="userDetailsService"
token-validity-seconds="1209600" data-source-ref="dataSource"/>
<security:logout delete-cookies="JSESSIONID" invalidate-session="true" logout-url="/j_spring_security_logout"/>
<!--<security:intercept-url pattern="/**" requires-channel="https"/>-->
<security:port-mappings>
<security:port-mapping http="80" https="443"/>
</security:port-mappings>
<security:logout logout-url="/logout" logout-success-url="/" success-handler-ref="myLogoutHandler"/>
<security:session-management session-fixation-protection="migrateSession">
<security:concurrency-control session-registry-ref="sessionReg" max-sessions="5" expired-url="/login"/>
</security:session-management>
</security:http>
<beans:bean id="sessionReg" class="org.springframework.security.core.session.SessionRegistryImpl"/>
<beans:bean id="rememberMeAuthenticationProvider"
class="org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices">
<beans:constructor-arg index="0" value="_spring_security_remember_me"/>
<beans:constructor-arg index="1" ref="userDetailsService"/>
<beans:constructor-arg index="2" ref="jdbcTokenRepository"/>
<property name="alwaysRemember" value="true"/>
</beans:bean>
<beans:bean id="jdbcTokenRepository"
class="org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl">
<beans:property name="createTableOnStartup" value="false"/>
<beans:property name="dataSource" ref="dataSource"/>
</beans:bean>
<!-- Remember me ends here -->
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider user-service-ref="LoginServiceImpl">
<security:password-encoder ref="encoder"/>
</security:authentication-provider>
</security:authentication-manager>
<beans:bean id="encoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
<beans:constructor-arg name="strength" value="11"/>
</beans:bean>
<beans:bean id="daoAuthenticationProvider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<beans:property name="userDetailsService" ref="LoginServiceImpl"/>
<beans:property name="passwordEncoder" ref="encoder"/>
</beans:bean>
</beans>
LoginServiceImpl :
#Transactional
#Service("userDetailsService")
public class LoginServiceImpl implements UserDetailsService {
#Autowired
private PersonDAO personDAO;
#Autowired
private Assembler assembler;
public LoginServiceImpl() {
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
Person person = personDAO.findPersonByUsername(username.toLowerCase());
if (person == null) {
throw new UsernameNotFoundException("Wrong username or password");
}
return assembler.buildUserFromUserEntity(person);
}
}
Assembler :
#Service("assembler")
public class Assembler {
#Transactional(readOnly = true)
User buildUserFromUserEntity(Person userEntity) {
String username = userEntity.getUsername().toLowerCase();
String password = userEntity.getPassword();
boolean enabled = userEntity.isEnabled();
boolean accountNonExpired = userEntity.isAccountNonExpired();
boolean credentialsNonExpired = userEntity.isCredentialsNonExpired();
boolean accountNonLocked = userEntity.isAccountNonLocked();
Collection<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
return new User(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
}
}
If there is any more information required, kindly let me know. Thank you.. :-)

Not sure of the configuration available in Spring, but this is possible with Tomcat(you mentioned using tomcat).
In your context.xml file, locate this line/statement
<Manager pathname="" />
Comment/delete this entry. This shall enable session persistence across graceful tomcat restarts.

Related

Spring-Security : Redirect to previous page after login(Tried other SO links)

I am working on a Spring-MVC application in which we are using Spring-Security for authentication and authorization. In our application we send out emails with URL's. Many times the user is not logged in, but after login, we would want to redirect to the original link after login. Thank you. Tried XML configuration, but it is not working.
Xml config :
<!-- Global Security settings -->
<security:http pattern="/resources/**" security="none"/>
<security:http create-session="ifRequired" use-expressions="true" auto-config="false" disable-url-rewriting="true">
<security:form-login login-page="/login" username-parameter="j_username" password-parameter="j_password"
login-processing-url="/j_spring_security_check" default-target-url="/canvaslisting"
always-use-default-target="true" authentication-failure-url="/denied"/>
<security:remember-me key="_spring_security_remember_me" user-service-ref="userDetailsService"
token-validity-seconds="1209600" data-source-ref="dataSource"/>
<security:logout delete-cookies="JSESSIONID" invalidate-session="true" logout-url="/j_spring_security_logout"/>
<security:intercept-url pattern="/**" requires-channel="https"/>
<security:port-mappings>
<security:port-mapping http="8080" https="8443"/>
</security:port-mappings>
<security:logout logout-url="/logout" logout-success-url="/" success-handler-ref="myLogoutHandler"/>
<security:session-management session-fixation-protection="newSession">
<security:concurrency-control session-registry-ref="sessionReg" max-sessions="5" expired-url="/login"/>
</security:session-management>
</security:http>
<beans:bean id="sessionReg" class="org.springframework.security.core.session.SessionRegistryImpl"/>
<beans:bean id="rememberMeAuthenticationProvider"
class="org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices">
<beans:constructor-arg index="0" value="_spring_security_remember_me"/>
<beans:constructor-arg index="1" ref="userDetailsService"/>
<beans:constructor-arg index="2" ref="jdbcTokenRepository"/>
<property name="alwaysRemember" value="true"/>
</beans:bean>
<beans:bean id="jdbcTokenRepository"
class="org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl">
<beans:property name="createTableOnStartup" value="false"/>
<beans:property name="dataSource" ref="dataSource"/>
</beans:bean>
<!-- Remember me ends here -->
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider user-service-ref="LoginServiceImpl">
<security:password-encoder ref="encoder"/>
</security:authentication-provider>
</security:authentication-manager>
<beans:bean id="encoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
<beans:constructor-arg name="strength" value="11"/>
</beans:bean>
<beans:bean id="daoAuthenticationProvider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<beans:property name="userDetailsService" ref="LoginServiceImpl"/>
<beans:property name="passwordEncoder" ref="encoder"/>
</beans:bean>
<beans:bean id="authenticationFilter"
class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<beans:property name="filterProcessesUrl" value="/login" />
<beans:property name="authenticationManager" ref="authenticationManager" />
<beans:property name="authenticationSuccessHandler">
<beans:bean class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<beans:property name="useReferer" value="true"/>
</beans:bean>
</beans:property>
<beans:property name="authenticationFailureHandler">
<beans:bean class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<beans:property name="defaultFailureUrl" value="/login?login_error=t" />
</beans:bean>
</beans:property>
</beans:bean>
</beans>
Thank you.
Although you have not shown how you have configured your tag, so I am assuming that, you already have, that see bellow code.
<security:http auto-config="true" use-expressions="false">
<security:form-login login-page="/login" login-processing-url="/login"
username-parameter="custom_username"
password-parameter="custom_password"
default-target-url="/appointments/"
always-use-default-target="true"
authentication-failure-url="/login?error=true"
/>
<security:intercept-url pattern="/appointments/*" access="ROLE_USER"/>
<security:intercept-url pattern="/schedule/*" access="ROLE_FOO"/>
<security:intercept-url pattern="/**" access="ROLE_ANONYMOUS, ROLE_USER"/>
</security:http>
so, if you are not mentioning these two lines
default-target-url="/appointments/"
always-use-default-target="true"
, then by default spring security will redirect you user to the same page (which he requested), and got authentication prompt.
If this still does not full fills your requirement then you may need to implement your own filter or take a look at FilterSecurityInterceptor or MethodSecurityInterceptor .
Bellow is an Example, which will help you to achieve your goal:
To understand this problem better, take a look on below example:
- user receives a newsletter mail where he's invited to vote for more beautiful holidays picture. The URL contains a hash parameter used to authenticate user in the page.
- when user clicks on given URL, it will arrive to voting page as already authenticated user.
So My Custom Filter Will be like bellow:
package com.osigu.ehr.config;
public class OneShotActionFilter extends GenericFilterBean {
private static final Logger LOGGER =
LoggerFactory.getLogger(OneShotActionFilter.class);
private static Map<String, String> users = new HashMap<String, String>();
static {
users.put("0000000000001", "bartosz");
users.put("0000000000002", "admin");
users.put("0000000000003", "mod");
}
private static final String PARAM_NAME = "uio";
private AuthenticationManager authenticationManager;
private UserDetailsService userDetailsService;
private final RedirectStrategy redirectStrategy = new
DefaultRedirectStrategy();
private enum AuthenticationStates {
REDIRECT, CONTINUE;
}
public void setAuthenticationManager(AuthenticationManager
authenticationManager) {
this.authenticationManager = authenticationManager;
}
public void setUserDetailsService(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain) throws IOException,
ServletException {
LOGGER.debug("One shot filter invoked");
if (attemptAuthentication(request) == AuthenticationStates.REDIRECT) {
// Note that we should handle that dynamically but for learning purposes
//we'll consider that one-shot
// authentication works only for this URL
this.redirectStrategy.sendRedirect((HttpServletRequest) request,
(HttpServletResponse) response,
"/secret/one-shot-action");
} else {
LOGGER.debug("User was not correctly authenticated, continue filter chain");
// continue execution of all other filters
// You can test the code without this fragment in the pages without ?uio parameter. You should see blank page because of
// security filter chain interruption.
filterChain.doFilter(request, response);
}
}
private AuthenticationStates attemptAuthentication(ServletRequest request) {
AuthenticationStates state = AuthenticationStates.CONTINUE;
Authentication authentication =
SecurityContextHolder.getContext().getAuthentication();
String code = request.getParameter(PARAM_NAME);
if ((authentication == null || !authentication.isAuthenticated()) && code !=
null &&
users.containsKey(code)) {
LOGGER.debug("Checking user for code " + code);
UserDetails user = userDetailsService.loadUserByUsername(users.get(code));
LOGGER.debug("Found user from code (" + users.get(code) + "). User found is " + user);
if (user != null) {
users.remove(code);
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(user.getUsername(),
user.getPassword());
authentication = this.authenticationManager.authenticate(authRequest);
if (authentication != null && authentication.isAuthenticated()) {
SecurityContextHolder.getContext().setAuthentication(authentication);
state = AuthenticationStates.REDIRECT;
}
}
}
return state;
}
}
And Finally Configure it in your xml, like bellow
<security:http authentication-manager-ref="frontend" auto-config="true" use-
expressions="true" access-denied-page="/access-denied">
<-- other filters are defined here -->
<security:custom-filter ref="oneShootAuthFilter"
after="CONCURRENT_SESSION_FILTER"/>
</security:http>
<bean id="oneShootAuthFilter"
class="com.waitingforcode.security.filter.OneShotActionFilter">
<property name="authenticationManager" ref="frontend" />
<property name="userDetailsService" ref="inMemoryUserService" />
</bean>
To test the filter, we can try to access to http://localhost:8080/?uio=0000000000001. You should be redirected (but only once) to http://localhost:8080/secret/one-shot-action page
I wrote this example for better clarity and as reference for future questions.
Do up vote if you think it helped you to clear your doubt.
you can also visit this link

spring login script how to check the db is connected?

i'm learning spring framework, and i created a login app according to
http://o7planning.org/web/fe/default/en/document/29799/simple-login-web-application-using-spring-mvc-spring-security-and-spring-jdbc
but when i try to login using the username & password at database, it redirects me to error, here is the xml
spring-security.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.2.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd">
<http use-expressions="true">
<intercept-url pattern="/" access="isAnonymous()" />
<intercept-url pattern="/welcome" access="isAnonymous()" />
<intercept-url pattern="/login" access="isAnonymous()" />
<intercept-url pattern="/logout" access="isAnonymous()" />
<intercept-url pattern="/userInfo"
access="hasAnyRole('ROLE_USER', 'ROLE_ADMIN')" />
<intercept-url pattern="/admin" access="hasRole('ROLE_ADMIN')" />
<intercept-url pattern="/other/**" access="isAuthenticated()" />
<access-denied-handler error-page="/403" />
<form-login login-page='/login' login-processing-url="/j_spring_security_check"
default-target-url="/userInfo" always-use-default-target="false"
authentication-failure-url="/login?error=true" username-parameter="username"
password-parameter="password" />
<logout logout-url="/logout" logout-success-url="/logoutSuccessful"
delete-cookies="JSESSIONID" invalidate-session="true" />
</http>
<authentication-manager>
<!-- authentication from database -->
<authentication-provider>
<jdbc-user-service data-source-ref="myDataSource"
users-by-username-query="select username,password, enabled from users where username=?"
authorities-by-username-query="select username, role from users where username=?" />
</authentication-provider>
</authentication-manager>
</beans:beans>
data-source-cfg.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd">
<bean id="myDataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/testdb_o7" />
<property name="username" value="root" />
<property name="password" value="" />
</bean>
</beans>
i use mysql, how can i check the app is connected to DB and retrieved the right username & password sucessfully
<beans:bean id="customUserDetailsService" class="com.myclass.dao.SimpleUserDetailsService">
</beans:bean>
<authentication-manager>
<authentication-provider user-service-ref="customUserDetailsService">
<password-encoder hash="md5"> </password-encoder>
<!-- [plaintext, sha, sha-256, md5, md4, {sha}, {ssha}] -->
</authentication-provider>
</authentication-manager>
<beans:bean id="authenticationFailureHandler" class="org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler">
<beans:property name="exceptionMappings">
<beans:props>
<beans:prop key="org.springframework.security.core.userdetails.UsernameNotFoundException">/login.action?error=1</beans:prop>
</beans:props>
</beans:property>
</beans:bean>
In SimpleUserDetailService for example you will test and check user from DB
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.Repository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import UserBean;
#Repository
#Service
#Transactional(readOnly=true)
public class SimpleUserDetailsService implements UserDetailsService {
#Autowired
private UserDao userDAO;
private UserBean domainUser;
public UserDetails loadUserByUsername(String login)
throws UsernameNotFoundException {
domainUser = userDAO.getUser(login);
boolean enabled = true;
boolean disabled = false;
boolean accountNonExpired = true;
boolean accountExpired = false;
boolean credentialsNonExpired = true;
boolean credentialsExpired = false;
boolean accountNonLocked = true;
boolean accountLocked = false;
if (null != domainUser){
return new User(
domainUser.getUsername(), //getLogin(),
domainUser.getPassword(),
enabled,
accountNonExpired,
credentialsNonExpired,
accountNonLocked,
getAuthorities(domainUser.getIduser() ) );
} else {
return new User(
"**20**",
"**//**",
disabled,
accountExpired,
credentialsExpired,
accountLocked,
getAuthorities(-20 ) ) ;
}
}
public Collection<? extends GrantedAuthority> getAuthorities(Integer userid) {
List<GrantedAuthority> authList = getGrantedAuthorities(getRoles(userid));
return authList;
}
public List<String> getRoles(Integer userid) {
List<String> roles = new ArrayList<String>();
roles = userDAO.getUserRoles(userid);
return roles;
}
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;
}
}
in userDAO.getUser(login) you select user from table_user where user_name=:login or email = :login

Spring-Security : SecurityContextHolder not populated with anonymous token, as it already contained

I am working on a Spring-MVC application which uses Spring-Security for authentication and related security purpose. Currently I am having problem querying the sessionRegistry to get a list of currently online users as the getAllPrincipals() method always contains NULL. I tried solutions mentioned in the documentation and many suggestions mentioned on other SO threads. I would really appreciate some help as I am trying various combinations which will work to get list of all Online users.
Here is my security-application-context.xml:
<import resource="servlet-context.xml" />
<!-- Global Security settings -->
<security:global-method-security pre-post-annotations="enabled" />
<security:http pattern="/resources/**" security="none"/>
<security:http create-session="ifRequired" use-expressions="true" auto-config="false" disable-url-rewriting="true">
<security:form-login login-page="/login" login-processing-url="/j_spring_security_check" default-target-url="/canvas/list" always-use-default-target="false" authentication-failure-url="/denied.jsp" />
<security:remember-me key="_spring_security_remember_me" user-service-ref="userDetailsService" token-validity-seconds="1209600" data-source-ref="dataSource"/>
<security:logout delete-cookies="JSESSIONID" invalidate-session="true" logout-url="/j_spring_security_logout"/>
<security:port-mappings>
<security:port-mapping http="80" https="443"/>
</security:port-mappings>
<security:logout logout-url="/logout" logout-success-url="/" success-handler-ref="myLogoutHandler"/>
</security:http>
<bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl" />
<beans:bean id="sas" class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy">
<beans:constructor-arg name="sessionRegistry" ref="sessionRegistry" />
<beans:property name="maximumSessions" value="-1" />
</beans:bean>
<beans:bean id="rememberMeAuthenticationProvider" class="org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices">
<beans:property name="key" value="_spring_security_remember_me" />
<property name="alwaysRemember" value="true"/>
<beans:property name="tokenRepository" ref="jdbcTokenRepository"/>
<beans:property name="userDetailsService" ref="LoginServiceImpl"/>
</beans:bean>
<beans:bean id="myAuthFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<property name="sessionAuthenticationStrategy" ref="sas"/>
<property name="rememberMeServices" ref="rememberMeAuthenticationProvider"/>
<property name="allowSessionCreation" value="true"/>
<property name="authenticationManager" ref="authenticationManager"/>
</beans:bean>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider user-service-ref="LoginServiceImpl">
<security:password-encoder ref="encoder"/>
</security:authentication-provider>
</security:authentication-manager>
<beans:bean id="encoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
<beans:constructor-arg name="strength" value="11" />
</beans:bean>
<beans:bean id="daoAuthenticationProvider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<beans:property name="userDetailsService" ref="LoginServiceImpl"/>
<beans:property name="passwordEncoder" ref="encoder"/>
</beans:bean>
here is where I am trying to get back the list of OnlineUsers :
#Service
#Transactional
public class OnlineUsersServiceImpl implements OnlineUsersService {
#Autowired
#Qualifier("sessionRegistry")
private SessionRegistry sessionRegistry;
#Override
public boolean checkIfUserhasSession(int id) {
Person person = this.personService.getPersonById(id);
String email = person.getUsername();
List<Object> principals = sessionRegistry.getAllPrincipals();
for (Object principal : principals) {
// It never reaches here
System.out.println("We reached here");
if (principal instanceof User) {
String username = ((User) principal).getUsername();
System.out.println("Username is "+username);
if(email.equals(username)){
return true;
}
}
}
return false;
}
Any help would be nice. As I am on this hopeless thing since 3-4 days.
Before injecting session registry you need to define session management part in your security-application-context.xml
In concurrency-control section you should set alias for session registry object
<security:http access-denied-page="/error403.jsp" use-expressions="true" auto-config="false">
<security:session-management session-fixation-protection="migrateSession" session-authentication-error-url="/login.jsp?authFailed=true">
<security:concurrency-control max-sessions="1" error-if-maximum-exceeded="true" expired-url="/login.html" session-registry-alias="sessionRegistry"/>
</security:session-management>
...
</security:http>
For java based configuration alternative to xml configuration
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(final HttpSecurity http) throws Exception {
// ...
http.sessionManagement().maximumSessions(1).sessionRegistry(sessionRegistry());
}
#Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
#Bean
public ServletListenerRegistrationBean<HttpSessionEventPublisher> httpSessionEventPublisher() {
return new ServletListenerRegistrationBean<HttpSessionEventPublisher>(new HttpSessionEventPublisher());
}
}
You need additional configuration in web.xml - listener for login/logout events
<listener>
<listener-class>
org.springframework.security.web.session.HttpSessionEventPublisher
</listener-class>
</listener>

Redirect from login URL

i am trying to redirect from login page when there is session. is there any option in class org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint below my spring xml. kindly give your inputs to achieve this
<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.1.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<beans:bean id="springSecurityFilterChain"
class="org.springframework.security.web.FilterChainProxy">
<filter-chain-map path-type="ant">
<filter-chain pattern="/resources/template/img/**"
filters="none" />
<filter-chain pattern="/resources/template/css/**"
filters="none" />
<filter-chain pattern="/resources/template/js/**"
filters="none" />
<filter-chain pattern="/resources/template/misc/**"
filters="none" />
<filter-chain pattern="/resources/js/**"
filters="none" />
<filter-chain pattern="/resources/tiles/**"
filters="none" />
<filter-chain pattern="/resources/img/**"
filters="none" />
<filter-chain pattern="/**"
filters="
securityContextPersistenceFilter,
logoutFilter,
authenticationProcessingFilter,
exceptionTranslationFilter,
filterSecurityInterceptor" />
</filter-chain-map>
</beans:bean>
<beans:bean id="securityContextPersistenceFilter"
class="org.springframework.security.web.context.SecurityContextPersistenceFilter">
</beans:bean>
<beans:bean id="exceptionTranslationFilter"
class="org.springframework.security.web.access.ExceptionTranslationFilter">
<beans:property name="authenticationEntryPoint" ref="authenticationEntryPoint" />
<beans:property name="accessDeniedHandler" ref="accessDeniedHandler" />
</beans:bean>
<beans:bean id="authenticationEntryPoint"
class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<!-- <property name="loginFormUrl" value="/login.jsp?error=entryPoint"
/> -->
<beans:property name="loginFormUrl" value="/login" />
</beans:bean>
<beans:bean id="accessDeniedHandler"
class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
<!-- <property name="errorPage" value="/login.jsp?error=access_denied"
/> -->
<beans:property name="errorPage" value= "/loginfail" />
</beans:bean>
<beans:bean id="authenticationProcessingFilter"
class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<beans:property name="authenticationManager" ref="authenticationManager" />
<beans:property name="authenticationFailureHandler">
<beans:bean class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<beans:property name="defaultFailureUrl" value="/loginfail" />
</beans:bean>
</beans:property>
<beans:property name="authenticationSuccessHandler">
<beans:bean class="org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler">
<beans:property name="defaultTargetUrl" value="/frame_design" />
</beans:bean>
</beans:property>
</beans:bean>
<beans:bean id="filterSecurityInterceptor"
class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<beans:property name="authenticationManager" ref="authenticationManager" />
<beans:property name="accessDecisionManager" ref="accessDecisionManager" />
<beans:property name="securityMetadataSource">
<filter-security-metadata-source
path-type="ant" id="securityDefinitionSource">
<intercept-url pattern="/frame_design*"
access="ADMIN" />
</filter-security-metadata-source>
</beans:property>
</beans:bean>
<beans:bean id="logoutFilter"
class="org.springframework.security.web.authentication.logout.LogoutFilter">
<beans:constructor-arg value="/logout" />
<beans:constructor-arg ref="logoutHandler">
</beans:constructor-arg>
</beans:bean>
<beans:bean id="logoutHandler"
class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler">
</beans:bean>
<authentication-manager alias="authenticationManager">
<authentication-provider ref="authenticationProvider">
</authentication-provider>
</authentication-manager>
<beans:bean id="authenticationProvider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<beans:property name="userDetailsService" ref="employAuthDetailsService" />
<beans:property name="passwordEncoder" ref="passwordEncoder"/>
<beans:property name="saltSource" ref="saltSource"/>
</beans:bean>
<beans:bean id ="passwordEncoder" class="org.springframework.security.authentication.encoding.ShaPasswordEncoder" >
<beans:constructor-arg value="512"/>
<beans:property name="iterations" value="1024"/>
</beans:bean>
<beans:bean id="saltSource"
class="org.springframework.security.authentication.dao.ReflectionSaltSource">
<beans:property name="userPropertyToUse" value="username"></beans:property>
</beans:bean>
<beans:bean id="employAuthDetailsService" class="com.app.myapp.security.UserDetailsServiceImp">
</beans:bean>
<beans:bean id="accessDecisionManager"
class="org.springframework.security.access.vote.AffirmativeBased">
<beans:property name="decisionVoters">
<beans:list>
<beans:ref bean="roleVoter" />
</beans:list>
</beans:property>
</beans:bean>
<beans:bean id="roleVoter"
class="org.springframework.security.access.vote.RoleHierarchyVoter">
<beans:property name="rolePrefix" value="" />
<beans:constructor-arg ref="roleHierarchy" />
</beans:bean>
<beans:bean id="roleHierarchy"
class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl">
<beans:property name="hierarchy">
<beans:value>
ADMIN
<!-- ADMIN > ROLE_OWNER -->
<!-- ROLE_OWNER > ROLE_DISTRIBUTOR -->
<!-- ROLE_DISTRIBUTOR > ROLE_RESELLER -->
<!-- ROLE_RESELLER > ROLE_USER -->
</beans:value>
</beans:property>
</beans:bean>
</beans:beans>
Preventing a already logged in user from accessing the log-in page again.
Most cleanest approach:
You could implement a Filter (HandlerInterceptor or HandlerInterceptorAdapter), that redirects the already loged in user
More a hack: use <sec:authenticate> in the login page to trigger an java script that redirect the user, when he is already logged in.
My LoginPageRedirectInterceptor:
public class LoginPageRedirectInterceptor extends HandlerInterceptorAdapter {
private String[] loginPagePrefixes = new String[] { "/login" };
private String redirectUrl = "/";
private UrlPathHelper urlPathHelper = new UrlPathHelper();
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
if (isInLoginPaths(this.urlPathHelper.getLookupPathForRequest(request))
&& isAuthenticated()) {
response.setContentType("text/plain");
sendRedirect(request, response);
return false;
} else {
return true;
}
}
private boolean isAuthenticated() {
Authentication authentication = SecurityContextHolder.getContext()
.getAuthentication();
if (authentication == null) {
return false;
}
if (authentication instanceof AnonymousAuthenticationToken) {
return false;
}
return authentication.isAuthenticated();
}
private void sendRedirect(HttpServletRequest request,
HttpServletResponse response) {
String encodedRedirectURL = response.encodeRedirectURL(
request.getContextPath() + this.redirectUrl);
response.setStatus(HttpStatus.SC_TEMPORARY_REDIRECT);
response.setHeader("Location", encodedRedirectURL);
}
private boolean isInLoginPaths(String requestUrl) {
for (String login : this.loginPagePrefixes) {
if (requestUrl.startsWith(login)) {
return true;
}
}
return false;
}
}
See: SimpleUrlAuthenticationSuccessHandler#AlwaysuseDefaultTargeturl
<beans:property name="authenticationSuccessHandler">
<beans:bean class="org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler">
<beans:property name="defaultTargetUrl" value="/frame_design" />
<beans:property name="alwaysUseDefaultTargetUrl" value="true" />
</beans:bean>
</beans:property>

Spring Security:DataBase authentication provider

Can't get Spring Security to work with DB authentication provider.
In-memory authentication provider works OK.
Step to reproduce:
when I logged with credentials sb,sb,login() method of AuthenticationService returned false.
There are no related log in Tomcat.
applicationContext.xml:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost/chirokDB?useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<bean id="userDetailsService" class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
service layer:
#Service("authenticationService")
public class AuthenticationServiceImpl implements AuthenticationService {
#Resource(name = "authenticationManager")
private AuthenticationManager authenticationManager;
public boolean login(String username, String password) {
try {
Authentication authenticate = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(
username, password));
if (authenticate.isAuthenticated()) {
SecurityContextHolder.getContext().setAuthentication(authenticate);
return true;
}
} catch (AuthenticationException e) {
}
return false;
}
managed bean level:
public String doLogin() {
boolean isLoggedIn = authenticationService.login(name, password);
if (isLoggedIn) {
return "index";
}
FacesContext.getCurrentInstance().addMessage("login failure", new FacesMessage());
return "failureLogin";
}
applicationContext-security.xml:
<global-method-security pre-post-annotations="enabled"/>
<http auto-config="true">
<form-login login-page="/login.xhtml" default-target-url="/index.xhtml"/>
<intercept-url pattern="/contacts.xhtml" access="ROLE_ANONYMOUS,ROLE_USER"/>
<intercept-url pattern="/delivery.xhtml" access="ROLE_USER"/>
<logout invalidate-session="true"/>
<session-management>
<concurrency-control max-sessions="1" error-if-maximum-exceeded="true"/>
</session-management>
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider>
<jdbc-user-service data-source-ref="dataSource"/>
</authentication-provider>
</authentication-manager>
persistence level:
MySql DB has following standard tables(required by Spring):
1. users
2. authorities
users table has record with username='sb' and password='sb'
authorities table has record with username='sb' and authority='ROLE_USER'
note
with user-in memory all works OK with following config:
<authentication-manager alias="authenticationManager">
<authentication-provider>
<user-service>
<user name="sb" password="sb" authorities="ROLE_USER"/>
</user-service>
</authentication-provider>
</authentication-manager>
assumption:
dataSource injected into org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl
As far Hibernate ORM used, perhaps some other than JdbcDaoImpl should be used?
Check if you're getting an Exception in your empty catch block (which always is a bad idea).

Categories

Resources