Several days ago I migrated from xml config to Java config and suddenly got that Spring security does not protecting any url, filters not working at all!
I implemented WebApplicationInitializer interface to configure Dispatcher servlet, and my config looks this:
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.setConfigLocation("com.test.project");
servletContext.addListener(new ContextLoaderListener(context));
ServletRegistration.Dynamic servlet = servletContext.addServlet("dispatcher",
new DispatcherServlet(context));
servlet.setLoadOnStartup(1);
servlet.addMapping("/rest/*");
}
Security config looks like this:
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Bean
public AutheProvider authProvider() {
return new AuthProvider();
}
#Bean
public AuthenticationFilter authenticationFilter() {
return new AuthenticationFilter();
}
#Bean
public UnauthorizedEntryPoint unauthorizedEntryPoint() {
return new UnauthorizedEntryPoint();
}
#Bean
#Override
public AuthenticationManager authenticationManager() {
ProviderManager providerManager = new ProviderManager(
Arrays.asList(authProvider()));
providerManager.setEraseCredentialsAfterAuthentication(false);
return providerManager;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.exceptionHandling().authenticationEntryPoint(unauthorizedEntryPoint())
.and()
.addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/rest/**").authenticated();
}
}
What I have missed?
SecurityConfiguration class is located in com.test.project package.
After project starts I can access any URL, there is no authenticationobject in SecurityContext and filters are never called, neiter mine, nor Spring Security filters.
Related
I have configured Basic Authentication my Spring-Boot application. Everything is Java Config, no xml.
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// Authenticate username -> admin, password -> admin & set role as "ROLE_USER"
auth.inMemoryAuthentication().withUser("admin").password("admin").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login").permitAll()
// All Requests should be Authenticated
.anyRequest().authenticated()
.and()
// Enable Basic Authentication
.httpBasic()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/main", true)
.loginProcessingUrl("/session")
.usernameParameter("Username").passwordParameter("Password")
.and()
.logout().logoutUrl("/logout").permitAll()
.and().csrf().disable();
}
}
It's configured for both Basic authentication and normal form login. When I tested the basic authentication from Rest-Client on Firefox, I can access the secure url "/main". But in the response headers, I'm getting Set-Cookie: JSESSIONID=301225C7AE7C74B0892887389996785D;.
I don't want cookies to be generated for basic authentication. I want true Stateless session for Basic Authentication. Do note that I need cookies to be generated for form-login to work, so disabling cookies is not an option. I know about the create-session="stateless" in xml configuration, but is there any way to do the same in Java config so that Basic Authentication is Stateless and Form-Authentication is Statefull..?
I know about the create-session="stateless" in xml configuration, but is there any way to do the same in Java config so that Basic Authentication is Stateless and Form-Authentication is Statefull..?
You can do the following.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
And For your problem following custom Java Config can be used.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
#Inject
UserDetailsService userService;
#Bean
public AuthenticationManager authenticationManager() throws Exception {
AuthenticationManager authenticationManager = new ProviderManager(
Arrays.asList(authenticationProvider()));
return authenticationManager;
}
#Bean
public AuthenticationProvider authenticationProvider() throws Exception {
CustomAuthenticationProvider authenticationProvider = new CustomAuthenticationProvider();
authenticationProvider.setUserDetailsService(userService);
authenticationProvider.setSaltSource(saltSource());
authenticationProvider.setPasswordEncoder(passwordEncoder());
authenticationProvider.afterPropertiesSet();
return authenticationProvider;
}
#Bean
public SaltSource saltSource() throws Exception {
ReflectionSaltSource saltSource = new ReflectionSaltSource();
saltSource.setUserPropertyToUse("salt");
saltSource.afterPropertiesSet();
return saltSource;
}
#Bean
public PasswordEncoder passwordEncoder() {
return new Md5PasswordEncoder();
}
#Bean
public FilterChainProxy springSecurityFilterChain()
throws ServletException, Exception {
List<SecurityFilterChain> securityFilterChains = new ArrayList<SecurityFilterChain>();
securityFilterChains.add(new DefaultSecurityFilterChain(
new AntPathRequestMatcher("/login**")));
securityFilterChains.add(new DefaultSecurityFilterChain(
new AntPathRequestMatcher("/resources/**")));
securityFilterChains.add(new DefaultSecurityFilterChain(
new AntPathRequestMatcher("/api/**"),
securityContextPersistenceFilterASCFalse(),
basicAuthenticationFilter(), exceptionTranslationFilter(),
filterSecurityInterceptor()));
securityFilterChains.add(new DefaultSecurityFilterChain(
new AntPathRequestMatcher("/**"),
securityContextPersistenceFilterASCTrue(), logoutFilter(),
usernamePasswordAuthenticationFilter(),
exceptionTranslationFilter(), filterSecurityInterceptor()));
return new FilterChainProxy(securityFilterChains);
}
#Bean
public SecurityContextPersistenceFilter securityContextPersistenceFilterASCTrue() {
return new SecurityContextPersistenceFilter(
new HttpSessionSecurityContextRepository());
}
#Bean
public SecurityContextPersistenceFilter securityContextPersistenceFilterASCFalse() {
HttpSessionSecurityContextRepository httpSessionSecurityContextRepository = new HttpSessionSecurityContextRepository();
httpSessionSecurityContextRepository.setAllowSessionCreation(false);
return new SecurityContextPersistenceFilter(
httpSessionSecurityContextRepository);
}
#Bean
public ExceptionTranslationFilter exceptionTranslationFilter() {
ExceptionTranslationFilter exceptionTranslationFilter = new ExceptionTranslationFilter(
new LoginUrlAuthenticationEntryPoint("/login"));
AccessDeniedHandlerImpl accessDeniedHandlerImpl = new AccessDeniedHandlerImpl();
accessDeniedHandlerImpl.setErrorPage("/exception");
exceptionTranslationFilter
.setAccessDeniedHandler(accessDeniedHandlerImpl);
exceptionTranslationFilter.afterPropertiesSet();
return exceptionTranslationFilter;
}
#Bean
public UsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter()
throws Exception {
UsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter = new UsernamePasswordAuthenticationFilter();
usernamePasswordAuthenticationFilter
.setAuthenticationManager(authenticationManager());
usernamePasswordAuthenticationFilter.setAllowSessionCreation(true);
SimpleUrlAuthenticationSuccessHandler successHandler = new SimpleUrlAuthenticationSuccessHandler(
"/index");
successHandler.setAlwaysUseDefaultTargetUrl(true);
usernamePasswordAuthenticationFilter
.setAuthenticationSuccessHandler(successHandler);
usernamePasswordAuthenticationFilter
.setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler(
"/login?error=true"));
usernamePasswordAuthenticationFilter
.setAuthenticationDetailsSource(new CustomWebAuthenticationDetailsSource());
usernamePasswordAuthenticationFilter.afterPropertiesSet();
return usernamePasswordAuthenticationFilter;
}
#Bean
public FilterSecurityInterceptor filterSecurityInterceptor()
throws Exception {
FilterSecurityInterceptor filterSecurityInterceptor = new FilterSecurityInterceptor();
filterSecurityInterceptor
.setAuthenticationManager(authenticationManager());
filterSecurityInterceptor
.setAccessDecisionManager(accessDecisionManager());
filterSecurityInterceptor.setRunAsManager(runAsManager());
LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = new LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>>();
List<ConfigAttribute> configs = new ArrayList<ConfigAttribute>();
configs.add(new org.springframework.security.access.SecurityConfig(
"isAuthenticated()"));
requestMap.put(new AntPathRequestMatcher("/**"), configs);
FilterInvocationSecurityMetadataSource filterInvocationSecurityMetadataSource = new ExpressionBasedFilterInvocationSecurityMetadataSource(
requestMap, new DefaultWebSecurityExpressionHandler());
filterSecurityInterceptor
.setSecurityMetadataSource(filterInvocationSecurityMetadataSource);
filterSecurityInterceptor.afterPropertiesSet();
return filterSecurityInterceptor;
}
public AffirmativeBased accessDecisionManager() throws Exception {
List<AccessDecisionVoter> voters = new ArrayList<AccessDecisionVoter>();
voters.add(new WebExpressionVoter());
voters.add(new RoleVoter());
AffirmativeBased affirmativeBased = new AffirmativeBased(voters);
affirmativeBased.setAllowIfAllAbstainDecisions(false);
affirmativeBased.afterPropertiesSet();
return affirmativeBased;
}
#Bean
public RunAsManager runAsManager() throws Exception {
RunAsManagerImpl runAsManager = new RunAsManagerImpl();
runAsManager.setKey("V_RUN_AS");
runAsManager.afterPropertiesSet();
return runAsManager;
}
#Bean
public LogoutFilter logoutFilter() throws ServletException {
List<LogoutHandler> handlers = new ArrayList<LogoutHandler>();
handlers.add(new CookieClearingLogoutHandler("JSESSIONID"));
handlers.add(new SecurityContextLogoutHandler());
LogoutFilter logoutFilter = new LogoutFilter("/login",
handlers.toArray(new LogoutHandler[] {}));
logoutFilter.afterPropertiesSet();
return logoutFilter;
}
#Bean
public RequestContextFilter requestContextFilter() {
return new RequestContextFilter();
}
#Bean
public BasicAuthenticationFilter basicAuthenticationFilter()
throws Exception {
BasicAuthenticationEntryPoint basicAuthenticationEntryPoint = new BasicAuthenticationEntryPoint();
basicAuthenticationEntryPoint.setRealmName("V_REALM");
basicAuthenticationEntryPoint.afterPropertiesSet();
BasicAuthenticationFilter basicAuthenticationFilter = new BasicAuthenticationFilter(
authenticationManager(), basicAuthenticationEntryPoint);
basicAuthenticationFilter
.setAuthenticationDetailsSource(new CustomWebAuthenticationDetailsSource());
basicAuthenticationFilter.afterPropertiesSet();
return basicAuthenticationFilter;
}
}
This configuration creates two different authentication mechanism.
For any request starting with /api/* it will be using a basicAuthenticationFilter and securityContextPersistenceFilterASCFalse with Session Creation False.
For any request starting with /* it will be using a usernamePasswordAuthenticationFilter and securityContextPersistenceFilterASCTrue with Session Creation True.
You can make use of this and alter it to cater your problem.
For anyone else that comes across this, here's something else to check.
I was hitting this same problem with Spring Boot and even with
sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
I was still seeing JSESSIONID cookies being set. In my case (using JWT), the missing piece seemed to be setting setAllowSessionCreation on the HttpSessionSecurityContextRepository object, like this:
public class StatelessAuthenticationFilter extends GenericFilterBean {
private final MyTokenAuthService authenticationService;
private SecurityContextRepository repository = new HttpSessionSecurityContextRepository();
protected final Logger logger = LoggerFactory.getLogger(getClass().getName());
public StatelessAuthenticationFilter(MyTokenAuthService authenticationService) {
this.authenticationService = authenticationService;
((HttpSessionSecurityContextRepository) repository).setAllowSessionCreation(false);
}
}
What pointed me this were these lines in HttpSessionSecurityContextRepository:
private boolean allowSessionCreation = true;
I am having a spring boot web application and login is handled using Spring security. The application is deployed on tomcat server. I want the login session to expire if idle for 30 seconds. Also the requirement is that, if someone refresh the web page , he should be forced to login again.
But i am with more trouble.
The login session is alive even after restarting the tomcat server. The application is not forcing me to re-login, but directly taking me to the page.
I have the below code in my muti-https security class for handling the flow from the UI application part.
#Configuration
#Order(7)
public static class UIConfigurerAdapter extends
WebSecurityConfigurerAdapter {
#Autowired
private CustUserDetails userDetailsService;
private PasswordEncoder encoder = new CustomEncoder();
#Override
protected void configure(
final HttpSecurity http)
throws Exception {
http.csrf()
.disable().authorizeRequests().anyRequest().authenticated().and()
.formLogin().loginPage("/login").defaultSuccessUrl("/home")
.permitAll().and().logout().permitAll()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/login?logout").permitAll().and()
.httpBasic().and()
.exceptionHandling()
.accessDeniedPage("/error").and().sessionManagement().maximumSessions(1);
}
#Override
protected void configure(
final AuthenticationManagerBuilder auth)
throws Exception {
auth.authenticationProvider(authenticationProvider());
}
/**
* authenticationProvider.
* #return AuthenticationProvider
*/
#Bean
public AuthenticationProvider authenticationProvider() {
LOG.debug("** set authenticationProvider **");
final DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider
.setPasswordEncoder(encoder);
authenticationProvider.setUserDetailsService(userDetailsService);
return authenticationProvider;
}
#Override
public void configure(
final WebSecurity security) {
security.ignoring().antMatchers("/static/**", "/webjars/**", "/info", "/health");
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean()
throws Exception {
return super.authenticationManagerBean();
}
#Bean
public ServletListenerRegistrationBean<HttpSessionEventPublisher> httpSessionEventPublisher() {
return new ServletListenerRegistrationBean<HttpSessionEventPublisher>(new HttpSessionEventPublisher());
}
}
How is the configuration to invalidate the session after a time peroid using spring4 and spring security. I do not have a web.xml file. Also how can i force the http session not to be re-used when the user forcefully refreshed the browser.
I'm trying to configure a Java-based Spring Security redirect to a login page for any request that is not authenticated and currently have the following configuration:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(WebSecurity security) throws Exception {
security
.ignoring()
.antMatchers("/resources/**")
;
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.formLogin()
.loginPage("/login").permitAll()
.and()
.authorizeRequests()
.anyRequest().authenticated()
;
}
}
In the class that implements WebApplicationInitializer I have the following:
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(SecurityConfig.class);
container.addListener(new ContextLoaderListener(rootContext));
Setting a breakpoint within the configure(HttpSecurity httpSecurity) method shows that the method is called on startup, but no request is redirected to /login.
To answer my own question for others with a similar problem. The solution was to add a new empty class extending AbstractSecurityWebApplicationInitializer as follows:
public class SecurityApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
// Nothing
}
I am using Spring 4.0.5.RELEASE and Spring Security 3.2.4.
I am trying to create a simple sample app using java config (based on the Spring samples). The app starts up and the authentication works correctly, that is, I am redirected to a login form when accessing protected url /settings/profile
However there is no /logout url generated? if I hit localhost:8080/logout I get a 404.
I've used similar code on a previous project, so maybe has something to do with versions?
Heres my Security Config
#Configuration
#EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
auth.inMemoryAuthentication().withUser("admin").password("password").roles("ADMIN");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/settings/**").hasRole("ROLE_ADMIN")
.and()
.formLogin()
.and()
.logout()
.deleteCookies("remove")
.invalidateHttpSession(true)
.logoutUrl("/logout")
.logoutSuccessUrl("/logout-success")
.permitAll();
}
}
Here is my WebAppInitializer to bootstrap the app
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { SecurityConfig.class , MvcConfig.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return null;
}
#Override
protected String[] getServletMappings() {
return new String[] {"/"};
}
}
and finally my MvcConfig
#EnableWebMvc
#Configuration
#ComponentScan(basePackages = {"web"})
public class MvcConfig extends WebMvcConfigurerAdapter {
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}
By default POST request is required to the logout url. To perform logout on GET request you need:
http
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"));
Or if you want to support PUT or other method, pass this as a parameter:
http
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout", "PUT"));
See the Docs: http://docs.spring.io/spring-security/site/docs/3.2.4.RELEASE/reference/htmlsingle/ (section 6.5.3. Logging Out)
I'm trying to add web security in spring but I don't want the filter to apply to certain things. How is that done in java?
And maybe there's a better way to do this because I created a custom filter but this is the only way I can think to instantiate it because of its dependencies.
Overall, what I want to do is this:
/resources/** SHOULD NOT go through the filter,
/login (POST) SHOULD NOT go through the filter,
everything else SHOULD go through the filter
Through various example I found through spring I was able to come up with this as for a start but it obviously doesn't work:
#Configuration
#EnableWebSecurity
#Import(MyAppConfig.class)
public class MySecurityConfig extends WebSecurityConfigurerAdapter
{
#Override
public void configure(WebSecurity webSecurity) throws Exception
{
webSecurity.ignoring().antMatchers("/resources/**");
}
#Override
public void configure(HttpSecurity httpSecurity) throws Exception
{
httpSecurity
.authorizeRequests()
.antMatchers("/resources/**").permitAll()
.antMatchers("/login").permitAll();
httpSecurity.httpBasic();
httpSecurity.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Bean
#Autowired
public TokenFilterSecurityInterceptor<TokenInfo> tokenInfoTokenFilterSecurityInterceptor(MyTokenUserInfoCache userInfoCache, ServerStatusService serverStatusService, HttpSecurity httpSecurity) throws Exception
{
TokenService<TokenInfo> tokenService = new TokenServiceImpl(userInfoCache);
TokenFilterSecurityInterceptor<TokenInfo> tokenFilter = new TokenFilterSecurityInterceptor<TokenInfo>(tokenService, serverStatusService, "RUN_ROLE");
httpSecurity.addFilter(tokenFilter);
return tokenFilter;
}
}
Are you interested in all of Spring Security ignoring the URLs or do you only want that specific filter to ignore the request? If you want all of Spring Security to ignore the request it can be done using the following:
#Configuration
#EnableWebSecurity
#Import(MyAppConfig.class)
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private MyTokenUserInfoCache userInfoCache;
#Autowired
private ServerStatusService serverStatusService;
#Override
public void configure(WebSecurity webSecurity) throws Exception
{
webSecurity
.ignoring()
// All of Spring Security will ignore the requests
.antMatchers("/resources/**")
.antMatchers(HttpMethod.POST, "/login");
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.addFilter(tokenInfoTokenFilterSecurityInterceptor())
.authorizeRequests()
// this will grant access to GET /login too do you really want that?
.antMatchers("/login").permitAll()
.and()
.httpBasic().and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Bean
public TokenFilterSecurityInterceptor<TokenInfo> tokenInfoTokenFilterSecurityInterceptor() throws Exception
{
TokenService<TokenInfo> tokenService = new TokenServiceImpl(userInfoCache);
return new TokenFilterSecurityInterceptor<TokenInfo>(tokenService, serverStatusService, "RUN_ROLE");
}
}
If you want to have only that specific Filter ignore particular requests you can do something like this:
#Configuration
#EnableWebSecurity
#Import(MyAppConfig.class)
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private MyTokenUserInfoCache userInfoCache;
#Autowired
private ServerStatusService serverStatusService;
#Override
public void configure(WebSecurity webSecurity) throws Exception
{
webSecurity
.ignoring()
// ... whatever is here is ignored by All of Spring Security
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.addFilter(tokenInfoTokenFilterSecurityInterceptor())
.authorizeRequests()
// this will grant access to GET /login too do you really want that?
.antMatchers("/login").permitAll()
.and()
.httpBasic().and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Bean
public TokenFilterSecurityInterceptor<TokenInfo> tokenInfoTokenFilterSecurityInterceptor() throws Exception
{
TokenService<TokenInfo> tokenService = new TokenServiceImpl(userInfoCache);
TokenFilterSecurityInterceptor tokenFilter new TokenFilterSecurityInterceptor<TokenInfo>(tokenService, serverStatusService, "RUN_ROLE");
RequestMatcher resourcesMatcher = new AntPathRequestMatcher("/resources/**");
RequestMatcher posLoginMatcher = new AntPathRequestMatcher("/login", "POST");
RequestMatcher ignored = new OrRequestMatcher(resourcesMatcher, postLoginMatcher);
return new DelegateRequestMatchingFilter(ignored, tokenService);
}
}
public class DelegateRequestMatchingFilter implements Filter {
private Filter delegate;
private RequestMatcher ignoredRequests;
public DelegateRequestMatchingFilter(RequestMatcher matcher, Filter delegate) {
this.ignoredRequests = matcher;
this.delegate = delegate;
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) {
HttpServletRequest request = (HttpServletRequest) req;
if(ignoredRequests.matches(request)) {
chain.doFilter(req,resp,chain);
} else {
delegate.doFilter(req,resp,chain);
}
}
}
1 In xml configuration of spring-security I use
<http pattern="/resources/**" security="none"/>
<http use-expressions="true">
<intercept-url pattern="/login" access="permitAll"/>
</http>
to retrieve it from security check.
2 After that add mvc:resource tag in your spring configuration
<mvc:resources mapping="/resource/**" location="/resource/"/>
Important: this config will only work if url is handled by dispatcher servlet. This means that in web.xml you must have
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>