Inheritance with #Controller in Spring - spring-mvc

I would like have a #Controller parent in my app, as this:
#Controller
public class controllerParent{
public String getUser (Model model,HttpServletRequest request){
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String name = auth.getName(); //get logged in username
user = userService.findByUserName(name);
model.addAttribute("user",user);
}
}
and a child:
public class chil extends controllerParent{
#RequestMapping(value="/something.html",method = RequestMethod.GET)
public String doSomething (Model model,HttpServletRequest request){
//Code to do something
}
}
And what want to do is that in each child controller call I want show in my JSP the user . Can I do this without rewrite spring security code?

Related

How to add custom argument in Spring controller method argument (like: #RequestParam, Model)?

This is a typical Spring controller method.
public ResponseEntity<RestApiResponse<OTPResponse>> sendOtp(HttpServletRequest request) {
UserDetails userDetails = (UserDetails) request.getSession().getAttribute(SessionKey.USER_DETAILS);
// Do some work
//...
}
To get the username I have to copy the bellow line over and over again in every other controller method.
UserDetails userDetails = (UserDetails) request.getSession().getAttribute(SessionKey.USER_DETAILS);
Can I do this as follows ?
// #UserDetails is some kind of imaginary annotation
public ResponseEntity<RestApiResponse<OTPResponse>> sendOtp(#UserDetails UserDetails userDetails) {
userDetails.getUsername();
// Do some work
//....
}
Can I intercept the request, get the userDetails from request and inject that as controller method argument?
you can use below code as util method
public UserDetails getUser(){
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (!(auth instanceof AnonymousAuthenticationToken)) {
return (UserDetails) auth.getPrincipal();
}
return null;
}
Another way of doing same.
#RequestMapping(method = RequestMethod.GET)
public ModelAndView anyMethodNameGoesHere(Principal principal) {
final String loggedInUserName = principal.getName();
}
You can intercept requests and do it yourself, however spring-security already have such feature. It is called #AuthenticationPrincipal.
You can use it as follow:
#GetMapping
String hello(#AuthenticationPrincipal java.security.Principal principal) {
return principal.getName();
}
If having a Principal isn't enough for your usecase, User also works:
#GetMapping
String hello(#AuthenticationPrincipal org.springframework.security.core.userdetails.User user) {
return user.getUsername();
}
You can even inject your custom user details this way if you want.
Please have a look at documentation.

Sprint Rest AuthenticationPrincipal returns customUser with only null values

I am trying to get the currently logged in user (basic auth) in a spring boot REST backend:
#RequestMapping(value = "/someURL", method = RequestMethod.GET)
public #ResponseBody Map someMethod(
Authentication auth, // <==== works
#AuthenticationPrincipal(expression = "liquidoUserModel") UserModel liquidoUserModel
)
{
log.debug(auth.getPrincipal()) // <==== THIS WORKS
log.debug(liquidoUserModel) // <==== returns an intance with empty fields
}
Here is my custom UserModel
#Data // Lombok magic for all the getters and setters
#Entity
#RequiredArgsConstructor
#Table(name = "users")
public class UserModel {
#Id
Long id;
#NonNull
#Column(unique = true)
public String email;
[...]
}
And this is my UserDetailsService
public class LiquidoUserDetailsService implements UserDetailsService {
#Autowired
UserRepo userRepo;
#Override
public LiquidoAuthUser loadUserByUsername(String email) throws UsernameNotFoundException {
UserModel userModel = userRepo.findByEmail(email);
return new LiquidoAuthUser(userModel.getEmail(), userModel.getPasswordHash(), getGrantedAuthorities(userModel), userModel);
}
}
And finally the LiquidoAuthUser
public class LiquidoAuthUser extends User {
private UserModel liquidoUserModel;
public LiquidoAuthUser(String username, String password, Collection<? extends GrantedAuthority> authorities, UserModel liquidoUserModel) {
super(username, password, authorities);
this.liquidoUserModel = liquidoUserModel;
}
public UserModel getLiquidoUserModel() {
return liquidoUserModel;
}
public void setLiquidoUserModel(UserModel userModel) {
this.liquidoUserModel = userModel;
}
}
And of course I have the #EnableWebMvc annotation on my main SpringApplication class.
My problem: How can I get the currently authenticated custom UserModel in the REST handler?
The strange thing: I actually can get my custom user from the Authentication auth object. Why does the #AuthenticationPrincipal not work?
I am using spring-security-4.1.3-RELEASE
Full code is open source at https://github.com/Doogiemuc/liquido-backend-spring
I tried and debug your code but not able to find issue of
#AuthenticationPrincipal. Typically this annotation is resolved by AuthenticationPrincipalArgumentResolver class of spring security web annotation. By using #EnableWebSecurity you will automatically have this added to your Spring MVC configuration. Need to be debug more on AuthenticationPrincipalArgumentResolver that for time being I will suggest go with Authentication class and get your object.

