Spring Boot autologin after registration not working - spring

I have a login and a registration page. I wanted to achieve the feature of autologin after the registration. I have gone through various docs and finally came up with this. Can someone figure out what went wrong here?
Web Security Configuration
#Configuration
#EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder()
{
return new BCryptPasswordEncoder();
}
#Autowired
public UserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/register/**","/css/**","/js/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/welcome",true)
.permitAll()
.and()
.rememberMe()
.rememberMeParameter("rememberme")
.rememberMeCookieName("myLogin")
.tokenValiditySeconds(360*60*60)
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login?logout")
.invalidateHttpSession(true)
.clearAuthentication(true)
.deleteCookies("myLogin");
}
}
Controller
#Autowired
protected AuthenticationManager authenticationManager;
#Autowired
UserRepo repo;
#Autowired
BCryptPasswordEncoder bCryptPasswordEncoder;
#RequestMapping("/login")
public String loginpage()
{
return "index";
}
#RequestMapping("/welcome")
public String welcomePage()
{
return "welcome";
}
#RequestMapping(value = "/register", method = RequestMethod.GET)
public String register(Model model)
{
model.addAttribute("user", new User());
return "register";
}
#RequestMapping(value = "/register",method = RequestMethod.POST)
public String registerIt(#Valid #ModelAttribute("user")User user, BindingResult result, Model model, HttpServletRequest request)
{
if(result.hasErrors())
{
return "register";
}
Roles roles1=new Roles();
Roles roles2=new Roles();
roles1.setRoles("ADMIN");
roles2.setRoles("USER");
ArrayList<Roles> roleList=new ArrayList<>();
roleList.add(roles1);
roleList.add(roles2);
user.setRoles(roleList);
user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
repo.save(user);
UsernamePasswordAuthenticationToken token=new UsernamePasswordAuthenticationToken(user.getUsername(),user.getPassword());
request.getSession();
token.setDetails(new WebAuthenticationDetails(request));
Authentication auth=authenticationManager.authenticate(token);
SecurityContextHolder.getContext().setAuthentication(auth);
return "welcome";
}
Still, after the registration, the page redirects to the Login page itself. I am not able to figure out what went wrong.... Please help...

Try this to init the Auth:
Ref: org.springframework.security.web.authentication.AuthenticationFilter#successfulAuthentication
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
org.springframework.security.core.userdetails.UserDetails userDetails =
new YOURUserDetail( PARAMS );
//create instance of your AUTH object
Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, other params )
securityContext.setAuthentication(authentication);
SecurityContextHolder.setContext(securityContext);

Related

Request method 'POST' is not supported

I'm trying to upgrade Spring Boot from 2.7.6 to 3.0.1. I have a problem during the login action. The following is my new WebSecurityConfig:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig {
private final CustomUserDetailsService customUserDetailsService;
private final CustomizeAuthenticationSuccessHandler customizeAuthenticationSuccessHandler;
public WebSecurityConfig(CustomUserDetailsService customUserDetailsService, CustomizeAuthenticationSuccessHandler customizeAuthenticationSuccessHandler) {
this.customUserDetailsService = customUserDetailsService;
this.customizeAuthenticationSuccessHandler = customizeAuthenticationSuccessHandler;
}
#Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(customUserDetailsService);
authenticationProvider.setPasswordEncoder(passwordEncoder());
return authenticationProvider;
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public AccessDeniedHandler accessDeniedHandler(){
return new CustomAccessDeniedHandler();
}
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests()
.requestMatchers("/").permitAll()
.requestMatchers("/login").permitAll()
.authenticated()
.and()
.csrf().disable()
.formLogin()
.successHandler(customizeAuthenticationSuccessHandler)
.loginPage("/login")
.failureUrl("/login?error=true")
.usernameParameter("email")
.passwordParameter("password")
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.invalidateHttpSession(true)
.logoutSuccessUrl("/login?logout=true")
.and()
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler())
.and()
.authenticationProvider(authenticationProvider());
http
.sessionManagement()
.maximumSessions(1)
.expiredUrl("/login?expired=true");
return http.build();
}
// This second filter chain will secure the static resources without reading the SecurityContext from the session.
#Bean
#Order(0)
SecurityFilterChain resources(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/resources/**", "/static/**", "/css/**", "/js/**", "/images/**").permitAll()
.anyRequest().permitAll())
.requestCache().disable()
.securityContext().disable()
.sessionManagement().disable();
return http.build();
}
}
Follow my CustomUserDetailService:
#Service
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
public CustomUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User findUserByEmail(String email) {
System.out.println(email);
User user = userRepository.findByEmail(email.toLowerCase());
System.out.println(user.getEmail());
return userRepository.findByEmail(email.toLowerCase());
}
#Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
User user = userRepository.findByEmail(email.toLowerCase());
if (user != null) {
List<GrantedAuthority> authorities = Arrays.asList(new SimpleGrantedAuthority( user.getRole()));;
return buildUserForAuthentication(user, authorities);
} else {
throw new UsernameNotFoundException("username not found");
}
}
private UserDetails buildUserForAuthentication(User user, List<GrantedAuthority> authorities) {
return new org.springframework.security.core.userdetails.User(user.getEmail(), user.getPassword(), authorities);
}
}
When I run the application I see the login page, but when I enter the credential and press submit I receive the error:
Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' is not supported]
and Tomcat shows:
HTTP Status 405 – Method Not Allowed Type Status Report
Message Method 'POST' is not supported.
I searched for a solution but really I don't understand where is the problem.
To use multiple HttpSecurity instances, you must specify a security matcher, otherwise the first SecurityFilterChain will process all requests, and no requests will reach the second chain.
See this section of the Spring Security reference documentation.
In your case the SecurityFilterChain called resources is matching all requests, because you don't have a security matcher.
Since the resources chain does not configure formLogin then Spring Security does not create the default /login POST endpoint.
You can fix this by changing requests to:
#Bean
#Order(0)
SecurityFilterChain resources(HttpSecurity http) throws Exception {
http
.securityMatchers((matchers) -> matchers
.requestMatchers("/resources/**", "/static/**", "/css/**", "/js/**", "/images/**") // the requests that this SecurityFilterChain will process
)
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().permitAll())
.requestCache().disable()
.securityContext().disable()
.sessionManagement().disable();
return http.build();
}
If you want more details on the difference between authorizeHttpRequests and requestMatchers you can check out this question.
This error typically occurs when the method in the controller is not mapped to a post request. Should be something like:
#RequestMapping(value = "/login", method = {RequestMethod.GET, RequestMethod.POST})
public ModelAndView login(...

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".

GET method is not supported, shows after success log in, how to solve this?

I am working on an administration system, I have HomeController.java:
#Controller
public class HomeController {
#Autowired
private UserRepository userRepository;
#Autowired
private UserService userService;
#GetMapping("/")
public String root() {
return "/home";
}
#GetMapping("/home")
public String home() {
return "/home";
}
#RequestMapping("/login")
public String login() {
return "/login";
}
#RequestMapping("/user")
public String userIndex() {
return "/index";
}
#GetMapping("/profile")
public String currentUser(#ModelAttribute("user") #Valid UserDto userDto, BindingResult result, Model model) {
Authentication loggedInUser = SecurityContextHolder.getContext().getAuthentication();
String email = loggedInUser.getName();
User user = userRepository.findByEmail(email);
String firstName = user.getFirstName();
model.addAttribute("firstName", firstName);
model.addAttribute("email", email);
return "profile";
}
when i try to log in with bad credentials everything works fine and it gives invalid pass or username.
The problem is when i enter the correct Credentials, i get this page:
Here is SecurityConfig.java:
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserService userService;
#Autowired
private UserRepository userRepository;
#Override
protected void configure(final HttpSecurity http) throws Exception {
// #formatter:off
http
.authorizeRequests()
.antMatchers(
"/"
, "/home"
, "/registration"
, "/forgot-password/"
, "/reset-password/"
, "/welcome"
, "/js/**"
, "/css/**"
, "/img/**"
, "/webjars/**"
)
.permitAll()
.and()
.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/super/**").hasRole("SUPER")
.antMatchers("/partner/**").hasRole("PARTNER")
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/index" , true)
.permitAll()
.and()
.logout()
.invalidateHttpSession(true)
.clearAuthentication(true)
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/login?logout")
.permitAll()
.and()
.rememberMe()
.and()
.rememberMe()
.key("uniqueAndSecret")
.userDetailsService(userService);
}
//Beans
#Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder(11);
}
#Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider auth = new DaoAuthenticationProvider();
auth.setUserDetailsService(userService);
auth.setPasswordEncoder(encoder());
return auth;
}
#Bean
public AuthenticationManager customAuthenticationManager() throws Exception {
return authenticationManager();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
}
}
Here:
#RequestMapping("/user")
public String userIndex() {
return "/index";
}
you do redirect to index (without '.html' extension), but there is no mapping for index url.
So you can change it to:
return "/index.html";
if you have index.html file in static directory.

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.

Spring injection of userdetailsservice in security config class fails

I am new to spring security and i am trying to configure it using java but when i try to inject a UserDetailsService into a security config class i get a 404 error page but when i inject it into a controller the injection works. am using spring version 4.1.6 and spring security 4.0.0
here is my security config class
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier("loginService")
UserDetailsService loginService; //THIS IS THE POINT OF FAILURE
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/")
.usernameParameter("username")
.passwordParameter("password")
.defaultSuccessUrl("/userlist")
.failureUrl("/")
.permitAll()
.and()
.logout()
.permitAll();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
/*auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");*/
auth.userDetailsService(loginService).passwordEncoder(passwordEncoder());
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/resources/**");
}
#Bean
public Md5PasswordEncoder passwordEncoder(){
Md5PasswordEncoder encoder = new Md5PasswordEncoder();
return encoder;
}
}
This is the UserDetailsService class
#Service("loginService")
public class LoginService implements UserDetailsService{
#Autowired
UserRepository userRepository;
#Transactional
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
SiteUser user = userRepository.findByUsername(username);
Collection<SimpleGrantedAuthority> authorities = new ArrayList<SimpleGrantedAuthority>();
SimpleGrantedAuthority userAuthority = new SimpleGrantedAuthority("ROLE_USER");
SimpleGrantedAuthority adminAuthority = new SimpleGrantedAuthority("ROLE_ADMIN");
User u = null;
if(user == null)
throw new UsernameNotFoundException("No such User: " + username);
else
{
if (user.getRole().equals("USER"))
authorities.add(userAuthority);
else if (user.getRole().equals("ADMIN"))
{
authorities.add(userAuthority);
authorities.add(adminAuthority);
}
u = new User(user.getUsername(), user.getPassword(), authorities);
}
return u;
}
}
The rest of the project is available
here
The solution was to add
#ComponentScan("com.ashken.*")
on top of the securityconfig class
I've found that it is less troublesome to simply register your implementation of UserDetailsService as a bean in SecurityConfig:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserRepository userRepository;
#Bean
public UserDetailsService userDetailsService() {
return new UserDetailsService() {
#Transactional
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
SiteUser user = userRepository.findByUsername(username);
Collection<SimpleGrantedAuthority> authorities = new ArrayList<SimpleGrantedAuthority>();
SimpleGrantedAuthority userAuthority = new SimpleGrantedAuthority("ROLE_USER");
SimpleGrantedAuthority adminAuthority = new SimpleGrantedAuthority("ROLE_ADMIN");
User u = null;
if(user == null) {
throw new UsernameNotFoundException("No such User: " + username);
} else {
if (user.getRole().equals("USER")) {
authorities.add(userAuthority);
} else if (user.getRole().equals("ADMIN")) {
authorities.add(userAuthority);
authorities.add(adminAuthority);
}
u = new User(user.getUsername(), user.getPassword(), authorities);
}
return u;
}
};
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/")
.usernameParameter("username")
.passwordParameter("password")
.defaultSuccessUrl("/userlist")
.failureUrl("/")
.permitAll()
.and()
.logout()
.permitAll();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
/*auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");*/
auth.userDetailsService(loginService).passwordEncoder(passwordEncoder());
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/resources/**");
}
#Bean
public Md5PasswordEncoder passwordEncoder(){
Md5PasswordEncoder encoder = new Md5PasswordEncoder();
return encoder;
}
}

Resources