Spring social returning wrong user profile - spring

I'm using Spring Social LinkedIn to retrieve user profiles with a custom ConnectController since I want to the user to login and retrieve the profile in one step. The issue is that sometimes the first user in the system is returned instead of the currently logged in user.
Here is my CustomConnectController
#Controller
#RequestMapping("/connect")
public class CustomConnectController extends ConnectController {
#Inject
public CustomConnectController(ConnectionFactoryLocator connectionFactoryLocator,
ConnectionRepository connectionRepository) {
super(connectionFactoryLocator, connectionRepository);
}
#Override
protected String connectView(String providerId) {
return "redirect:/hey/" + providerId + "Connect";
}
#Override
protected String connectedView(String providerId) {
return "redirect:/hey/" + providerId + "Connected";
}
}
and my webcontroller
#Controller
public class WebController {
#Autowired
private LinkedIn linkedin;
#Autowired
private ConnectionRepository repository;
#RequestMapping(value = "/hey/linkedinConnected", method = RequestMethod.GET)
public String linkedinConnected(HttpServletRequest request, Model model, Locale locale) {
if (repository.findConnections("linkedin").isEmpty()
|| !linkedin.isAuthorized()) {
return "redirect:/connect/linkedin";
}
LinkedInProfile userProfile = linkedin.profileOperations().getUserProfile();
return "loggedinpage";
}
#RequestMapping(value = "/hey/linkedinConnect", method = RequestMethod.GET)
public String linkedinConnect(HttpServletRequest request, Model model, Locale locale) {
if (repository.findConnections("linkedin").isEmpty()
|| !linkedin.isAuthorized()) {
return "redirect:/connect/linkedin";
}
LinkedInProfile userProfile = linkedin.profileOperations().getUserProfile();
return "loggedinpage";
}
}
Any ideas of what I'm doing wrong?

Related

Spring boot - Pass argument from interceptor to method in controller

For learning purposes, I have made a custom authentication system where I pass a token from the client to the server through the Authorization header.
In the server side, I'd like to know if it's possible to create in the interceptor, before the request reaches a method in the controller, an User object with the email from the token as a property, and then pass this user object to every request where I require it.
This what I'd like to get, as an example:
#RestController
public class HelloController {
#RequestMapping("/")
public String index(final User user) {
return user.getEmail();
}
}
public class User {
private String email;
}
Where user is an object that I created in the pre-interceptor using the request Authorization header and then I can pass, or not, to any method in the RestController.
Is this possible?
#Recommended solution
I would create a #Bean with #Scope request which would hold the user and then put the appropriate entity into that holder and then take from that holder inside the method.
#Component
#Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class CurrentUser {
private User currentUser;
public User getCurrentUser() {
return currentUser;
}
public void setCurrentUser(User currentUser) {
this.currentUser = currentUser;
}
}
and then
#Component
public class MyInterceptor implements HandlerInterceptor {
private CurrentUser currentUser;
#Autowired
MyInterceptor(CurrentUser currentUser) {
this.currentUser = currentUser;
}
#Override
public boolean preHandle(
HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
this.currentUser.setCurrentUser(new User("whatever"));
return true;
}
}
and in the Controller
#RestController
public class HelloController {
private CurrentUser currentUser;
#Autowired
HelloController(CurrentUser currentUser) {
this.currentUser = currentUser;
}
#RequestMapping("/")
public String index() {
return currentUser.getCurrentUser().getEmail();
}
}
#Alternative solution
In case your object that you would like to have, only contains one field, you can just cheat on that and add that field to the HttpServletRequest parameters and just see the magic happen.
#Component
public class MyInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(
HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//TRY ONE AT THE TIME: email OR user
//BOTH SHOULD WORK BUT SEPARATELY OF COURSE
request.setAttribute("email", "login#domain.com");
request.setAttribute("user", new User("login#domain.com"));
return true;
}
}
You can use a local thread context object as follows - which will be handling one parameter per request thread (thread safe):
public abstract class LoggedUserContext {
private static ThreadLocal<User> currentLoggedUser = new ThreadLocal<>();
public static void setCurrentLoggedUser(User loggedUser) {
if (currentLoggedUser == null) {
currentLoggedUser = new ThreadLocal<>();
}
currentLoggedUser.set(loggedUser);
}
public static User getCurrentLoggedUser() {
return currentLoggedUser != null ? currentLoggedUser.get() : null;
}
public static void clear() {
if (currentLoggedUser != null) {
currentLoggedUser.remove();
}
}
}
Then in the interceptor prehandle function:
LoggedUserContext.setCurrentLoggedUser(loggedUser);
And in the interceptor postHandler function:
LoggedUserContext.clear();
From any other place:
User loggedUser = LoggedUserContext.getCurrentLoggedUser();

SecurityContextHolder.getContext().getAuthentication() always return 'anonymousUser'

