Displaying username logged in using thymeleaf fragment header in Spring boot application - spring

I have a fragment for my web application which is used across multiple pages to display a navbar with the user logged in : if they are logged in, if not : display login & signup button.
I want to know how to include my attribute of a logged in user to a fragment as currently I have the logged in user attribute mapped to each controller displaying a web page, not a fragment.
To get the current logged in user I use
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
model.addAttribute("loggedinuser", authentication.getName());
However this is only specific to one get request. I want to be able to display the logged in user in a fragment which is then called by each page.
Here is an example of a controller getting the logged in user
#GetMapping(path= "")
public String getMainPage(HttpServletRequest request, Model model) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
model.addAttribute("loggedinuser", authentication.getName());
model.addAttribute("roles", authentication.getAuthorities());
return "mainpage";
}
Here is my class extending WebSecurityConfigurerAdapter to authenticate a users username and password against one in the database
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
String userbyUsernameQuery = "select username, password, '1' as enabled from auth_user where username=?;";
String rolebyUsernameQuery = "SELECT auth_user.username, auth_role.role_name as authority from auth_user\n" +
"INNER JOIN auth_user_role ON auth_user.auth_user_id = auth_user_role.auth_user_id\n" +
"INNER JOIN auth_role ON auth_role.auth_role_id = auth_user_role.auth_role_id\n" +
"WHERE auth_user.username =?";
auth.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery(userbyUsernameQuery)
.authoritiesByUsernameQuery(rolebyUsernameQuery)
.passwordEncoder(passwordEncoder());
}
The part of the navbarHeader fragment I want to change is this
<ul class="nav navbar-nav navbar-right">
<li><a th:href="#{/registration}"><span class="glyphicon glyphicon-user"></span>Sign up</a></li>
<li><a th:href="#{/login}"><span class="glyphicon glyphicon-log-in"></span> Login</a></li>
</ul>
I want to display the logged in user if logged in & disable the sign up button. If not then display the log in button & signup button
Here is my mainpage page which displays the current user logged in and role which I want to move to the fragment so I do not need to add all the code in each controller & html page.
<div align="right" id="loggedIn" th:if="${loggedinuser != null}">
<div style="margin: 10px"
th:text="'Logged in as: '+${loggedinuser} + ' Role: ' +${roles}">
</div>
</div>
The user class
#Data
#AllArgsConstructor
#NoArgsConstructor
public class User{
private int id;
private String name;
private String lastName;
private String username;
private String password;
private String email;
private String enabled;
public User(String name, Collection<? extends GrantedAuthority> authorities) {
}
}

For this issue I basically utilize ModelAttribute which would be available across all the Presentation compilation. So your controller would like this
public class MyController {
#GetMapping(path= "hi")
public String getMainPage(HttpServletRequest request, Model model) {
return "mainpage";
}
#GetMapping(path= "hello")
public String getMainPage(HttpServletRequest request, Model model) {
return "helloPage";
}
#ModelAttribute("loggedinuser")
public User globalUserObject() {
// Add all null check and authentication check before using. Because this is global
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
model.addAttribute("loggedinuser", authentication.getName());
model.addAttribute("roles", authentication.getAuthorities());
// Create User pojo class
User user = new User(authentication.getName(), Arrays.asList(authentication.getAuthorities()))
return user;
}
}
Now the object loggedinuser available for all the pages.

Related

Inserting data into related table with thymeleaf

