AuthenticationSuccessHandler in Spring Security - java

I've used spring security in a Spring Boot application and there are 2 types of users: one is an ADMIN, and one just a simple user. I get the data from a DataSource, then I execute an SQL query.
My problem is with a redirection: for every user I have a different homepage. I'm trying to use to AthenticationSuccessHandler, but it won't work.
Please help.
My Spring security class configuration :
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import javax.sql.DataSource;
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
Securityhandler successHandler;
// Pour l'authentification des Utilisateur de Table Utilisateur
#Autowired
public void GlobalConfig(AuthenticationManagerBuilder auth,DataSource dataSource) throws Exception {
auth.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery("SELECT \"Pseudo\" AS principal , \"Password\" AS credentials , true FROM \"UTILISATEUR\" WHERE \"Pseudo\" = ? ")
.authoritiesByUsernameQuery("SELECT u.\"Pseudo\" AS principal , r.role as role FROM \"UTILISATEUR\" u ,\"Role\" r where u.id_role=r.id_role AND \"Pseudo\" = ? ")
.rolePrefix("_ROLE");
}
// ne pas appliqué la securité sur les ressources
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/bootstrap/**","/css/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.successHandler(successHandler);
}
}
And this is my AuthenticationSuccessHandler:
import java.io.IOException;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
public class Securityhandler implements AuthenticationSuccessHandler {
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
Set<String> roles = AuthorityUtils.authorityListToSet(authentication.getAuthorities());
if (roles.contains("ROLE_Admin")) {
response.sendRedirect("/admin/home.html");
}
}
}
And this is the error in the console:
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name
'org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration':
Injection of autowired dependencies failed;

import java.io.IOException;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
#Component
public class Securityhandler implements AuthenticationSuccessHandler {
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
Set<String> roles = AuthorityUtils.authorityListToSet(authentication.getAuthorities());
if (roles.contains("ROLE_ADMIN")) {
response.sendRedirect("admin/home.html");
}
}
}
You've missed the #Component annotation in your success handler class.

Rather than sublcassing AuthenticationSuccessHandler,
It's worth knowing about the Spring security role-checking config:
#Configuration
#EnableWebSecurity
public class SecSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN");
}
...
}
OR pre-checking a Role per endpoint:
#Autowired
#PreAuthorize("hasRole('ADMIN')")
#RequestMapping("/")
public ModelAndView home(HttpServletRequest request) throws Exception {
}
where the default Role prefix is ROLE_
https://docs.spring.io/spring-security/site/docs/3.0.x/reference/el-access.html
https://www.baeldung.com/spring-security-expressions-basic

Related

Spring Boot Security "Authentication Manager" doesn't receive username parameter

public AuthenticationResponse authenticate(AuthenticationRequest request){
authManager.authenticate(
new UsernamePasswordAuthenticationToken(
request.getUsername(),
request.getPassword()
)
);
return null; // returns other stuff, irrelevant
}
The result I get in the console is:
Hibernate: select u1_0.id,u1_0.email,u1_0.password,u1_0.role,u1_0.username from user u1_0 where u1_0.username=?
If I print what is inside authManager.authenticate(...) I get:
UsernamePasswordAuthenticationToken [Principal=test, Credentials=[PROTECTED], Authenticated=false, Details=null, Granted Authorities=[]]
Principal=USERNAME, so the username is lost when authManager.authenticate() executes, and I don't understand the problem.
This is the service
package com.XX.XX.security.service;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import com.XX.XX.security.auth.AuthenticationRequest;
#Service
public class AuthenticationService {
private final AuthenticationManager authManager;
public AuthenticationService(AuthenticationManager authManager) {
this.authManager = authManager;
}
public AuthenticationResponse authenticate(AuthenticationRequest request){
authManager.authenticate(new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword()));
return null;
}
}
Also this is the only other class where AuthenticationManager is mentioned
package com.XX.XX.security.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
#Configuration
public class ApplicationConfig {
#Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception{
return authConfig.getAuthenticationManager();
}
}
SecurityConfig:
package com.XX.XX.security.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
#Configuration
#EnableWebSecurity
public class SecurityConfig {
private final JwtAuthenticationFilter jwtAuthFilter;
private final AuthenticationProvider authenticationProvider;
public SecurityConfig(JwtAuthenticationFilter jwtAuthFilter, AuthenticationProvider authenticationProvider) {
this.jwtAuthFilter = jwtAuthFilter;
this.authenticationProvider = authenticationProvider;
}
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception{
httpSecurity
.csrf().disable()
.authorizeHttpRequests()
.requestMatchers("/api/v1/auth/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authenticationProvider(authenticationProvider)
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
return httpSecurity.build();
}
}
I can add more information from any class if needed

type org.springframework.security.authentication.encoding.PasswordEncoder cannot be resolved. It is indirectly referenced from required .class files

Hi there so I'm trying to create a Spring Security Login page which roots back to my DB(sql server) where my credentials are stored.
import java.util.List;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
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;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
//Some imports like JDBC template and beanrowmapper are present but dont mind them.. as i was trying somethings out..
#Configuration
#EnableWebSecurity
public class SecurityConfigWithDB extends WebSecurityConfigurerAdapter{
#Autowired
private DataSource dataSource;
#Bean
public PasswordEncoder passwordEncoder1() {
return new BCryptPasswordEncoder();
}
public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth
.jdbcAuthentication()
.passwordEncoder(new BCryptPasswordEncoder()) // this is where i get the error//
.dataSource(dataSource)
.usersByUsernameQuery("Select UserName, Password, Enable FROM LoginDetails WHERE Username=?")
.authoritiesByUsernameQuery("Select UserName, Role FROM LoginDetails WHERE Username=?");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().permitAll()
.and()
.logout().permitAll();
}
}
And the error goes like :
The type org.springframework.security.authentication.encoding.PasswordEncoder cannot be resolved. It is indirectly referenced from required .class files

The method withDefaults() is undefined for the type SecurityConfiguration [duplicate]

This question already has answers here:
Why is my method undefined for the type object?
(4 answers)
Closed 2 years ago.
I'm building a spring application with spring security. I have added basic authentication of a username and password in the code. I want to access APIs via a reactjs web app with this basic authentication. For this, I'm trying to add the cors policy.
In the security configuration of spring security, I'm getting the error:
The method withDefaults() is undefined for the type SecurityConfiguration
I have got all the necessary dependencies installed.
What can be the possible solution?
Attaching my code below.
package com.example.demo;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.Customizer;
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;
import org.springframework.security.config.annotation.web.configurers.CorsConfigurer;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors(withDefaults()) //getting the error here
.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/login").authenticated()
.antMatchers(HttpMethod.OPTIONS).permitAll()
.antMatchers(HttpMethod.GET).authenticated()
.anyRequest().authenticated()
.and()
.httpBasic();
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Collections.singletonList("http://localhost:3000"));
configuration.setAllowedHeaders(List.of("*"));
configuration.setAllowedMethods(Arrays.asList("GET","POST", "OPTIONS"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
#Bean
public PasswordEncoder getPasswordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
}
Your SecurityConfiguration, nor the WebSecurityConfigurerAdapter have a method withDefaults(). You need to add a static import:
import static org.springframework.security.config.Customizer.withDefaults;

Testing spring-boot security, Thymeleaf error

I'm using Thymeleaf with Spring-boot and I get a problem testing the security #withmockuser.
This is the code for testing the controller
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
#SuppressWarnings("SpringJavaAutowiringInspection")
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = SecurityConfiguration.class)
#WebMvcTest(IndexController.class)
public class IndexControllerTest {
#Autowired
private MockMvc mvc;
#MockBean
private IndexController IndexController;
#Test
#WithMockUser
public void testAuthenticated() throws Exception {
this.mvc.perform(get("/"))
.andExpect(status().is(200));
}
}
And this is the controller
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
#Controller
public class IndexController {
private DemoshopService demoshopService;
#Autowired
public void setDemoshopService(DemoshopService demoshopService) {
this.demoshopService = demoshopService;
}
#RequestMapping(value = "/", method = RequestMethod.GET)
public String list(Model model) {
model.addAttribute("demoshops", demoshopService.listAllDemoshops());
return "index";
}
}
It will give me the following error
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateProcessingException: Exception processing template ()
Does this mean Thymeleaf reads "/" as an template?
Without #withMockUser it does what I'm expecting.
Thank you for the help.
EDIT:
as requested the security configuration:
import org.springframework.beans.factory.annotation.Autowired;
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;
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/css/**").permitAll()
.antMatchers("/images/**").permitAll()
.antMatchers("/webjars/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("test1").password("password").roles("USER")
.and()
.withUser("test2").password("password").roles("USER")
.and()
.withUser("test3").password("password").roles("USER")
.and()
.withUser("test4").password("password").roles("USER")
.and()
.withUser("test5").password("password").roles("USER")
.and()
.withUser("test6").password("password").roles("USER");
}
}
The usernames are normally e-mail address but I changed them for now.
Without parameters #WithMockUser will run test with the username "user", the password "password", and the roles "ROLE_USER".
Use #WithMockUser(username="test1",roles={"USER"}) to run test for test1 user for example.

Allow access to one URL with username/password and others from IP address

I'm trying to figure out how to do the following with Spring Security:
I need to allow outside access on a certain endpoint, at /webhooks/, but protect it with a HTTP basic username/password. On all other endpoints, access must be restricted, except from certain subnets.
Here's what I have thus far. It's not working, as everything gets denied.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
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;
/**
* Created on 27 July 2016 # 1:49 PM
* Component for project "security"
*/
#Configuration
#EnableWebSecurity
#PropertySource("classpath:/test.properties")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Value("${test.webhooks.username}")
private String username;
#Value("${test.webhooks.password}")
private String password;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/webhooks/").authenticated()
.and().authorizeRequests()
.antMatchers("/**").hasIpAddress("10.0.0.0/8")
.antMatchers("/**").hasIpAddress("172.16.0.0/16")
.antMatchers("/**").hasIpAddress("192.168.1.0/24")
.antMatchers("/**").hasIpAddress("172.0.0.0/8")
.antMatchers("/**").denyAll()
;
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder
.inMemoryAuthentication()
.withUser(username).password(password).roles("WEBHOOKS_ACCESS")
;
}
}
Any help would be awesome! I'm not sure if the chained ant matchers are correct in any event.
OK, I found out how to do this. Not sure if this is the "spring way" or whatever, but it seems to work. Any suggestions are welcome.
So my new class looks as follows:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.annotation.Order;
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;
/**
* Created on 27 July 2016 # 1:49 PM
* Component for project "security"
*
*/
#Configuration
#EnableWebSecurity
#PropertySource("classpath:/security.properties")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Value("${security.webhooks.username}")
private String username;
#Value("${security.webhooks.password}")
private String password;
#Configuration
#Order(1)
public static class WebHookSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/webhooks/")
.authorizeRequests()
.anyRequest().hasRole("WEBHOOKS_ACCESS")
.and()
.httpBasic()
.and()
.csrf().disable();
}
}
#Configuration
#Order(2)
public static class InternalSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**")
.authorizeRequests()
.anyRequest()
.access("hasIpAddress('10.0.0.0/8') or hasIpAddress('172.16.0.0/16') or hasIpAddress('192.168.1.0/24') or hasIpAddress('172.0.0.0/8') or hasIpAddress('127.0.0.1')")
;
}
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder
.inMemoryAuthentication()
.withUser(username).password(password).roles("WEBHOOKS_ACCESS")
;
}
}
Which I derived from this documentation. Hope this helps someone!

Categories