I created Spring boot application with the following configuration:
Spring boot 2.1.0.RELEASE
OpenJdk 11
I have an AuditConfiguration class in my project that looks like:
#Configuration
#EnableJpaAuditing(auditorAwareRef = "auditorProvider")
public class AuditConfiguration {
#Bean
public AuditorAware<String> auditorProvider() {
return new AuditorAwareImpl();
}
class AuditorAwareImpl implements AuditorAware<String> {
#Override
public Optional<String> getCurrentAuditor() {
Principal principal =
SecurityContextHolder.getContext().getAuthentication();
return Optional.of(principal.getName());
}
}
}
and SecurityContextHolder.getContext().getAuthentication() always returns anonymousUser.
However, the following code returns the correct user name.
#RestController
#RequestMapping("/history")
public class HistoryEndpoint {
#RequestMapping(value = "/username", method = RequestMethod.GET)
#ResponseBody
public String currentUserName(Principal principal) {
return principal.getName();
}
}
I need your help for resolving this issue.
I got authenticared user using following class. i had problem with JPA Auditing.
#CreatedBy always saved null. then i tried to get authenticated user SecurityContextHolder.getContext().getAuthentication() using this method. that method returned annonymousUser. however my issue is fixed.
#ManagedBean
#EnableJpaAuditing
public class SpringSecurityAuditorAware implements AuditorAware<String> {
private final HttpServletRequest httpServletRequest;
public SpringSecurityAuditorAware(HttpServletRequest httpServletRequest) {
this.httpServletRequest = httpServletRequest;
}
#Override
public Optional<String> getCurrentAuditor() {
return Optional.ofNullable(httpServletRequest.getUserPrincipal())
.map(Principal::getName);
}
}

Spring Java how to use controller

I'm completely new to Spring framework. and I have a task to make phone book application on spring. I need to make registration and authorization and also my phone book. I have 2 controllers for that, first UserController that controls authorization and registration
#Controller
public class UserController {
#Autowired
private UserService userService;
#Autowired
private SecurityService securityService;
#Autowired
private UserValidator userValidator;
#RequestMapping(value = "/registration", method = RequestMethod.GET)
public String registration(Model model) {
model.addAttribute("userForm", new User());
return "registration";
}
#RequestMapping(value = "/registration", method = RequestMethod.POST)
public String registration(#ModelAttribute("userForm")
User userForm, BindingResult bindingResult, Model model) {
userValidator.validate(userForm, bindingResult);
if (bindingResult.hasErrors()) {
return "registration";
}
userService.save(userForm);
securityService.autoLogin(userForm.getUsername(), userForm.getConfirmPassword());
return "redirect:/welcome";
}
#RequestMapping(value = "/login", method = RequestMethod.GET)
public String login(Model model, String error, String logout) {
if(error!=null) {
model.addAttribute("error", "Username or password is incorrect.");
}
if (logout!=null) {
model.addAttribute("message", "logged out successfully");
}
return "login";
}
#RequestMapping(value = {"/", "/welcome"}, method = RequestMethod.GET)
public String welcome(Model model) {
return "welcome";
}
#RequestMapping(value = "/admin", method = RequestMethod.GET)
public String admin(Model model) {
return "admin";
}
}
and ContactController that controls my fuctionality(adding, removing, editing and shows contacts)
#Controller
public class ContactController {
private ContactService contactService;
#Autowired(required = true)
#Qualifier(value = "contactService")
public void setContactService(ContactService contactService) {
this.contactService = contactService;
}
#RequestMapping(value = {"admin", "welcome"}, method = RequestMethod.GET)
public String listContactsForAdmin(Model model) {
model.addAttribute("contact", new Contact());
model.addAttribute("listContacts", this.contactService.listContacts());
return "admin";
}
#RequestMapping(value = "admin/add", method = RequestMethod.POST)
public String addContact(#ModelAttribute("contact") Contact contact) {
if (contact.getId() == 0) {
this.contactService.addContact(contact);
} else {
this.contactService.updateContact(contact);
}
return "redirect:/admin";
}
#RequestMapping("/remove/{id}")
public String removeContact(#PathVariable("id") int id) {
this.contactService.removeContact(id);
return "redirect:/admin";
}
#RequestMapping("/edit/{id}")
public String editBook(#PathVariable("id") int id, Model model) {
model.addAttribute("contact", this.contactService.getContactById(id));
model.addAttribute("listContacts", this.contactService.listContacts());
return "admin";
}
#RequestMapping("contactData/{id}")
public String contactData(#PathVariable("id") int id, Model model) {
model.addAttribute("contact", this.contactService.getContactById(id));
return "contactData";
}
}
when i try to authenticate or registr. new user I have such error:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.IllegalStateException: Ambiguous handler methods mapped for HTTP path 'http://localhost:8087/admin': {public java.lang.String kz.adilka.springsecurity.app.controller.UserController.admin(org.springframework.ui.Model), public java.lang.String kz.adilka.springsecurity.app.controller.ContactController.listContactsForAdmin(org.springframework.ui.Model)}
it says that I have problem with mapping admin page. but for me it seems to be ok, or maybe I missed smth
The reason is that you do not set the controller value for your controllers and they have the same RequestMapping method
#Controller // do not have identifier here
public class UserController {
#Autowired
private UserService userService;
#Autowired
private SecurityService securityService;
#Autowired
private UserValidator userValidator;
#RequestMapping(value = "/admin", method = RequestMethod.GET)
public String admin(Model model) {
return "admin";
}
}
#Controller // do not have identifier here
public class ContactController {
private ContactService contactService;
#RequestMapping(value = {"admin", "welcome"},
method = RequestMethod.GET)
public String listContactsForAdmin(Model model) {
model.addAttribute("contact", new Contact());
model.addAttribute("listContacts",
this.contactService.listContacts());
return "admin";
}
}
One possible solution is the set RequestMapping for each Controller method:
#Controller(value = "user")
public class UserController {
#Autowired
private UserService userService;
#Autowired
private SecurityService securityService;
#Autowired
private UserValidator userValidator;
#RequestMapping(value = "/admin", method = RequestMethod.GET)
public String admin(Model model) {
return "admin";
}
}
#Controller(value = "contact")
public class ContactController {
private ContactService contactService;
#RequestMapping(value = {"admin", "welcome"}, method = RequestMethod.GET)
public String listContactsForAdmin(Model model) {
model.addAttribute("contact", new Contact());
model.addAttribute("listContacts", this.contactService.listContacts());
return "admin";
}
}

