Getting redirected after login but I am not actually logged in Spring Security - spring-boot

Update: I tried implemting a CSRF token, and now when I try to log in, I always get redirected to the default Spring login page localhost:8080/login, and when I log in there again, it again redirects me to the login successful url....
This is my WebSecurityConfig
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
AccountDetailsService accountDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(accountDetailsService).passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception{
http
.authorizeRequests()
.antMatchers("/api/me").permitAll()
.antMatchers( HttpMethod.POST,"/api/addthing", "/api/addotherthing").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.defaultSuccessUrl("http://192.168.1.105:3000/api/adminpage", true)
.and()
.logout()
.logoutSuccessUrl("http://192.168.1.105:3000/")
.logoutUrl("/logout")
.deleteCookies("JSESSIONID");
http.csrf().disable();
}
#Bean
public BCryptPasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
Here's my AccountDetailsService
#Service("userDetailsService")
#Transactional
public class AccountDetailsService implements UserDetailsService {
#Autowired
AccountService accountService;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Account account = accountService.findAccountByUsername(username);
return new org.springframework.security.core.userdetails.User(
account.getUsername(), account.getPassword(),
true, true, true, true,
accountService.getAuthorities(account)
);
}
}
Also the AccountSrvice:
#Service
public class AccountService {
#Autowired
AccountRepository accountRepository;
#Autowired
PasswordEncoder passwordEncoder;
public Account findAccountByUsername(String username){
return accountRepository.findFirstByUsername(username);
}
public List<GrantedAuthority> getAuthorities(Account account){
List<GrantedAuthority> roles = new ArrayList<>();
String role = account.getRole();
roles.add(new SimpleGrantedAuthority(role));
return roles;
}
public void createNewAccount(String username, String password, String role) {
Account account = new Account(username, passwordEncoder.encode(password), role);
accountRepository.save(account);
}
}
The problem that I am having is, after logging in, I get redirected to the /api/adminpage/ site, which means that the login is successful, right? But when I try getting my Principal with this method:
#CrossOrigin
#GetMapping("/api/me")
public Principal getMe(Principal principal){
return principal;
}
It just gives me an empty response, which means that I am not logged in.. Also when I try making a POST request to some of the urls in antMatches I can't... Can someone explain what am I doing wrong here?

Well, my whole day is now gone on this stupid little thing, but I got it working, and I'm not even happy about it anymore
The fix was to add "proxy": "http://192.168.1.105:8080" in my package.json in the React app.

Related

Configuring both http basic and Form based authentication in latest Spring security 6.0