I have table user and table wallet, table wallet have userId so they are connected.
I created controller like this:
#PostMapping("/user/{user_id}/wallets")
public ResponseEntity<?> createWallet(#PathVariable(value = "user_id") Long user_id,
#RequestBody Wallet walletRequest) {
if (walletRepository.existsByUserIdAndWalletName(user_id, walletRequest.getWalletName())) {
return ResponseEntity.badRequest().body(new MessageResponse("You already have wallet with that name, choose another!"));
}
Wallet comment = userRepository.findById(user_id).map(tutorial -> {
walletRequest.setUser(tutorial);
return walletRepository.save(walletRequest);
}).orElseThrow(() -> new IllegalArgumentException("Not found user with id = " + user_id));
return new ResponseEntity<>(comment, HttpStatus.CREATED);
}
And that works fine when I go in postman and hit API /api/user/1/wallets with the appropriate JSON body, I mean wallet is added to user with ID 1.
But I dont know how to transform this to consume in Thymeleaf?
This is all stuff that is related to this thing:
First of all API to show new wallet form:
#GetMapping("/showNewWalletForm")
public String showNewWalletForm(Model model) {
Wallet wallet = new Wallet();
model.addAttribute("wallet", wallet);
return "new_wallet";
}
Form inside html:
<form action="#" th:action="#{/api/wallet/saveWallet}" th:object="${wallet}" method="POST">
<input type="text" th:field="*{walletName}" placeholder="Wallet name" class="form-control mb-4 col-4">
<input type="text" th:field="*{initialBalance}" placeholder="Enter balance" class="form-control mb-4 col-4">
<button type="submit" class="btn btn-info col-2"> Save Wallet</button>
</form>
And API to save wallet:
#PostMapping("/saveWallet")
public String saveWallet(#ModelAttribute("wallet") Wallet wallet) {
// save employee to database
walletService.saveWallet(wallet);
return "redirect:/";
}
Obviously I'm getting Column 'user_id' cannot be null since I didnt set it anywhere as I did in postman.
This is Wallet class:
#Entity
#Table(name = "wallet")
public class Wallet {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotEmpty(message = "Please, insert a wallet name")
private String walletName;
private double initialBalance;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "user_id", nullable = false)
#OnDelete(action = OnDeleteAction.CASCADE)
#JsonIgnore
private User user;
To make this work, you need to be able to fetch the logged-in user and since this is something you will need often, I suggest that you create a Spring managed class.
#Component("CurrentUserUtility")
public class CurrentUserUtility {
public static UserDetailsImpl getCurrentUser() {
Authentication tAuthentication = getAuthentication();
if (tAuthentication != null) {
return (UserDetailsImpl) getAuthentication().getPrincipal();
} else {
return null;
}
}
private static Authentication getAuthentication() {
return SecurityContextHolder.getContext().getAuthentication();
}
}
UserDetailsImpl is the class implementing the UserDetails interface and this class should have a userId field (or any other field such as username or email that can be used to uniquely identify the current user).
Now, the only thing you need to do in you controller before saving a new wallet is retrieve the current user using the above utility class.
#PostMapping("/saveWallet")
public String saveWallet(#ModelAttribute("wallet") Wallet wallet) {
//user_id shoudn't be null after the next line
wallet.setUser(userRepository.findById( CurrentUserUtility.getCurrentUser().getUserId() ));
walletService.saveWallet(wallet);
return "redirect:/";
}

Extends Spring security user class

