Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring boot - return user object after log in

I have a spring boot application with WebSecurityConfigurerAdapter configured like this -

http.csrf().disable()
                    .exceptionHandling()
                    .authenticationEntryPoint(restAuthenticationEntryPoint)
                    .and()
                .authorizeRequests()
                    .antMatchers("/user/*", "/habbit/*").authenticated()
                    .and()
                .formLogin()
                    .loginProcessingUrl("/login")
                    .permitAll()
                    .usernameParameter("email")
                    .passwordParameter("pass")
                    .successHandler(authenticationSuccessHandler)
                    .failureHandler(new SimpleUrlAuthenticationFailureHandler())
                    .and()
                .logout()
                    .logoutUrl("/logout")
                    .invalidateHttpSession(true);

Can I add something like my own controller that would, after successful authentication, return back a custom object with some details about the authenticated user?

Update: To clarity, i'm using an angular application as the client. Currently I need to make 2 requests form my client to the server: 1. POST request to /login URL for authentication. 2. GET request to retrieve authenticated user data.

My aim is to have the 1st request return to me user information so I don't have to make the 2dn request. Currently the 1st request only authenticates the user, creates a session on the server and send back a '200 OK' status response with no data. I want it to return a success response with data about the logged in user.

Answered:

The correct answer is in comments so i will write it here: I needed to redirect from my successHandler to my controller which in turn returns the currently logged in user info ( in my case controller is in url '/user/me':

 @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                                        Authentication authentication) throws ServletException, IOException {
        clearAuthenticationAttributes(request);
        getRedirectStrategy().sendRedirect(request, response, "/user/me");
    }
like image 259
Tomas Avatar asked Feb 06 '16 12:02

Tomas


People also ask

What is SecurityContextHolder getContext () getAuthentication ()?

The HttpServletRequest.getUserPrincipal() will return the result of SecurityContextHolder.getContext().getAuthentication() . This means it is an Authentication which is typically an instance of UsernamePasswordAuthenticationToken when using username and password based authentication.


2 Answers

If I understand your problem right, I can suggest next way.

First of all you have to implement class, that will contain user information. This class must be inherited from org.springframework.security.core.userdetails.User:

public class CustomUserDetails extends User {

    public CustomUserDetails(String username, String password,
         Collection<? extends GrantedAuthority> authorities) {            
        super(username, password, authorities);
    }

    //for example lets add some person data        
    private String firstName;
    private String lastName;

    //getters and setters
}

Next step, you have create you own implementation of interface org.springframework.security.core.userdetails.UserDetailsService:

@Service
public class CustomUserDetailService implements UserDetailsService{

    @Override
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException{         

        if(StringUtils.isEmpty(userName)) 
            throw new UsernameNotFoundException("User name is empty");

        //if you don't use authority based security, just add empty set
        Set<GrantedAuthority> authorities = new HashSet<>();
        CustomUserDetails userDetails = new CustomUserDetails(userName, "", authorities);            

        //here you can load user's data from DB or from 
        //any other source and do:
        //userDetails.setFirstName(firstName);
        //userDetails.setLastName(lastName);

        return userDetails;
    }

}

As you see, this class has just one method, where you can load and set custom user details. Note, that I marked this class with @Service annotation. But you can register it in your Java-config or XML context.

Now, to access your user data after successful authentication, you can use next approach, when Spring will automatically pass principal in controller's method:

@Controller
public class MyController{

    @RequestMapping("/mapping")
    public String myMethod(Principal principal, ModelMap model){
        CustomUserDetails userDetails = (CustomUserDetails)principal;
        model.addAttribute("firstName", userDetails.getFirstName());
        model.addAttribute("lastName", userDetails.getLastName());
    }
}

Or another one way:

@Controller
public class MyController{

    @RequestMapping("/mapping")
    public String myMethod(ModelMap model){
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        CustomUserDetails userDetails = (CustomUserDetails)auth.getPrincipal();
        model.addAttribute("firstName", userDetails.getFirstName());
        model.addAttribute("lastName", userDetails.getLastName());
    }
}

This method can be used in other places, where Spring does not pass principal automatically.

To go to specific address after successful authentication you can use SimpleUrlAuthenticationSuccessHandler. Just create it in your config:

@Bean
public SavedRequestAwareAuthenticationSuccessHandler successHandler() {
    SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
    successHandler.setTargetUrlParameter("/succeslogin");
    return successHandler;
}

and use it in your configuration:

http.formLogin()
    .loginProcessingUrl("/login")
    .permitAll()
    .usernameParameter("email")
    .passwordParameter("pass")
    .successHandler(successHandler())

after that you can create controller, that will send response from speciafied url:

@Controller
@RequestMapping("/sucesslogin")
public class SuccessLoginController{

     @RequestMapping(method = RequestMethod.POST)
     public String index(ModelMap model, Principal principal){
         //here you can return view with response
     }

}

Of cause, you can return not only view, but JSON response (using @ResponseBody annotation), or something else, depends on you front-end. Hope this will be helpful.

like image 170
Ken Bekov Avatar answered Sep 30 '22 03:09

Ken Bekov


In the accepted answer you need two calls to get the data you want. Simply return the data after the login in a custom AjaxAuthenticationSuccessHandler like this.

@Bean
public AjaxAuthenticationSuccessHandler ajaxAuthenticationSuccessHandler() {
    return new AjaxAuthenticationSuccessHandler() {

        @Override
        public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
            response.getWriter().write(new ObjectMapper().writeValueAsString(new UserAuthenticationResponse(authentication.getName(), 123l)));
            response.setStatus(200);
        }

    };
}

and register the successhandler:

http.successHandler(ajaxAuthenticationSuccessHandler())
like image 31
Simon Ludwig Avatar answered Sep 30 '22 04:09

Simon Ludwig