I'm trying to configure REST and Form based authentication in new Spring boot and spring secuirty latest. I went through following post Combining basic authentication and form login for the same REST Api and new spring configuration using SecurityFilterChain and created the following. As I learnt that WebSecurityConfigurerAdapter method in the above post is deprecated.
#Configuration
#Order(2)
#EnableWebSecurity
public class RESTBasedConfigurationAdapter {
#Autowired
private AuthenticationProvider authenticationProvider;
#Bean
public BCryptPasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
#Bean
public SecurityFilterChain configure(HttpSecurity http) throws Exception {
http.antMatcher("/api/**")
.authorizeRequests().anyRequest().hasAnyRole(...)
.and().httpBasic()
.authenticationEntryPoint(authenticationEntryPoint());
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
return http.build();
}
}
and
#Configuration
#Order(Ordered.HIGHEST_PRECEDENCE)
#EnableWebSecurity
public class FormBasedConfigurationAdapter {
#Autowired
private AuthenticationProvider authenticationProvider;
#Autowired
private AuthenticationSuccessHandler authenticationSuccessHandler;
#Bean
public SecurityFilterChain configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers(...)
.permitAll().anyRequest().authenticated()
.and().formLogin().loginPage("/login").successHandler(authenticationSuccessHandler)
.permitAll().and().logout().permitAll();
return http.build();
}
}
But the configure method FormBasedConfigurationAdapter's is never called. Please explain how to configure so that both http Basic and Form based Authentication can be done.
As far I under I want two flows.
One flow REST which uses the following AuthenticationProvider for logging STATELESS
#Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Autowired
private UserDetailsService userDetailsService;
#Autowired
#Lazy
private BCryptPasswordEncoder passwordEncoder;
#Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
String userName = authentication.getName();
String password = authentication.getCredentials().toString();
UserDetails userDetails = userDetailsService.loadUserByUsername(userName);
if (passwordEncoder.matches(password, userDetails.getPassword())) {
return new UsernamePasswordAuthenticationToken(userName, password, userDetails.getAuthorities());
} else {
throw new BadCredentialsException(" Bad Credentials ");
}
}
#Override
public boolean supports(Class<?> authenticationType) {
return authenticationType.equals(UsernamePasswordAuthenticationToken.class);
}
}
and for other FormBased authentication, I would like to go through controller as below.
#PostMapping("/login")
public String login(#ModelAttribute("loginForm") LoginForm loginForm,
BindingResult bindingResult,
Model model) {
loginValidator.validate(loginForm, bindingResult);
securityService.login(loginForm.getUserName(), loginForm.getPasswd());
if (bindingResult.hasErrors()) {
return "login";
}
return "...";
}
to facilitate Validation for Authenticate using Service as below.
#Override
public void login(String username, String password) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
new UsernamePasswordAuthenticationToken(userDetails, password, userDetails.getAuthorities());
if (passwordEncoder.matches(password, userDetails.getPassword())) {
if (usernamePasswordAuthenticationToken.isAuthenticated()) {
SecurityContextHolder.getContext()
.setAuthentication(usernamePasswordAuthenticationToken);
logger.debug(String.format("Auto login %s successfully!", username));
}
} else {
throw new BadCredentialsException(" Bad Credentials ");
}
}
Please explain how to achieve this. I also tried doing both HttpSecurity mapping in the same class but it is not working due to various reasons.

Why can't I go to admin REST API request mapping after I have logged in - Spring Boot