I'm Working on a Spring security project . I try to extends the security.core.userdetails.User class to add more details while registering the users.
User Extended class
public class UserDetails extends User {
private int id;
private String Country;
public UserDetails(String username, String password, Collection<? extends GrantedAuthority> authorities, int id,
String country) {
super(username, password, authorities);
this.id = id;
Country = country;
}
public UserDetails(String username, String password, boolean enabled, boolean accountNonExpired,
boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities,
int id, String country) {
super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
this.id = id;
Country = country;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCountry() {
return Country;
}
public void setCountry(String country) {
Country = country;
}
I have also added Id and country in my entity class(model class).
But when i try to register the user .
It give an error.org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [insert into users (username, password, enabled) values (?,?,?)]; Field 'id' doesn't have a default value; nested exception is java.sql.SQLException: Field 'id' doesn't have a default value
(The value of id and country is hard coded)
Controller class
try {
List<GrantedAuthority> authority = new ArrayList<>();
authority.add(new SimpleGrantedAuthority(form.getRole()));
String encodedPassword = passwordEncoder.encode(form.getPassword());
UserDetails details = new UserDetails(form.getUsername(), encodedPassword, authority, 10 ,"India");
System.out.println(details.getId()+" "+details.getCountry() +" "+details.getUsername());
System.out.println(details);
detailsManager.createUser(details);
}
OUPUT
10 India alpha#gmail.com
com.example.demo.model.UserDetails [Username=alpha#gmail.com, Password=[PROTECTED], Enabled=true, AccountNonExpired=true, credentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[ROLE_ADMIN]]
I don't know why its is calling the parent class constructor.
The SQL is incorrect. Spring Security's INSERT by default populates the username, password, and enabled columns. However, the users table you created requires an id column as well. Since the query doesn't specify the value, it fails.
You could try extending JdbcUserDetailsManager's various methods to be aware of your id field as well. You'd need to at least extend createUser so it adds the id to the INSERT statement and findUserByUsername so it constructs your custom object.
A better way, though, would be to use Spring Data. This allows your domain object to be independent of Spring Security. Also, Spring Data has much broader SQL support.
It might be helpful to call your class something different than a Spring Security interface. So, let's imagine that your custom class (the one with the id) is called YourUser (instead of UserDetails). Now, you can wire a Spring Data-based UserDetailsService to Spring Security like so:
#Service
public class YourUserRepositoryUserDetailsService implements UserDetailsService {
private final YourUserRepository users; // your Spring Data repository
// ... constructor
#Override
public UserDetails findUserByUsername(String username) {
YourUser user = this.users.findByUsername(username)
.orElseThrow(() -> new UserNotFoundException("not found"));
return new UserDetailsAdapter(user);
}
private static class UserDetailsAdapter extends YourUser implements UserDetails {
UserDetailsAdapter(YourUser user) {
super(user); // copy constructor
}
// ... implement UserDetails methods
}
}
This UserDetailsService replaces the JdbcUserDetailsManager that you are publishing.

Get attribute from current page

TaskController
#GetMapping("/admin/addTask")
public String task(String email, Model model, HttpSession session){
session.setAttribute("email",email);
model.addAttribute("task",new Task());
return "task";
}
#PostMapping("/admin/addTask")
public String addTask(#Valid Task task, BindingResult bindingResult,HttpSession session){
if(bindingResult.hasErrors()){
return "task";
}
String emailik = (String) session.getAttribute("email");
taskService.addTask(task,userService.findOne(emailik));
return "redirect:/admin/users";
}
#GetMapping("/admin/usertask")
public String getEmail(User email, Model model,HttpSession session){
model.addAttribute("task",taskService.findUserTask(email));
model.addAttribute("emaail",session.getAttribute("email"));
return "usertasklist";
}
users list controller
#GetMapping("admin/users")
public String listUsers(Model model, #RequestParam(defaultValue = "") String name) {
model.addAttribute("users",userService.findByName(name));
return "list";
}
}
when i enter user in admin/users i get link like /admin/usertask?email=rafs#gmail.com where is users tasks
and i want this work with it
<div th:each="email:${emaail}">
<a th:href="#{/admin/addTask(email=${email})}" class="btn btn-dark"></a>
</div>
but with getattribute it is displayed just after adding task
is there any way to get this email?
If you want to get the email in usertaks endpoint /admin/usertask?email=rafs#gmail.com
You need to change your controller like this
#GetMapping("/admin/usertask")
public String getEmail(#RequestParam("email") String email, Model model,HttpSession session){
model.addAttribute("task",taskService.findUserTask(email));
model.addAttribute("emaail",session.getAttribute("email"));
return "usertasklist";
}

Display user details in jsp using spring security

I have implemented an app in spring boot with spring security. I need to display User's firstname, lastname and the image path in jsp page (which is used for header, it means globally available even if we navigate to another URL) after successfully logged in. I'm using remember-me using PersistentLogin. So I can't use session to store details. Because If I close the browser, session will be destroyed.
I have implemented CustomUserDetailsService, it returns org.springframework.security.core.userdetails.User
#Service("customUserDetailsService")
public class CustomUserDetailsService implements UserDetailsService{
//codes
return new org.springframework.security.core.userdetails.User(
username,
password,
enabled,
accountNonExpired,
credentialsNonExpired,
accountNonLocked,
authorities);
}
I know there are two limitaions
If I don't use remember-me, I can easily store within session.
If I return User model class in CustomUserDetailsService ...,I can easily get user details in jsp
page using<security:authentication property="principal.firstName">
tag in jsp. But I need to return org.springframework.security.core.userdetails.User
Unfortunately I need both limitation. My User model class has firstName, lastName, imagePath,.. etc.
How can I display user details in jsp page? Any approaches available? Thanks in advance.
Spring inbuilt provides the solution to do the same.
Java code :
public User getCurrentUser() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null) {
Object principal = auth.getPrincipal();
if (principal instanceof User) {
return ((User) principal);
}
}
}
JSP code :
${pageContext["request"].userPrincipal.principal}
HWat i have done is, I created a prototype of User called UserAuth
public class UserAuth extends org.springframework.security.core.userdetails.User{
private String firstName;
private String lastName;
private String imagePath;
public UserAuth(String username, String password, boolean enabled,
boolean accountNonExpired, boolean credentialsNonExpired,
boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities,
String firstName, String lastName, String imagePath) {
super(username, password, enabled, accountNonExpired,
credentialsNonExpired, accountNonLocked, authorities);
this.firstName = firstName;
this.lastName = lastName;
this.imagePath = imagePath;
}
//getters and setters
}
In CustomeUserDetailsService
#Service("customUserDetailsService")
public class CustomUserDetailsService implements UserDetailsService{
//codes
return UserAuth
username,
password,
enabled,
accountNonExpired,
credentialsNonExpired,
accountNonLocked,
authorities,
user.getFirstName(),
user.getLastName(),
user.getImagePath());
}

