/*
* Copyright 2012-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.lightadmin.core.config.context;
import org.lightadmin.core.config.LightAdminConfiguration;
import org.lightadmin.core.web.security.LightAdminRequestCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.access.vote.AffirmativeBased;
import org.springframework.security.access.vote.RoleVoter;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.RememberMeAuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.authentication.encoding.ShaPasswordEncoder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
import org.springframework.security.web.access.ExceptionTranslationFilter;
import org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices;
import org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import javax.servlet.Filter;
import java.io.IOException;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Properties;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Maps.newLinkedHashMap;
import static java.util.Arrays.asList;
@Configuration
public class LightAdminSecurityConfiguration {
private static final String REMEMBER_ME_DIGEST_KEY = "LightAdmin";
private static final String ROLE_ADMIN = "ROLE_ADMIN";
private static final String[] PUBLIC_RESOURCES = {
"/images/**", "/scripts/**", "/styles/**",
"/rest/**/file",
"/login", "/page-not-found", "/access-denied",
"/dynamic/logo"
};
@Value("classpath:users.properties")
private Resource usersResource;
@Autowired
private LightAdminConfiguration lightAdminConfiguration;
@Bean
@Autowired
public FilterChainProxy springSecurityFilterChain(Filter filterSecurityInterceptor, Filter authenticationFilter, Filter rememberMeAuthenticationFilter, Filter logoutFilter, Filter exceptionTranslationFilter, Filter securityContextPersistenceFilter) {
List<SecurityFilterChain> filterChains = newArrayList();
for (String pattern : PUBLIC_RESOURCES) {
filterChains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher(applicationUrl(pattern))));
}
filterChains.add(new DefaultSecurityFilterChain(AnyRequestMatcher.INSTANCE, securityContextPersistenceFilter, exceptionTranslationFilter, logoutFilter, authenticationFilter, rememberMeAuthenticationFilter, filterSecurityInterceptor));
return new FilterChainProxy(filterChains);
}
@Bean
@Autowired
public Filter filterSecurityInterceptor(AuthenticationManager authenticationManager) throws Exception {
FilterSecurityInterceptor filter = new FilterSecurityInterceptor();
filter.setAuthenticationManager(authenticationManager);
filter.setAccessDecisionManager(new AffirmativeBased(asList((AccessDecisionVoter) new RoleVoter())));
filter.setSecurityMetadataSource(securityMetadataSource());
filter.afterPropertiesSet();
return filter;
}
private FilterInvocationSecurityMetadataSource securityMetadataSource() {
LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> map = newLinkedHashMap();
map.put(AnyRequestMatcher.INSTANCE, asList((ConfigAttribute) new SecurityConfig(ROLE_ADMIN)));
return new DefaultFilterInvocationSecurityMetadataSource(map);
}
@Bean
@Autowired
public Filter authenticationFilter(AuthenticationManager authenticationManager, RequestCache requestCache) {
UsernamePasswordAuthenticationFilter authenticationFilter = new UsernamePasswordAuthenticationFilter();
authenticationFilter.setFilterProcessesUrl(applicationUrl("/j_spring_security_check"));
authenticationFilter.setAuthenticationManager(authenticationManager);
SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
successHandler.setRequestCache(requestCache);
authenticationFilter.setAuthenticationSuccessHandler(successHandler);
authenticationFilter.setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler(applicationUrl("/login?login_error=1")));
return authenticationFilter;
}
@Bean
public Filter exceptionTranslationFilter(RequestCache requestCache) {
AccessDeniedHandlerImpl accessDeniedHandler = new AccessDeniedHandlerImpl();
accessDeniedHandler.setErrorPage(applicationUrl("/access-denied"));
LoginUrlAuthenticationEntryPoint authenticationEntryPoint = new LoginUrlAuthenticationEntryPoint(applicationUrl("/login"));
ExceptionTranslationFilter exceptionTranslationFilter = new ExceptionTranslationFilter(authenticationEntryPoint, requestCache);
exceptionTranslationFilter.setAccessDeniedHandler(accessDeniedHandler);
return exceptionTranslationFilter;
}
@Bean
public Filter logoutFilter() {
SecurityContextLogoutHandler logoutHandler = new SecurityContextLogoutHandler();
logoutHandler.setInvalidateHttpSession(false);
LogoutFilter logoutFilter = new LogoutFilter(applicationUrl("/"), logoutHandler);
logoutFilter.setFilterProcessesUrl(applicationUrl("/logout"));
return logoutFilter;
}
@Bean
public Filter securityContextPersistenceFilter() {
HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();
repo.setSpringSecurityContextKey(keyWithNamespace(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY));
return new SecurityContextPersistenceFilter(repo);
}
@Bean
public Filter rememberMeAuthenticationFilter(AuthenticationManager authenticationManager, UserDetailsService userDetailsService) {
TokenBasedRememberMeServices rememberMeServices = new TokenBasedRememberMeServices(REMEMBER_ME_DIGEST_KEY, userDetailsService);
rememberMeServices.setCookieName(keyWithNamespace(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY));
return new RememberMeAuthenticationFilter(authenticationManager, rememberMeServices);
}
@Bean
public RequestCache requestCache() {
return new LightAdminRequestCache();
}
@Bean
@Autowired
public AuthenticationManager authenticationManager(AuthenticationProvider authenticationProvider, AuthenticationProvider rememberMeAuthenticationProvider) {
return new ProviderManager(asList(authenticationProvider, rememberMeAuthenticationProvider));
}
@Bean
@Autowired
public AuthenticationProvider authenticationProvider(UserDetailsService usersService) throws Exception {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setPasswordEncoder(new ShaPasswordEncoder());
provider.setUserDetailsService(usersService);
provider.afterPropertiesSet();
return provider;
}
@Bean
@Primary
public UserDetailsService userDetailsService() throws IOException {
Properties usersPproperties = PropertiesLoaderUtils.loadProperties(usersResource);
return new InMemoryUserDetailsManager(usersPproperties);
}
@Bean
public AuthenticationProvider rememberMeAuthenticationProvider() {
return new RememberMeAuthenticationProvider(REMEMBER_ME_DIGEST_KEY);
}
private String applicationUrl(String path) {
return lightAdminConfiguration.getApplicationUrl(path);
}
private String keyWithNamespace(String key) {
return "lightadmin:" + key;
}
}