Spring MVC architecture

I have three .jsp pages on my app (index, user and admin). And I have three controllers for everything page but in my administration and user controllers use duplicate methods. For example, I have a method "getGenres" (For shows all genres from DB) in admin controller and user controller. How can I combine these methods if "#RequestMapping" is different in controllers?
#Controller
#EnableWebMvc
#RequestMapping("admin")
public class AdminController {
#Autowired
private GenreTableService genreService;
#RequestMapping(value = "genres")
public ResponseEntity<Map<String, List<Genre>>> getGenres() throws ServiceException {
Map<String, List<Genre>> genres = new HashMap<>(1);
genres.put("genres", genreService.getAll());
return new ResponseEntity<>(genres, HttpStatus.OK);
}
You want to combine methods. this is sample code.
enter link description here
enter link description here
like this :
#Controller
#RequestMapping("/common")
public class AdminController {
#Autowired
private GenreTableService genreService;
#RequestMapping(value = "/genres")
public ResponseEntity<Map<String, List<Genre>>> getGenres() throws ServiceException {
Map<String, List<Genre>> genres = new HashMap<>(1);
genres.put("genres", genreService.getAll());
return new ResponseEntity<>(genres, HttpStatus.OK);
}
}
or
#Controller
public class AdminController {
#Autowired
private GenreTableService genreService;
#RequestMapping(value = {"/admin/genres", "/user/genres"})
public ResponseEntity<Map<String, List<Genre>>> getGenres() throws ServiceException {
Map<String, List<Genre>> genres = new HashMap<>(1);
genres.put("genres", genreService.getAll());
return new ResponseEntity<>(genres, HttpStatus.OK);
}
}

Spring Social Facebook Template always return same user

I am using Spring Social 2.0.2.RELEASE to provide social login with Facebook. My problem is that Spring Social always return the same first user when I use FacebookTemplate. Here the example:
```
#Autowired
private Facebook facebook;
#RequestMapping(value = "/facebook/login", method = RequestMethod.GET)
public ModelAndView handleFacebookLogin(HttpServletResponse response) {
//always the same user
User profile = facebook.fetchObject("me", User.class, "id", "name", "link", "email");
return new ModelAndView("redirect:/dashboard");
}
```
I also have a Custom ConnectController:
```
#Controller
#RequestMapping("/connect")
public class CustomConnectController extends ConnectController {
#Autowired
public CustomConnectController(ConnectionFactoryLocator connectionFactoryLocator,
ConnectionRepository connectionRepository) {
super(connectionFactoryLocator, connectionRepository);
}
#Override
protected RedirectView connectionStatusRedirect(String providerId, NativeWebRequest request) {
return new RedirectView("/facebook/login");
}
}
```
If a open two browsers and try to login with different users, it always return the first one. My current solution is just copy the entire ConnectController to my app and change the behaviour. It is terrible and I hope that I am making a big mistake.
I had the same issue and solved the problem by creating this class:
#Configuration
public class UniqueSessionUserID extends SocialConfigurerAdapter {
#Override
public UserIdSource getUserIdSource() {
return new UserIdSource() {
#Override
public String getUserId() {
RequestAttributes request = RequestContextHolder.getRequestAttributes();
String uuid = (String) request.getAttribute("_socialUserUUID", RequestAttributes.SCOPE_SESSION);
if (uuid == null) {
uuid = UUID.randomUUID().toString();
}
request.setAttribute("_socialUserUUID", uuid, RequestAttributes.SCOPE_SESSION);
return uuid;
}
};
}
}
Here is a link where it is explained in more detail why this is necessary:
Spring Social Facebook more than one user

Resources