How to get user information for logged in user SpringSecurity + AngularJS

Currently I am using the following to log users into my application. However I would like to use an angular function to actually perform the login. To do this I would like to create a rest web service to authenticate, however all the examples I see on SO use User which I thought was Depreciated. I would also like the service to return information about the user.
The short of what I am asking is how can I change the MyUserDetailsService to be used as a restful service for logging in, or how can I create a service that I can use for logging in that will return the user object after logging in.
<form class="navbar-form" action="/j_spring_security_check" method="post">
<input class="span2" name="j_username" type="text" placeholder="Email">
<input class="span2" name="j_password" type="password" placeholder="Password">
<input type="submit" class="btn btn-primary" value="Log in"/>
</form>
This is my authenticationManager
<authentication-manager>
<authentication-provider user-service-ref="MyUserDetailsService">
<password-encoder ref="passwordEncoder"/>
</authentication-provider>
</authentication-manager>
Here is the user details service I am currently using for login.
#Service("MyUserDetailsService")
public class MyUserDetailsService implements UserDetailsService {
private static final Logger logger = LoggerFactory.getLogger(MyUserDetailsService.class);
private UserManager userManager;
#Autowired
public MyUserDetailsService(UserManager userManager) {
this.userManager = userManager;
}
#Override
#Transactional
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException, DataAccessException {
if ((email == null) || email.trim().isEmpty()) {
throw new UsernameNotFoundException("Email is null or empty");
}
logger.debug("Checking users with email: " + email);
Users users = userManager.findByEmail(email);
if (users == null) {
String errorMsg = "User with email: " + email + " could not be found";
logger.debug(errorMsg);
throw new UsernameNotFoundException(errorMsg);
}
Collection<GrantedAuthority> grantedAuthorities = toGrantedAuthorities(users.getRoleNames());
String password = users.getPassword();
boolean enabled = users.isEnabled();
boolean userNonExpired = true;
boolean credentialsNonExpired = true;
boolean userNonLocked = true;
return new User(email, password, enabled, userNonExpired, userNonLocked, credentialsNonExpired, grantedAuthorities);
}
public static Collection<GrantedAuthority> toGrantedAuthorities(List<String> roles) {
List<GrantedAuthority> result = newArrayList();
for (String role : roles) {
result.add(new SimpleGrantedAuthority(role));
}
return result;
}
}
There are some JSTL tags for Spring Security you can use on the View to do some things. If JSTL is not an option, for whatever reason(s), you can do something like this:
${pageContext.request.userPrincipal.principal.yourCustomProperty}
Also, you could get the Principal in your Controller and set it on the Model.

Resources