I had updated spring security from 3x to 4.0.1.RELEASE. Then I finally had the change to fully remove old XML, and replace it with pure java config. But my security isn't working properly.
Problem:
my default login page does not authorize, under POST /login.htm I have 404.
my main app can run as unauthorized
because I have 404 on login POST, I am not entering UserDetailsService
all beans are provided into this configuration, I have no context start problems
My configuration file:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
#ComponentScan(basePackages = {"com.dal.dao.security", "com.services.security"})
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
LocalUserDao userDao;
#Autowired
UserRoleMapper roleMapper;
#Autowired
StandardPasswordEncoder passwordEncoder;
private UserDetailsService methodSecurityService() throws Exception {
return new UserDetailsServiceImpl(userDao, roleMapper);
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(methodSecurityService()).passwordEncoder(passwordEncoder);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/*")
.authenticated()
.and()
.formLogin()
.loginPage("/login.htm").failureUrl("/login.htm?error")
.usernameParameter("username")
.passwordParameter("password")
.and().logout().logoutSuccessUrl("/index.htm")
.and().csrf()
.and().exceptionHandling().accessDeniedPage("/403");
}
}
Could anyone help me with this? I have already watched few no-xml configuration, but they don't seem to be working on my example.
Source of my code can be found here.
I think the fix is simple (i hope) you didn't advise that anyone can access your login form:
http.authorizeRequests()
.antMatchers("/*")
.authenticated()
.and()
.formLogin()
.loginPage("/login.htm").failureUrl("/login.htm?error")
.usernameParameter("username")
.passwordParameter("password")
.permitAll() // ADD THIS LINE
.and().logout().logoutSuccessUrl("/index.htm")
.and().csrf()
.and().exceptionHandling().accessDeniedPage("/403");
Essentially you are redirecting users to login page without enabling unauthenticated access to it. Basically you are asking them to authenticate to view the authentication form :).
From Spring Security:
Granting access to the formLogin() URLs is not done by default since
Spring Security needs to make certain assumptions about what is
allowed and what is not. To be secure, it is best to ensure granting
access to resources is explicit.
Related
My question is, why does spring show me the login page first even though I specified that all pages should be denied in my spring security settings? It is true that authentication is done first and then authorization, but in this particular case, there is no need to identify the user, and no one is supposed to be allowed access to the entire web application.
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest()
.denyAll()
.and()
.formLogin()
.and()
.httpBasic();
}
}
I have a very basic Spring Security setup using Session. My problem is that I can't find a way to use any kind of Session Listener (both Spring and Servlet API versions) to listen to SessionCreated event. Login is working and session is being created properly.
The reason I need a listener is because I want to initialize certain session attributes (ex. shopping kart, recent items list) so I can access them seamlessly from #Controller request mappings, without having to worry whether session attributes are initialized.
Security configuration code:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
DataSource dataSource;
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
.and()
.authorizeRequests()
.antMatchers("/secured/**").authenticated()
.anyRequest().permitAll()
.and()
.formLogin()
.loginPage("/login")
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/")
.and()
.rememberMe().key("unique");
}
...
}
First, I have tried the most basic session listenter:
#Component
public class InitHttpSessionListener implements HttpSessionListener {
public void sessionCreated(HttpSessionEvent event) {
...
}
}
I have also tried answers from here, which also didn't work
As what was getting clear from your comments is that you are using Spring Session JDBC. Due to the nature of JDBC this doesn't support publishing of session events and thus you cannot listen to those events.
As a workaround you could create your own AuthenticationSuccessHandler and put the logic for filling the Session in there. Or listen to an AuthenticationSuccessEvent using a Spring event listener (would be a bit harder to get to the session but doable).
The goal is to manipulate the roles of a user(or simply create a new Authentication object since I'v got a hunch they cant be manipulated directly) before its stored in the context. After a lot of digging I found that the authentication is perfomred in the OAuth2ClientAuthenticationProcessingFilter.attemptAuthentication and later stored using a sessionStrategy.
The part where I am stuck at is telling spring to use/override that filter.
So far I have managed to create a CustomOAuth2ClientAuthenticationProcessingFilter extending the original one.
I tried to instantiate the filter and add it to the filter chane like so.
#Configuration
#EnableOAuth2Sso
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/login**", "/callback/**", "/resources/**", "/static/**", "/webjars/**", "/webjar/**", "/error**")
.permitAll()
.and()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.csrf().disable()
.logout()
.permitAll()
.logoutSuccessUrl("/")
.and().addFilterAt(createCustomOAuth2ClientAuthenticationProcessingFilter(), AbstractPreAuthenticatedProcessingFilter.class)
;
}
private CustomOAuth2ClientAuthenticationProcessingFilter createCustomOAuth2ClientAuthenticationProcessingFilter(){
OAuth2SsoProperties sso = (OAuth2SsoProperties)this.getApplicationContext().getBean(OAuth2SsoProperties.class);
OAuth2RestOperations restTemplate = ((UserInfoRestTemplateFactory)this.getApplicationContext().getBean(UserInfoRestTemplateFactory.class)).getUserInfoRestTemplate();
ResourceServerTokenServices tokenServices = (ResourceServerTokenServices)this.getApplicationContext().getBean(ResourceServerTokenServices.class);
CustomOAuth2ClientAuthenticationProcessingFilter customFilter=new CustomOAuth2ClientAuthenticationProcessingFilter(sso.getLoginPath());
customFilter.setRestTemplate(restTemplate);
customFilter.setTokenServices(tokenServices);
customFilter.setApplicationEventPublisher(this.getApplicationContext());
return customFilter;
}
but that, as expected, just calls both filters- mine and the default one.
Is there a way to "replace" the original filter functionality without creating serious issues and save the ability to later use the #Secured anotation for example, in order to secure application endpoints ?
I'v read about using postProcessor in the configuration but never found an extensive well expleined answer.I'v also read about custom configuration classes but not for OAuth2 . Any suggestions are welcome.
This is my spring security configuration class:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
DataSource dataSource;
#Autowired
public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery(
"select nickname,password, true from client where nickname=?")
.authoritiesByUsernameQuery(
"select username, role from user_roles where username=?");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/*")
.access("hasRole('ROLE_USER')");
http.formLogin()
.defaultSuccessUrl("/", true)
.loginPage("/login");
}
}
When I am trying to open my webpage I get this error:
The page isn’t redirecting properly. Firefox has detected that the
server is redirecting the request for this address in a way that will
never complete. This problem can sometimes be caused by disabling or
refusing to accept cookies.
When I delete configure method everything works fine.
Can anyone tell me how can I solve this ?
After
.loginPage("/login")
you should add
.permitAll();
Doing the above should fix your issues. As for why that was happening, it's because your loginPage requires the user to be authenticated, which causes Spring to redirect the user to the loginPage, and it loops from there.
Firefox is just nice enough to stop the request when it detects that behavior.
I also suggest that you use .anyRequest() instead of .antMatchers("/*").
The final result should look like this
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest()
.access("hasRole('ROLE_USER')")
.and()
.formLogin()
.defaultSuccessUrl("/", true)
.loginPage("/login")
.permitAll();
}
There is a configuration problem.
You have a pattern for intercept-url "/*" and ROLE_USER' what means that if a user is not authorized - it will be redirected to login page.
Application Context resolves login page and finds out that /login page matches "/*" pattern, that should be intercepted and authenticated for ROLE_USER. A user without authentication, evidently, doesn't have ROLE_USER and redirected to /login page and all over again.
Allowing unathorized users acces the login page should do the trick:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login")
.permitAll()
.antMatchers("/*")
.access("hasRole('ROLE_USER')");
http.formLogin()
.defaultSuccessUrl("/", true)
.loginPage("/login");
}
Pay attention to the order. More specific filters should be written first, or they will be 'shadowed' by wider filters and ignored.
permitAll() could be applied to login page directly omitting first matcher, as was already proposed:
.loginPage("/login").permitAll();
I am new to Java and I trying to write first web application with Spring framework (I have some experience with django/python and sinatra/ruby).
I need to implement user authentication system and have no idea what is the best way to do this correctly. Should I use Spring Security for this? Or there is some another ways?
Since you starting a new application, I strongly urge to use Spring Boot. It will take away a lot of your initial pain in configuring the various aspects of Spring.
As for security, you should go with Spring Security which of course is easy to configure with Spring Boot.
Following this guide, a simple Spring Security configuration which uses in memory authentication would look like:
#Configuration
#EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.anyRequest().authenticated();
http
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
}
As you can see, the first method is used to configure which parts of the application are to be secured and how, while the second method configures how the users will be authenticated.
One common way to provide a customized way to authenticate users, is to provide a custom UserDetailsService implementation as is done here
A very easy way to configure authentication when you already have configured a DataSource and the user credentials are stored there, is the following:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private DataSource dataSource;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.anyRequest().authenticated();
http
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
#Override
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth
.jdbcAuthentication()
.dataSource(dataSource)
.withUser("user").password("password").roles("USER").and()
.withUser("admin").password("password").roles("USER", "ADMIN").and()
.withDefaultSchema();
}
}
Here is the complete reference of Spring Security. Also here is security part of the Spring Boot documentation. You can find a multitude of sample applications using various aspects of Spring Security here
Initially it may seem that Spring Security is complicated, but once you get the hang of it, you'll appreciate it's extended feature set and customizability.
One final note, when to comes to things like security which are common in so many applications, there is no need to reinvent the wheel! Go with Spring Security (or perhaps Apache Shiro)