I have some issues with my REST API, created from Spring Boot and Spring Security.
First I have created my Spring security configuration. As you see here, I have two paths with two different authorizations - USER and ADMIN.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter{
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private UserRepository userRepository;
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// Set the default standard admin and password as admin, if not exist
User user = userRepository.findByUsername("admin");
if(user == null) {
user = new User();
user.setUserID(0);
user.setUsername("admin");
user.setPassword(passwordEncoder().encode("admin"));
Set<Role> roles = new HashSet<Role>();
Role role = new Role();
role.setRoleID(0);
role.setRolename("ADMIN");
roles.add(role);
user.setRoles(roles);
userRepository.save(user);
}
// Connect our database to spring and also with a password encoder
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests().antMatchers("/user/**").authenticated().anyRequest().hasAnyAuthority("ROLE_USER");
http.authorizeRequests().antMatchers("/admin/**").authenticated().anyRequest().hasAnyAuthority("ROLE_ADMIN");
http.httpBasic();
http.formLogin().permitAll();
}
}
And this is my controllers both user and admin.
#RestController
#RequestMapping("/admin")
public class AdminController {
#Autowired
private UserRepository userRepository;
#Autowired
private BCryptPasswordEncoder passwordEncoder;
#PostMapping("/addUser")
public String addUser(#RequestBody User user) {
user.setPassword(passwordEncoder.encode(user.getPassword()));
userRepository.save(user);
return "Added user by admin";
}
#GetMapping("/adminHello")
public String adminHello() {
return "Admin say hello";
}
}
#RestController
#RequestMapping("/user")
public class UserController {
#GetMapping("/userHello")
public String userHello() {
return "processing..";
}
}
If I try to login to http://localhost:8080/login and write in my password and my username. Then I will be able to go in. Fine!
But these are the problems.
If I enter http://localhost:8080/user/userHello with a Admin account, I still get "processing..."
If I enter http://localhost:8080/admin/adminHello with a Admin account, I get
"Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Fri Sep 13 00:23:42 CEST 2019
There was an unexpected error (type=Forbidden, status=403).
Forbidden"
Why? Have I forgot something? My Accound have the ADMIN role in the database. Very clear.
Clearly your Role's are not working there is some issue with configuration.
Try this
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/user/**").hasRole("USER")
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
.formLogin().permitAll();
}

successForwardUrl does not work with Spring Social after authenticating successfully

I'm working on a Spring Boot project integrating with Spring Social. After authenticating with google successfully, I want to redirect to the end point /userInfo, but it seems to redirect to the previous page where I make a request to authenticate to Google: http://localhost:8080/auth/google
I've also tried to create a bean CustomAuthenticationSuccessHandler, which implements the AuthenticationSuccessHandler and add that to the configuration file, but it didn't work either
My WebSecurityConfiguration:
#Configuration
#EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Autowired
DataSource dataSource;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
// This bean is load the user specific data when form login is used.
#Override
public UserDetailsService userDetailsService() {
return userDetailsService;
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
// Enable jdbc authentication
#Autowired
public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication()
.dataSource(dataSource)
.passwordEncoder(passwordEncoder())
.usersByUsernameQuery("select user_name, encryted_password"
+ " from app_user where user_name=?");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
// Pages do not require login
http.authorizeRequests()
.antMatchers("/", "/signup", "/login", "/logout")
.permitAll();
http.authorizeRequests()
.antMatchers("/user/**")
.access("hasRole('" + AppRole.ROLE_USER + "')");
// For ADMIN only.
http.authorizeRequests()
.antMatchers("/admin/**")
.access("hasRole('" + AppRole.ROLE_ADMIN + "')");
// When the user has logged in as XX.
// But access a page that requires role YY,
// AccessDeniedException will be thrown.
http.authorizeRequests()
.and()
.exceptionHandling()
.accessDeniedPage("/403");
// Form Login config
http.authorizeRequests()
.and()
.formLogin()
.loginProcessingUrl("/j_spring_security_check") // the url to submit the username and password to
.loginPage("/login") // the custom login page
.successForwardUrl("/userInfo") // the landing page after a successful login
.failureUrl("/login?error=true") // the landing page after an unsuccessful login
.usernameParameter("username")
.passwordParameter("password");
// Logout Config
http.authorizeRequests()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/logoutSuccessful");
http.apply(new SpringSocialConfigurer())
.signupUrl("/signup");
}
}
MyController:
#RestController
#Transactional
public class MainController {
#Autowired
private AppUserDAO appUserDAO;
#Autowired
private ConnectionFactoryLocator connectionFactoryLocator;
#Autowired
private UsersConnectionRepository userConnectionRepository;
#Autowired
private AppUserValidator appUserValidator;
#RequestMapping(value = {"/", "/welcome"}, method = RequestMethod.GET)
public String welcomePage(Model model) {
model.addAttribute("title", "Welcome");
model.addAttribute("message", "This is welcome page!");
return "welcomePage";
}
#RequestMapping(value = "/logoutSuccessful", method = RequestMethod.GET)
public String logoutSuccessfulPage(Model model) {
model.addAttribute("title", "Logout");
return "logoutSuccessfulPage";
}
#RequestMapping(value = "/userInfo", method = RequestMethod.GET)
public String userInfo(Model model, Principal principal) {
// After user login successfully.
String userName = principal.getName();
System.out.println("User Name: " + userName);
UserDetails loginedUser = (UserDetails) ((Authentication) principal).getPrincipal();
String userInfo = WebUtils.toString(loginedUser);
model.addAttribute("userInfo", userInfo);
return "userInfoPage";
}
Are there any ways to forward to /userInfo url after logging in with Spring Social ?
Try in POST "/userInfo" return "redirect:/userInfoPage".

Spring boot and jwt Encoded password does not look like BCrypt

I know there are several questions about this, but none of them helped, everything seemed ok to me... I hope I'm wrong about this.
I've followed this tutorial to create authentication with JWT for my Spring Boot application, but I'm stuck with this warning
o.s.s.c.bcrypt.BCryptPasswordEncoder : Encoded password does not look like BCrypt
This is the class that extends WebSecurityConfigurerAdapter:
#EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
private UserDetailsService userDetailsService;
private BCryptPasswordEncoder bCryptPasswordEncoder;
public WebSecurity(UserDetailsService userDetailsService, BCryptPasswordEncoder bCryptPasswordEncoder) {
this.userDetailsService = userDetailsService;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.POST, SIGN_UP_URL).permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()));
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
}
And this is the class that implements UserDetailsService:
public class UserDetailsServiceImpl implements UserDetailsService {
private ApplicationUserRepository applicationUserRepository;
public UserDetailsServiceImpl(ApplicationUserRepository applicationUserRepository) {
this.applicationUserRepository = applicationUserRepository;
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.out.println("loadUserByUsername");
ApplicationUser applicationUser = applicationUserRepository.findByUsername(username);
if (applicationUser == null) {
System.out.println("User non trovato");
throw new UsernameNotFoundException(username);
}
System.out.println("Username: " + applicationUser.getUsername());
return new User(applicationUser.getUsername(), applicationUser.getPassword(), emptyList());
}
}
Also, I'm wondering where the class WebSecurity is istantiated with the correct parameters passed. Is this managed automatically by Spring?
Found the problem, it was that my class UserDetailsServiceImpl did not have the "#Service" annotation. Now it's working fine.

Spring-boot Spring Security; Users without the correct roles are still accessing role-specific pages

I am attempting to make a web page that is only accessible by the 'Admin' role, however all users are able to access it. I have User and Role entities with a functioning ManyToMany relationship set up.
Here is my SecurityConfig.java:
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserService userService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(
"/registration",
"/js/**",
"/css/**",
"/img/**",
"/webjars/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.authorizeRequests()
.antMatchers("/competition/**").hasRole("Admin")
.and()
.logout()
.invalidateHttpSession(true)
.clearAuthentication(true)
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/login?logout")
.permitAll();
}
#Bean
public BCryptPasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
#Bean
public DaoAuthenticationProvider authenticationProvider(){
DaoAuthenticationProvider auth = new DaoAuthenticationProvider();
auth.setUserDetailsService(userService);
auth.setPasswordEncoder(passwordEncoder());
return auth;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
}
as you can see with the line
.antMatchers("/competition/**").hasRole("Admin")
I'm trying to make link /competition/** admin-only.
Here is the controller:
#Controller
public class CompetitionController {
#Autowired
CompetitionRepository competitionRepository;
#GetMapping("/competition/{competitors}")
public String match(ModelMap map, #PathVariable(value = "competitors") String competitors, Principal principal) {
String[] parts = competitors.split("-");
String part1 = parts[0];
String part2 = parts[1];
map.addAttribute("part1", part1);
map.addAttribute("part2", part2);
return "match";
}
}
Finally here is my UserService:
#Service
public class UserServiceImpl implements UserService {
#Autowired
private UserRepository userRepository;
#Autowired
private BCryptPasswordEncoder passwordEncoder;
#Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
User user = userRepository.findByEmail(email);
if (user == null){
throw new UsernameNotFoundException("Invalid username or password.");
}
return new org.springframework.security.core.userdetails.User(user.getEmail(),
user.getPassword(),
mapRolesToAuthorities(user.getRoles()));
}
public User findByEmail(String email){
return userRepository.findByEmail(email);
}
public User save(UserRegistrationDto registration){
User user = new User();
user.setFirstName(registration.getFirstName());
user.setLastName(registration.getLastName());
user.setEmail(registration.getEmail());
user.setPassword(passwordEncoder.encode(registration.getPassword()));
user.setRoles(Arrays.asList(new Role("ROLE_USER")));
User userAdmin = userRepository.findByEmail("admin#email.com");
if (userAdmin == null){
userAdmin = new User();
userAdmin.setFirstName("Admin");
userAdmin.setLastName("");
userAdmin.setEmail("admin#email.com");
userAdmin.setPassword(passwordEncoder.encode("admin"));
userAdmin.setRoles(Arrays.asList(new Role("ROLE_Admin")));
userRepository.save(userAdmin);
}
return userRepository.save(user);
}
private Collection<? extends GrantedAuthority> mapRolesToAuthorities(Collection<Role> roles){
return roles.stream()
.map(role -> new SimpleGrantedAuthority(role.getName()))
.collect(Collectors.toList());
}
}
I attempted to change .hasRole to .hasAuthority as seen in this answer (to no avail): Spring Security Java configuration for authenticated users with a role.

Resources