How to port SpringMVC Application to SpringREST?

We have created SpringMVC application using Spring-Boot and Thymleaf. Now as per new requirement, I have to convert them to SPring-REST for external application consumption(AngularJS and Android App) without affecting the thymleaf pages.
Please assist.
Below is the sample code. Like this many controllers are there
#Controller
#RequestMapping(value = "/admin/register")
#SessionAttributes("roles")
public class AdminRegisterController {
#Autowired
private UserService userService;
#Autowired
private RoleRepository roleRepository;
#ModelAttribute("user")
public User constructUser() {
return new User();
}
#ModelAttribute("roles")
public List<Role> InitializeRoles() {
return roleRepository.findAll();
}
// Display Register Page
#RequestMapping
public String showRegister(Model model) {
model.addAttribute("current", "register");
return "register";
}
// Inserting new User
#RequestMapping(method = RequestMethod.POST)
public ModelAndView doRegister(#Valid #ModelAttribute("user") User user, BindingResult result) {
if (result.hasErrors()) {
return new ModelAndView("register");
}
userService.save(user);
RedirectView redirectView = new RedirectView("/admin/register?success=true");
redirectView.setExposeModelAttributes(false);
return new ModelAndView(redirectView);
}
#RequestMapping("/available")
#ResponseBody
public String available(#RequestParam String username) {
User user = userService.findOne(username);
Boolean available = userService.findOne(username) == null;
return available.toString();
}
}
You can use the javax.ws.rs api for doing that.
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.0</version>
</dependency>
And use #RestController instead of simple #Controllers in your current code.

Can we get HttpRequest Object in Controller in Spring MVC

Is it possible that I can get HttpRequest object in my Controller class in Spring MVC ?
#Controller
public class ContactController {
#Autowired
private ContactService contactService;
#RequestMapping("/login")
public String displayLoginPage(#ModelAttribute("login") Login login, BindingResult result) {
return "login";
}
}
#RequestMapping("/login")
public String displayLoginPage(HttpServletRequest request, #ModelAttribute("login") Login login, BindingResult result){
return "login";
}

Override Generic Controller

I have the following function in my abstract controller
public abstract class GenericController<T extends PersistentObject> {
...
...
...
#RequestMapping(value = "/validation.json", method = RequestMethod.POST)
#ResponseBody
public ValidationResponse ajaxValidation(#Valid T t, BindingResult result) {
ValidationResponse res = new ValidationResponse();
if (!result.hasErrors()) {
res.setStatus("SUCCESS");
} else {
res.setStatus("FAIL");
List<FieldError> allErrors = result.getFieldErrors();
List<ErrorMessage> errorMesages = new ArrayList<ErrorMessage>();
for (FieldError objectError : allErrors) {
errorMesages.add(new ErrorMessage(objectError.getField(),
objectError.getDefaultMessage()));
}
res.setErrorMessageList(errorMesages);
}
return res;
}
At most cases the validation is sufficient for different kind of entities. Now I would like to customize the validation on my concrete controller as
#Controller
#RequestMapping("user")
public class UserController extends GenericController<User> {
#RequestMapping(value = "/validation.json", method = RequestMethod.POST)
#ResponseBody
public ValidationResponse ajaxValidation(#Valid User user,
BindingResult result, Locale locale) {
ValidationResponse res = super.ajaxValidation(user, result);
if (!user.getPassword().equals(user.getConfirmPassword())) {
res.setStatus("FAIL");
res.getErrorMessageList().add(
new ErrorMessage("confirmPassword", messageSource
.getMessage("password.mismatch", null, locale)));
}
return res;
}
}
With this I get the following error java.lang.IllegalStateException: Ambiguous mapping found. Cannot map 'userController' bean method. How can I solve this issue ? Is there a better approach ?
The problem here is that after extending your generic controller you have two different methods
public ValidationResponse ajaxValidation(#Valid T t, BindingResult result)
and
public ValidationResponse ajaxValidation(#Valid User user,
BindingResult result, Locale locale)
with the exact same mapping
user/validation.json
An ugly solution would be to add the Locale locale param to your abstract controller method (even if you don't use it) and add the #Overwrite annotation to the UserController method (you'll get a compilation error otherwise). This way the two methods become one.
The generic controller is extended by other classes ? Then you will have two identical requestmappings.
Are you sure the UserController is correctly getting user prepend request mapping applied ? As this works :
#RequestMapping("test")
public class ExampleController extends AbstractController {
#RequestMapping(value = "/Home", method = RequestMethod.GET)
public String getHome(Model model) {
return "home";
}
with the same mapping in AbstractController
#RequestMapping(value = "/Home", method = RequestMethod.GET)
public String getHome(Model model) {
return "home";
}

Resources